From 31c63d21edfd3825c9c6138f8526bc7981e696e6 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Mon, 23 Nov 2020 13:40:03 -0600 Subject: [PATCH] GROOVY-9821 pt.2 --- .../groovy/tests/search/InferencingTests.java | 141 +++++++++++++----- .../core/tests/xform/TypeCheckedTests.java | 23 +++ .../stc/StaticTypeCheckingVisitor.java | 22 ++- .../stc/StaticTypeCheckingVisitor.java | 22 ++- 4 files changed, 160 insertions(+), 48 deletions(-) diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java index a4c6c2e9e4..c1eb5eba1e 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.builder/src/org/eclipse/jdt/core/groovy/tests/search/InferencingTests.java @@ -507,6 +507,7 @@ public void testLocalVar27() { " x = 3.14\n" + " break\n" + " default:\n" + + " break" + " }\n" + " x\n" + "}"; @@ -647,124 +648,168 @@ public void testLocalMethod5() { @Test public void testMatcher1() { - String contents = "def x = \"\" =~ /pattern/\nx"; + String contents = "def x = ('' =~ /pattern/)"; assertType(contents, "x", "java.util.regex.Matcher"); } @Test public void testMatcher2() { - String contents = "(\"\" =~ /pattern/).hasGroup()"; + String contents = "('' =~ /pattern/).hasGroup()"; assertType(contents, "hasGroup", "java.lang.Boolean"); } @Test public void testPattern1() { - String contents = "def x = ~/pattern/\nx"; + String contents = "def x = ~/pattern/"; assertType(contents, "x", "java.util.regex.Pattern"); } @Test public void testPattern2() { - String contents = "def x = \"\" ==~ /pattern/\nx"; + String contents = "def x = \"\" ==~ /pattern/"; assertType(contents, "x", "java.lang.Boolean"); } @Test public void testSpread1() { - String contents = "def z = [1,2]*.value"; - assertType(contents, "value", "java.lang.Integer"); + String contents = "def x = ['1','2']*.bytes"; + assertType(contents, "x", "java.util.List"); } @Test public void testSpread2() { - String contents = "[1,2,3]*.intValue()"; - assertType(contents, "intValue", "java.lang.Integer"); + String contents = "def x = [1,2,3]*.intValue()"; + assertType(contents, "x", "java.util.List"); } @Test public void testSpread3() { - String contents = "[1,2,3]*.intValue()[0].value"; - assertType(contents, "value", "java.lang.Integer"); + String contents = "def x = [1,2,3]*.intValue()[0].intValue()"; + assertType(contents, "x", "java.lang.Integer"); } @Test public void testSpread4() { - String contents = "[x:1,y:2,z:3]*.getKey()"; - assertType(contents, "getKey", "java.lang.String"); + String contents = "def x = [a:1,b:2,c:3]*.getKey()"; + assertType(contents, "x", "java.util.List"); } @Test public void testSpread5() { - String contents = "[x:1,y:2,z:3]*.getValue()"; - assertType(contents, "getValue", "java.lang.Integer"); + String contents = "def x = [a:1,b:2,c:3]*.getValue()"; + assertType(contents, "x", "java.util.List"); } @Test public void testSpread6() { - String contents = "[x:1,y:2,z:3]*.key"; - assertType(contents, "key", "java.lang.String"); + String contents = "def x = [a:1,b:2,c:3]*.key"; + assertType(contents, "x", "java.util.List"); } @Test public void testSpread7() { - String contents = "[x:1,y:2,z:3]*.value"; - assertType(contents, "value", "java.lang.Integer"); + String contents = "def x = [a:1,b:2,c:3]*.value"; + assertType(contents, "x", "java.util.List"); } @Test public void testSpread8() { - String contents = "[x:1,y:2,z:3]*.key[0].toLowerCase()"; - assertType(contents, "toLowerCase", "java.lang.String"); + String contents = "def x = [a:1,b:2,c:3]*.key[0].toLowerCase()"; + assertType(contents, "x", "java.lang.String"); } @Test public void testSpread9() { - String contents = "[x:1,y:2,z:3]*.value[0].intValue()"; - assertType(contents, "intValue", "java.lang.Integer"); + String contents = "def x = [a:1,b:2,c:3]*.value[0].intValue()"; + assertType(contents, "x", "java.lang.Integer"); } @Test public void testSpread10() { - String contents = "[1,2,3]*.value[0].value"; - assertType(contents, "value", "java.lang.Integer"); + String contents = "def x = ['1','2','3']*.bytes[0].length"; + assertType(contents, "x", "java.lang.Integer"); } @Test public void testSpread11() { - String contents = "Set strings = ['1','2','3'] as Set\n" + - "strings*.bytes\n"; - assertType(contents, "bytes", "byte[]"); + String contents = "Set strings = ['1','2','3']; def x = strings*.bytes"; + assertType(contents, "x", "java.util.List"); } @Test public void testSpread12() { - String contents = "Set strings = ['1','2','3'] as Set\n" + - "strings*.length()\n"; - assertType(contents, "length", "java.lang.Integer"); + String contents = "Set strings = ['1','2','3']; def x = strings*.length()"; + assertType(contents, "x", "java.util.List"); } @Test public void testSpread13() { - String contents = "@groovy.transform.TypeChecked\n" + - "class Foo {\n" + - " static def meth() {\n" + - " Set beans = []\n" + - " beans*.additionalBeanInfo\n" + - " }\n" + + String contents = + "@groovy.transform.TypeChecked\n" + + "void test(Set beans) {\n" + + " beans*.additionalBeanInfo\n" + "}\n"; assertType(contents, "beans", "java.util.Set"); assertType(contents, "additionalBeanInfo", "java.beans.BeanInfo[]"); } - @Test // https://github.com/groovy/groovy-eclipse/issues/763 + @Test // GROOVY-9021 public void testSpread14() { + createJavaUnit("Pojo", + "interface Pojo {\n" + + " java.util.Collection getStrings();\n" + + "}\n"); + + String contents = + "@groovy.transform.TypeChecked\n" + + "void test(Pojo pojo) {\n" + + " def result = pojo.strings*.bytes\n" + // exercises StaticTypeCheckingVisitor#getTypeForSpreadExpression + "}\n"; + assertType(contents, "strings", "java.util.Collection"); + assertType(contents, "result", "java.util.List"); + } + + @Test // GROOVY-9021 + public void testSpread15() { + createJavaUnit("Pojo", + "interface Pojo {\n" + + " java.util.List getStrings();\n" + + "}\n"); + + String contents = + "@groovy.transform.TypeChecked\n" + + "void test(Pojo pojo) {\n" + + " def result = pojo.strings*.bytes\n" + // exercises StaticTypeCheckingVisitor#getTypeForListExpression + "}\n"; + assertType(contents, "strings", "java.util.List"); + assertType(contents, "result", "java.util.List"); + } + + @Test + public void testSpread16() { + createJavaUnit("Pojo", + "interface Pojo {\n" + + " java.util.Map getStrings();\n" + + "}\n"); + + String contents = + "@groovy.transform.TypeChecked\n" + + "void test(Pojo pojo) {\n" + + " def result = pojo.strings*.value\n" + // exercises StaticTypeCheckingVisitor#getTypeForMapExpression + "}\n"; + assertType(contents, "strings", "java.util.Map"); + assertType(contents, "result", "java.util.List"); + } + + @Test // https://github.com/groovy/groovy-eclipse/issues/763 + public void testSpread17() { String contents = "def strings = [[['1','2','3']]]\n" + "def result = strings*.length()\n"; assertType(contents, "result", "java.util.List"); } @Test // CommandRegistry.iterator() lacks generics - public void testSpread15() { + public void testSpread18() { String contents = "import org.codehaus.groovy.tools.shell.CommandRegistry\n" + "def registry = new CommandRegistry()\n" + @@ -773,7 +818,7 @@ public void testSpread15() { } @Test - public void testSpread16() { + public void testSpread19() { String contents = "import java.util.regex.Matcher\n" + "Matcher matcher = ('abc' =~ /./)\n" + @@ -782,7 +827,7 @@ public void testSpread16() { } @Test - public void testSpread17() { + public void testSpread20() { String contents = "Reader reader = null\n" + "def result = reader*.with {it}\n"; @@ -794,6 +839,22 @@ public void testMapLiteral() { assertType("[:]", "java.util.Map"); } + @Test // GROOVY-9021 + public void testMapProperty() { + createJavaUnit("Pojo", + "interface Pojo {\n" + + " java.util.Map getMap();\n" + + "}\n"); + + String contents = + "@groovy.transform.TypeChecked\n" + + "void test(Pojo pojo) {\n" + + " def result = pojo.map.name\n" + // exercises StaticTypeCheckingVisitor#getTypeForMapExpression + "}\n"; + assertType(contents, "map", "java.util.Map"); + assertType(contents, "result", "java.lang.Number"); + } + @Test public void testBoolean1() { assertType("!x", "java.lang.Boolean"); diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java index 6a0d8ec1dd..516bab7a70 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java @@ -550,6 +550,29 @@ public void testTypeChecked9821() { runNegativeTest(sources, ""); } + @Test + public void testTypeChecked9821a() { + //@formatter:off + String[] sources = { + "Main.groovy", + "@groovy.transform.TypeChecked\n" + + "def test(A a) {\n" + + " a.bees.c\n" + + "}\n", + + "Types.java", + "interface A {\n" + + " java.util.List getBees();\n" + + "}\n" + + "interface B {\n" + + " Object getC();\n" + + "}\n", + }; + //@formatter:on + + runNegativeTest(sources, ""); + } + @Test public void testTypeChecked9822() { //@formatter:off diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index eb40534a27..988c498fcd 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -1768,7 +1768,7 @@ private ClassNode getTypeForSpreadExpression(ClassNode testClass, ClassNode obje AtomicReference result = new AtomicReference(); if (existsProperty(subExp, true, new PropertyLookupVisitor(result))) { ClassNode intf = LIST_TYPE.getPlainNodeReference(); - intf.setGenericsTypes(new GenericsType[]{new GenericsType(getWrapper(result.get()))}); + intf.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(result.get()))}); return intf; } return null; @@ -1776,16 +1776,22 @@ private ClassNode getTypeForSpreadExpression(ClassNode testClass, ClassNode obje private ClassNode getTypeForListPropertyExpression(ClassNode testClass, ClassNode objectExpressionType, PropertyExpression pexp) { if (!implementsInterfaceOrIsSubclassOf(testClass, LIST_TYPE)) return null; + /* GRECLIPSE edit -- GROOVY-9821 ClassNode intf = GenericsUtils.parameterizeType(objectExpressionType, LIST_TYPE.getPlainNodeReference()); GenericsType[] types = intf.getGenericsTypes(); if (types == null || types.length != 1) return OBJECT_TYPE; PropertyExpression subExp = new PropertyExpression(varX("{}", types[0].getType()), pexp.getPropertyAsString()); + */ + GenericsType[] types = (testClass.equals(LIST_TYPE) ? testClass : GenericsUtils.parameterizeType(testClass, LIST_TYPE)).getGenericsTypes(); + ClassNode itemType = (types != null && types.length == 1 ? getCombinedBoundType(types[0]) : OBJECT_TYPE); + + PropertyExpression subExp = new PropertyExpression(varX("{}", itemType), pexp.getPropertyAsString()); + // GRECLIPSE end AtomicReference result = new AtomicReference(); if (existsProperty(subExp, true, new PropertyLookupVisitor(result))) { - intf = LIST_TYPE.getPlainNodeReference(); - ClassNode itemType = result.get(); - intf.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(itemType))}); + ClassNode intf = LIST_TYPE.getPlainNodeReference(); + intf.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(result.get()))}); return intf; } return null; @@ -1794,11 +1800,15 @@ private ClassNode getTypeForListPropertyExpression(ClassNode testClass, ClassNod private ClassNode getTypeForMapPropertyExpression(ClassNode testClass, ClassNode objectExpressionType, PropertyExpression pexp) { if (!implementsInterfaceOrIsSubclassOf(testClass, MAP_TYPE)) return null; ClassNode intf; + /* GRECLIPSE edit -- GROOVY-9821 if (objectExpressionType.getGenericsTypes() != null) { intf = GenericsUtils.parameterizeType(objectExpressionType, MAP_TYPE.getPlainNodeReference()); } else { intf = MAP_TYPE.getPlainNodeReference(); } + */ + intf = testClass.equals(MAP_TYPE) ? testClass : GenericsUtils.parameterizeType(testClass, MAP_TYPE); + // GRECLIPSE end // 0 is the key, 1 is the value GenericsType[] types = intf.getGenericsTypes(); if (types == null || types.length != 2) return OBJECT_TYPE; @@ -1818,7 +1828,11 @@ private ClassNode getTypeForMapPropertyExpression(ClassNode testClass, ClassNode addStaticTypeError("Spread operator on map only allows one of [key,value]", pexp); } } else { + /* GRECLIPSE edit -- GROOVY-9821 return types[1].getType(); + */ + return getCombinedBoundType(types[1]); + // GRECLIPSE end } return null; } diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index e70fbc9abe..e9dd547f84 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -1705,7 +1705,7 @@ private ClassNode getTypeForSpreadExpression(final ClassNode testClass, final Cl AtomicReference result = new AtomicReference<>(); if (existsProperty(subExp, true, new PropertyLookupVisitor(result))) { ClassNode intf = LIST_TYPE.getPlainNodeReference(); - intf.setGenericsTypes(new GenericsType[]{new GenericsType(getWrapper(result.get()))}); + intf.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(result.get()))}); return intf; } return null; @@ -1713,16 +1713,22 @@ private ClassNode getTypeForSpreadExpression(final ClassNode testClass, final Cl private ClassNode getTypeForListPropertyExpression(final ClassNode testClass, final ClassNode objectExpressionType, final PropertyExpression pexp) { if (!implementsInterfaceOrIsSubclassOf(testClass, LIST_TYPE)) return null; + /* GRECLIPSE edit -- GROOVY-9821 ClassNode intf = GenericsUtils.parameterizeType(objectExpressionType, LIST_TYPE.getPlainNodeReference()); GenericsType[] types = intf.getGenericsTypes(); if (types == null || types.length != 1) return OBJECT_TYPE; PropertyExpression subExp = new PropertyExpression(varX("{}", types[0].getType()), pexp.getPropertyAsString()); + */ + GenericsType[] types = (testClass.equals(LIST_TYPE) ? testClass : GenericsUtils.parameterizeType(testClass, LIST_TYPE)).getGenericsTypes(); + ClassNode itemType = (types != null && types.length == 1 ? getCombinedBoundType(types[0]) : OBJECT_TYPE); + + PropertyExpression subExp = new PropertyExpression(varX("{}", itemType), pexp.getPropertyAsString()); + // GRECLIPSE end AtomicReference result = new AtomicReference<>(); if (existsProperty(subExp, true, new PropertyLookupVisitor(result))) { - intf = LIST_TYPE.getPlainNodeReference(); - ClassNode itemType = result.get(); - intf.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(itemType))}); + ClassNode intf = LIST_TYPE.getPlainNodeReference(); + intf.setGenericsTypes(new GenericsType[]{new GenericsType(wrapTypeIfNecessary(result.get()))}); return intf; } return null; @@ -1731,11 +1737,15 @@ private ClassNode getTypeForListPropertyExpression(final ClassNode testClass, fi private ClassNode getTypeForMapPropertyExpression(final ClassNode testClass, final ClassNode objectExpressionType, final PropertyExpression pexp) { if (!implementsInterfaceOrIsSubclassOf(testClass, MAP_TYPE)) return null; ClassNode intf; + /* GRECLIPSE edit -- GROOVY-9821 if (objectExpressionType.getGenericsTypes() != null) { intf = GenericsUtils.parameterizeType(objectExpressionType, MAP_TYPE.getPlainNodeReference()); } else { intf = MAP_TYPE.getPlainNodeReference(); } + */ + intf = testClass.equals(MAP_TYPE) ? testClass : GenericsUtils.parameterizeType(testClass, MAP_TYPE); + // GRECLIPSE end // 0 is the key, 1 is the value GenericsType[] types = intf.getGenericsTypes(); if (types == null || types.length != 2) return OBJECT_TYPE; @@ -1755,7 +1765,11 @@ private ClassNode getTypeForMapPropertyExpression(final ClassNode testClass, fin addStaticTypeError("Spread operator on map only allows one of [key,value]", pexp); } } else { + /* GRECLIPSE edit -- GROOVY-9821 return types[1].getType(); + */ + return getCombinedBoundType(types[1]); + // GRECLIPSE end } return null; }