You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2020/09/20 00:55:42 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-9735: connect placeholder from context with method generic params

This is an automated email from the ASF dual-hosted git repository.

paulk pushed a commit to branch GROOVY_3_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new 1db8a3c  GROOVY-9735: connect placeholder from context with method generic params
1db8a3c is described below

commit 1db8a3c23b3f7bc619ba46c6433017cfe84ff6bc
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Sep 13 16:17:34 2020 -0500

    GROOVY-9735: connect placeholder from context with method generic params
    
    @groovy.transform.TypeChecked
      class C<T extends Number> {
        List<T> list
        def test() {
          m(list) { /*...*/ } // "it" should infer as T/Number
        }
        def <U> void m(Iterable<U> items,
            @ClosureParams(FirstParam.FirstGenericType) Closure block) {
        }
      }
    
    closes #1363
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   | 68 ++++++++++++++--------
 .../transform/stc/StaticTypeCheckingSupport.java   | 15 +++--
 .../transform/stc/StaticTypeCheckingVisitor.java   | 10 ++--
 .../stc/ClosureParamTypeInferenceSTCTest.groovy    | 33 ++++++++++-
 4 files changed, 89 insertions(+), 37 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 15a8413..7186af0 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -55,7 +55,6 @@ import java.util.function.Predicate;
 import static groovy.lang.Tuple.tuple;
 import static org.apache.groovy.util.SystemUtil.getSystemPropertySafe;
 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;
 
 /**
@@ -514,12 +513,10 @@ public class GenericsUtils {
                 extractSuperClassGenerics(type.getGenericsTypes(), target.getGenericsTypes(), spec);
             }
         } else {
-            // have first to find matching super class or interface
+            // find matching super class or interface
             ClassNode superClass = getSuperClass(type, target);
-
             if (superClass != null) {
-                ClassNode corrected = getCorrectedClassNode(type, superClass, false);
-                extractSuperClassGenerics(corrected, target, spec);
+                extractSuperClassGenerics(correctToGenericsSpecRecurse(createGenericsSpec(type), superClass), target, spec);
             } else {
                 // if we reach here, we have an unhandled case
                 throw new GroovyBugError("The type " + type + " seems not to normally extend " + target + ". Sorry, I cannot handle this.");
@@ -907,39 +904,60 @@ public class GenericsUtils {
     }
 
     /**
-     * Check whether the ClassNode has non generics placeholders, aka not placeholder
+     * Checks if the type has any non-placeholder (aka resolved) generics.
      *
-     * @param parameterizedType the class node
-     * @return the result
      * @since 3.0.0
      */
-    public static boolean hasNonPlaceHolders(ClassNode parameterizedType) {
-        return checkPlaceHolders(parameterizedType, genericsType -> !genericsType.isPlaceholder());
+    public static boolean hasNonPlaceHolders(final ClassNode type) {
+        return checkPlaceHolders(type, gt -> !gt.isPlaceholder());
     }
 
     /**
-     * Check whether the ClassNode has generics placeholders
-     * @param parameterizedType the class node
-     * @return the result
+     * Checks if the type has any placeholder (aka unresolved) generics.
+     *
      * @since 3.0.0
      */
-    public static boolean hasPlaceHolders(ClassNode parameterizedType) {
-        return checkPlaceHolders(parameterizedType, GenericsType::isPlaceholder);
+    public static boolean hasPlaceHolders(final ClassNode type) {
+        return checkPlaceHolders(type, gt -> gt.isPlaceholder());
     }
 
-    private static boolean checkPlaceHolders(ClassNode parameterizedType, Predicate<GenericsType> p) {
-        if (null == parameterizedType) return false;
-
-        GenericsType[] genericsTypes = parameterizedType.getGenericsTypes();
-
-        if (null == genericsTypes) return false;
-
-        for (GenericsType genericsType : genericsTypes) {
-            if (p.test(genericsType)) {
-                return true;
+    private static boolean checkPlaceHolders(final ClassNode type, final Predicate<GenericsType> p) {
+        if (type != null) {
+            GenericsType[] genericsTypes = type.getGenericsTypes();
+            if (genericsTypes != null) {
+                for (GenericsType genericsType : genericsTypes) {
+                    if (p.test(genericsType)) {
+                        return true;
+                    }
+                }
             }
         }
+        return false;
+    }
 
+    /**
+     * Checks for any placeholder (aka unresolved) generics.
+     */
+    public static boolean hasUnresolvedGenerics(final ClassNode type) {
+        if (type.isGenericsPlaceHolder()) return true;
+        if (type.isArray()) {
+            return hasUnresolvedGenerics(type.getComponentType());
+        }
+        GenericsType[] genericsTypes = type.getGenericsTypes();
+        if (genericsTypes != null) {
+            for (GenericsType genericsType : genericsTypes) {
+                if (genericsType.isPlaceholder()) return true;
+                ClassNode lowerBound = genericsType.getLowerBound();
+                ClassNode[] upperBounds = genericsType.getUpperBounds();
+                if (lowerBound != null) {
+                    if (hasUnresolvedGenerics(lowerBound)) return true;
+                } else if (upperBounds != null) {
+                    for (ClassNode upperBound : upperBounds) {
+                        if (hasUnresolvedGenerics(upperBound)) return true;
+                    }
+                }
+            }
+        }
         return false;
     }
 
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index b569fc9..ec5c502 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -105,7 +105,6 @@ import static org.codehaus.groovy.ast.ClassHelper.make;
 import static org.codehaus.groovy.ast.ClassHelper.makeWithoutCaching;
 import static org.codehaus.groovy.ast.ClassHelper.short_TYPE;
 import static org.codehaus.groovy.ast.ClassHelper.void_WRAPPER_TYPE;
-import static org.codehaus.groovy.ast.tools.GenericsUtils.getSuperClass;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.asBoolean;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND;
 import static org.codehaus.groovy.syntax.Types.BITWISE_AND_EQUAL;
@@ -1730,7 +1729,7 @@ public abstract class StaticTypeCheckingSupport {
      * Should the target not have any generics this method does nothing.
      */
     static void extractGenericsConnections(final Map<GenericsTypeName, GenericsType> connections, final ClassNode type, final ClassNode target) {
-        if (target == null || type == target || !isUsingGenericsOrIsArrayUsingGenerics(target)) return;
+        if (target == null || target == type || !isUsingGenericsOrIsArrayUsingGenerics(target)) return;
         if (type == null || type == UNKNOWN_PARAMETER_TYPE) return;
 
         if (target.isGenericsPlaceHolder()) {
@@ -1743,10 +1742,16 @@ public abstract class StaticTypeCheckingSupport {
             extractGenericsConnections(connections, type.getGenericsTypes(), target.getGenericsTypes());
 
         } else {
-            // first find matching super class or interface
-            ClassNode superClass = getSuperClass(type, target);
+            // find matching super class or interface
+            ClassNode superClass = GenericsUtils.getSuperClass(type, target);
             if (superClass != null) {
-                extractGenericsConnections(connections, getCorrectedClassNode(type, superClass, true), target);
+                if (GenericsUtils.hasUnresolvedGenerics(superClass)) {
+                    Map<String, ClassNode> spec = GenericsUtils.createGenericsSpec(type);
+                    if (!spec.isEmpty()) {
+                        superClass = GenericsUtils.correctToGenericsSpecRecurse(spec, superClass);
+                    }
+                }
+                extractGenericsConnections(connections, superClass, target);
             } else {
                 // if we reach here, we have an unhandled case
                 throw new GroovyBugError("The type " + type + " seems not to normally extend " + target + ". Sorry, I cannot handle this.");
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 6109f8f..3f93d0e 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1764,11 +1764,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return member;
     }
 
-    private void storeWithResolve(final ClassNode typeToResolve, final ClassNode receiver, final ClassNode declaringClass, final boolean isStatic, final Expression expressionToStoreOn) {
-        ClassNode type = typeToResolve;
-        if (missesGenericsTypes(type)) {
-            Map<GenericsTypeName, GenericsType> resolvedPlaceholders = resolvePlaceHoldersFromDeclaration(receiver, declaringClass, null, isStatic);
-            type = resolveGenericsWithContext(resolvedPlaceholders, type);
+    private void storeWithResolve(ClassNode type, final ClassNode receiver, final ClassNode declaringClass, final boolean isStatic, final Expression expressionToStoreOn) {
+        if (GenericsUtils.hasUnresolvedGenerics(type)) {
+            type = resolveGenericsWithContext(resolvePlaceHoldersFromDeclaration(receiver, declaringClass, null, isStatic), type);
         }
         if (expressionToStoreOn instanceof PropertyExpression) {
             storeInferredTypeForPropertyExpression((PropertyExpression) expressionToStoreOn, type);
@@ -5136,7 +5134,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
                     extractGenericsConnections(connections, argumentType, paramType);
                     extractGenericsConnectionsForSuperClassAndInterfaces(resolvedPlaceholders, connections);
+
                     applyGenericsConnections(connections, resolvedPlaceholders);
+                    applyGenericsConnections(placeholdersFromContext, resolvedPlaceholders);
                 }
             }
         }
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index d991a6f..87416a2 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -1378,8 +1378,6 @@ method()
 
     void testGroovy9570() {
         assertScript '''
-            interface Item {}
-
             class C<I extends Item> {
                 Queue<I> queue
 
@@ -1396,6 +1394,37 @@ method()
                 }
             }
 
+            interface Item {}
+
+            new C()
+        '''
+    }
+
+    void testGroovy9735() {
+        assertScript '''
+            import groovy.transform.stc.*
+
+            class C<I extends Item> {
+                Queue<I> queue
+
+                def c = { ->
+                    x(queue) { I item ->
+                        println item
+                    }
+                }
+
+                def m() {
+                    x(queue) { I item ->
+                        println item
+                    }
+                }
+
+                def <T> T x(Collection<T> y, @ClosureParams(FirstParam.FirstGenericType) Closure z) {
+                }
+            }
+
+            interface Item {}
+
             new C()
         '''
     }