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 3ddec80e99..12916aa04b 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 @@ -794,6 +794,24 @@ public void testTypeChecked7363() { runNegativeTest(sources, ""); } + @Test + public void testTypeChecked7753() { + //@formatter:off + String[] sources = { + "Main.groovy", + "@groovy.transform.Field\n" + + "String x = 'X'\n" + + "@groovy.transform.TypeChecked\n" + + "public List getStrings() {\n" + + " x ? [x] : Collections.emptyList()\n" + + "}\n" + + "print strings\n", + }; + //@formatter:on + + runConformTest(sources, "[X]"); + } + @Test public void testTypeChecked7804() { //@formatter:off @@ -3100,6 +3118,45 @@ public void testTypeChecked10111a() { runConformTest(sources); } + @Test + public void testTypeChecked10166() { + //@formatter:off + String[] sources = { + "Main.groovy", + "@groovy.transform.TypeChecked\n" + + "@SuppressWarnings('rawtypes')\n" + + "abstract class A {\n" + + " T getC() {\n" + + " }\n" + + " Map toMap() {\n" + + " c.getMap(this)\n" + + " }\n" + + "}\n" + + "@groovy.transform.TypeChecked\n" + + "@SuppressWarnings('rawtypes')\n" + + "class C {\n" + + " Map getMap(T a) {\n" + + " }\n" + + " T getObj(Map m) {\n" + + " A a = null\n" + + " a.c.get(1)\n" + + " }\n" + + " T get(int i) {\n" + + " }\n" + + "}\n" + + "new C()\n", + }; + //@formatter:on + + runNegativeTest(sources, + "----------\n" + + "1. ERROR in Main.groovy (at line 17)\n" + + "\ta.c.get(1)\n" + + "\t^^^^^^^^^^\n" + + "Groovy:[Static type checking] - Cannot find matching method A#get(int). Please check if the declared type is correct and if the method exists.\n" + + "----------\n"); + } + @Test public void testTypeChecked10179() { //@formatter:off diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/tools/GenericsUtils.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/tools/GenericsUtils.java index d97973c21c..b209627d02 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/tools/GenericsUtils.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/ast/tools/GenericsUtils.java @@ -54,7 +54,6 @@ import java.util.Objects; import java.util.concurrent.atomic.AtomicReference; -import static org.codehaus.groovy.ast.GenericsType.GenericsTypeName; import static org.codehaus.groovy.runtime.DefaultGroovyMethods.plus; import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.getCorrectedClassNode; import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf; @@ -142,8 +141,8 @@ public static GenericsType buildWildcardType(final ClassNode... types) { return gt; } - public static Map extractPlaceholders(ClassNode cn) { - Map ret = new HashMap(); + public static Map extractPlaceholders(ClassNode cn) { + Map ret = new HashMap<>(); extractPlaceholders(cn, ret); return ret; } @@ -155,7 +154,7 @@ public static Map extractPlaceholders(ClassNode * @param node the class node to check * @param map the generics type information collector */ - public static void extractPlaceholders(ClassNode node, Map map) { + public static void extractPlaceholders(ClassNode node, Map map) { if (node == null) return; if (node.isArray()) { @@ -198,7 +197,7 @@ public static void extractPlaceholders(ClassNode node, Map makeDeclaringAndActualGenericsTyp private static Tuple2, ClassNode> doMakeDeclaringAndActualGenericsTypeMap(ClassNode declaringClass, ClassNode actualReceiver, boolean tryToFindExactType) { ClassNode parameterizedType = findParameterizedTypeFromCache(declaringClass, actualReceiver, tryToFindExactType); - + /* GRECLIPSE edit -- GROOVY-10166 if (parameterizedType == null) { return new Tuple2<>(Collections.emptyMap(), parameterizedType); } @@ -953,6 +952,22 @@ private static Tuple2, ClassNode> doMakeDeclarin result = connectGenericsTypes(result); return new Tuple2<>(result, parameterizedType); + */ + if (parameterizedType != null && parameterizedType.isRedirectNode() && !parameterizedType.isGenericsPlaceHolder()) { + // declaringClass may be "List -> List" and parameterizedType may be "List -> List" + GenericsType[] targetGenericsTypes = parameterizedType.redirect().getGenericsTypes(); + if (targetGenericsTypes != null) { + GenericsType[] sourceGenericsTypes = parameterizedType.getGenericsTypes(); + if (sourceGenericsTypes == null) sourceGenericsTypes = EMPTY_GENERICS_ARRAY; + Map map = new LinkedHashMap<>(); + for (int i = 0, m = sourceGenericsTypes.length, n = targetGenericsTypes.length; i < n; i += 1) { + map.put(targetGenericsTypes[i], i < m ? sourceGenericsTypes[i] : targetGenericsTypes[i]); + } + return new Tuple2<>(map, parameterizedType); + } + } + return new Tuple2<>(Collections.emptyMap(), parameterizedType); + // GRECLIPSE end } private static Map connectGenericsTypes(Map genericsTypeMap) { @@ -999,6 +1014,7 @@ private static boolean checkPlaceHolders(final ClassNode parameterizedType, fina return false; } + /* GRECLIPSE edit private static Map makePlaceholderAndParameterizedTypeMap(ClassNode declaringClass) { if (declaringClass == null) { return Collections.emptyMap(); @@ -1016,6 +1032,7 @@ private static Map makePlaceholderAndParameterizedTy } return result; } + */ /** * Get the actual type according to the placeholder name diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/tools/GenericsUtils.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/tools/GenericsUtils.java index ef118c3f2c..e3cdcc786f 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/tools/GenericsUtils.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/ast/tools/GenericsUtils.java @@ -866,6 +866,7 @@ private static Map makeDeclaringAndActualGenericsTyp private static Tuple2, ClassNode> doMakeDeclaringAndActualGenericsTypeMap(final ClassNode declaringClass, final ClassNode actualReceiver, final boolean tryToFindExactType) { ClassNode parameterizedType = findParameterizedTypeFromCache(declaringClass, actualReceiver, tryToFindExactType); + /* GRECLIPSE edit -- GROOVY-10166 if (parameterizedType == null) { return tuple(Collections.emptyMap(), parameterizedType); } @@ -877,8 +878,25 @@ private static Tuple2, ClassNode> doMakeDeclarin result = connectGenericsTypes(result); return tuple(result, parameterizedType); + */ + if (parameterizedType != null && parameterizedType.isRedirectNode() && !parameterizedType.isGenericsPlaceHolder()) { + // declaringClass may be "List -> List" and parameterizedType may be "List -> List" + GenericsType[] targetGenericsTypes = parameterizedType.redirect().getGenericsTypes(); + if (targetGenericsTypes != null) { + GenericsType[] sourceGenericsTypes = parameterizedType.getGenericsTypes(); + if (sourceGenericsTypes == null) sourceGenericsTypes = EMPTY_GENERICS_ARRAY; + Map map = new LinkedHashMap<>(); + for (int i = 0, m = sourceGenericsTypes.length, n = targetGenericsTypes.length; i < n; i += 1) { + map.put(targetGenericsTypes[i], i < m ? sourceGenericsTypes[i] : targetGenericsTypes[i]); + } + return tuple(map, parameterizedType); + } + } + return tuple(Collections.emptyMap(), parameterizedType); + // GRECLIPSE end } + /* GRECLIPSE edit private static Map makePlaceholderAndParameterizedTypeMap(final ClassNode declaringClass) { if (null == declaringClass) { return Collections.emptyMap(); @@ -898,6 +916,7 @@ private static Map makePlaceholderAndParameterizedTy return result; } + */ private static Map connectGenericsTypes(final Map genericsTypeMap) { Map result = new LinkedHashMap<>(); diff --git a/base/org.codehaus.groovy40/src/org/codehaus/groovy/ast/tools/GenericsUtils.java b/base/org.codehaus.groovy40/src/org/codehaus/groovy/ast/tools/GenericsUtils.java index 2e10653406..836afa95c8 100644 --- a/base/org.codehaus.groovy40/src/org/codehaus/groovy/ast/tools/GenericsUtils.java +++ b/base/org.codehaus.groovy40/src/org/codehaus/groovy/ast/tools/GenericsUtils.java @@ -865,10 +865,10 @@ private static Map makeDeclaringAndActualGenericsTyp private static Tuple2, ClassNode> doMakeDeclaringAndActualGenericsTypeMap(final ClassNode declaringClass, final ClassNode actualReceiver, final boolean tryToFindExactType) { ClassNode parameterizedType = findParameterizedTypeFromCache(declaringClass, actualReceiver, tryToFindExactType); + /* GRECLIPSE edit -- GROOVY-10166 if (parameterizedType == null) { return tuple(Collections.emptyMap(), parameterizedType); } - Map result = new LinkedHashMap<>(); result.putAll(makePlaceholderAndParameterizedTypeMap(declaringClass)); result.putAll(makePlaceholderAndParameterizedTypeMap(parameterizedType)); @@ -876,8 +876,25 @@ private static Tuple2, ClassNode> doMakeDeclarin result = connectGenericsTypes(result); return tuple(result, parameterizedType); + */ + if (parameterizedType != null && parameterizedType.isRedirectNode() && !parameterizedType.isGenericsPlaceHolder()) { + // declaringClass may be "List -> List" and parameterizedType may be "List -> List" + GenericsType[] targetGenericsTypes = parameterizedType.redirect().getGenericsTypes(); + if (targetGenericsTypes != null) { + GenericsType[] sourceGenericsTypes = parameterizedType.getGenericsTypes(); + if (sourceGenericsTypes == null) sourceGenericsTypes = EMPTY_GENERICS_ARRAY; + Map map = new LinkedHashMap<>(); + for (int i = 0, m = sourceGenericsTypes.length, n = targetGenericsTypes.length; i < n; i += 1) { + map.put(targetGenericsTypes[i], i < m ? sourceGenericsTypes[i] : targetGenericsTypes[i]); + } + return tuple(map, parameterizedType); + } + } + return tuple(Collections.emptyMap(), parameterizedType); + // GRECLIPSE end } + /* GRECLIPSE edit private static Map makePlaceholderAndParameterizedTypeMap(final ClassNode declaringClass) { if (null == declaringClass) { return Collections.emptyMap(); @@ -897,6 +914,7 @@ private static Map makePlaceholderAndParameterizedTy return result; } + */ private static Map connectGenericsTypes(final Map genericsTypeMap) { Map result = new LinkedHashMap<>(); @@ -1010,7 +1028,8 @@ public static Tuple2 parameterizeSAM(final ClassNode sam Map generics = makeDeclaringAndActualGenericsTypeMapOfExactType(abstractMethod.getDeclaringClass(), samType); Function resolver = t -> { if (t.isGenericsPlaceHolder()) { - return findActualTypeByGenericsPlaceholderName(t.getUnresolvedName(), generics); + ClassNode type = findActualTypeByGenericsPlaceholderName(t.getUnresolvedName(), generics); + return type; } return t; };