You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by em...@apache.org on 2022/04/06 16:48:10 UTC

[groovy] 02/02: GROOVY-9968: STC `@ClosureParams`: for "T -> Type" use Type not Object

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

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

commit 2e891232ea00f8c43081c0b8310ef368bea80d8b
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Apr 6 11:26:18 2022 -0500

    GROOVY-9968: STC `@ClosureParams`: for "T -> Type" use Type not Object
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 27 +++++-----
 .../stc/ClosureParamTypeInferenceSTCTest.groovy    | 17 +++++++
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 57 ++++++++++++++++++++++
 3 files changed, 87 insertions(+), 14 deletions(-)

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 45b88812f0..6a8a4ee826 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -3074,10 +3074,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         return compilationUnit != null ? compilationUnit.getTransformLoader() : getSourceUnit().getClassLoader();
     }
 
-    private void doInferClosureParameterTypes(final ClassNode receiver, final Expression arguments, final ClosureExpression expression, final MethodNode selectedMethod, final Expression hintClass, Expression resolverClass, final Expression options) {
+    private void doInferClosureParameterTypes(final ClassNode receiver, final Expression arguments, final ClosureExpression expression, final MethodNode selectedMethod, final Expression hintClass, final Expression resolverClass, final Expression options) {
+        Parameter[] closureParams = expression.getParameters();
+        if (closureParams == null) return; // no-arg closure
+
         List<ClassNode[]> closureSignatures = getSignaturesFromHint(expression, selectedMethod, hintClass, options);
         List<ClassNode[]> candidates = new LinkedList<ClassNode[]>();
-        Parameter[] closureParams = expression.getParameters();
         for (ClassNode[] signature : closureSignatures) {
             // in order to compute the inferred types of the closure parameters, we're using the following trick:
             // 1. create a dummy MethodNode for which the return type is a class node for which the generic types are the types returned by the hint
@@ -3086,7 +3088,6 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             // In practice, it could be done differently but it has the main advantage of reusing
             // existing code, hence reducing the amount of code to debug in case of failure.
             ClassNode[] inferred = resolveGenericsFromTypeHint(receiver, arguments, selectedMethod, signature);
-            if (closureParams == null) return; // no-arg closure
             if (signature.length == closureParams.length // same number of arguments
                     || (signature.length == 1 && closureParams.length == 0) // implicit it
                     || (closureParams.length > signature.length && inferred[inferred.length - 1].isArray())) { // vargs
@@ -3097,10 +3098,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             Iterator<ClassNode[]> candIt = candidates.iterator();
             while (candIt.hasNext()) {
                 ClassNode[] inferred = candIt.next();
-                final int length = closureParams.length;
-                for (int i = 0; i < length; i++) {
+                for (int i = 0, n = closureParams.length; i < n; i += 1) {
                     Parameter closureParam = closureParams[i];
-                    final ClassNode originType = closureParam.getOriginType();
+                    ClassNode originType = closureParam.getOriginType();
                     ClassNode inferredType;
                     if (i < inferred.length - 1 || inferred.length == closureParams.length) {
                         inferredType = inferred[i];
@@ -3113,7 +3113,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             continue;
                         }
                     }
-                    if (!typeCheckMethodArgumentWithGenerics(originType, inferredType, i == length - 1)) {
+                    if (!typeCheckMethodArgumentWithGenerics(originType, inferredType, i == (n - 1))) {
                         candIt.remove();
                     }
                 }
@@ -3130,10 +3130,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (closureParams.length == 0 && inferred.length == 1) {
                 expression.putNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS, inferred);
             } else {
-                final int length = closureParams.length;
-                for (int i = 0; i < length; i++) {
+                for (int i = 0, n = closureParams.length; i < n; i += 1) {
                     Parameter closureParam = closureParams[i];
-                    final ClassNode originType = closureParam.getOriginType();
+                    ClassNode originType = closureParam.getOriginType();
                     ClassNode inferredType = OBJECT_TYPE;
                     if (i < inferred.length - 1 || inferred.length == closureParams.length) {
                         inferredType = inferred[i];
@@ -3145,7 +3144,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             addError("Incorrect number of parameters. Expected " + inferred.length + " but found " + closureParams.length, expression);
                         }
                     }
-                    boolean lastArg = i == length - 1;
+                    boolean lastArg = i == (n - 1);
 
                     if (!typeCheckMethodArgumentWithGenerics(originType, inferredType, lastArg)) {
                         addError("Expected parameter of type " + inferredType.toString(false) + " but got " + originType.toString(false), closureParam.getType());
@@ -3159,8 +3158,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     private ClassNode[] resolveGenericsFromTypeHint(final ClassNode receiver, final Expression arguments, final MethodNode selectedMethod, final ClassNode[] signature) {
         ClassNode dummyResultNode = new ClassNode("ClForInference$" + UNIQUE_LONG.incrementAndGet(), 0, OBJECT_TYPE).getPlainNodeReference();
-        final GenericsType[] genericTypes = new GenericsType[signature.length];
-        for (int i = 0; i < signature.length; i++) {
+        GenericsType[] genericTypes = new GenericsType[signature.length];
+        for (int i = 0, n = signature.length; i < n; i += 1) {
             genericTypes[i] = new GenericsType(signature[i]);
         }
         dummyResultNode.setGenericsTypes(genericTypes);
@@ -3209,7 +3208,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     private static ClassNode createUsableClassNodeFromGenericsType(final GenericsType genericsType) {
         ClassNode value = genericsType.getType();
         if (genericsType.isPlaceholder()) {
-            value = OBJECT_TYPE;
+            value = value.isRedirectNode() ? value.redirect() : OBJECT_TYPE; // GROOVY-9968
         }
         ClassNode lowerBound = genericsType.getLowerBound();
         if (lowerBound != null) {
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index 8ea49057b5..1a9aef2677 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -1440,4 +1440,21 @@ method()
             new B()
         '''
     }
+
+    void testGroovy9968() {
+        assertScript '''
+            import groovy.transform.*
+            @Canonical class Pogo { String prop }
+            @Canonical class Type<T extends Pogo> implements Iterable<T> {
+                Iterator<T> iterator() {
+                    list.iterator()
+                }
+                List<T> list
+            }
+
+            def iterable = new Type([new Pogo('x'), new Pogo('y'), new Pogo('z')])
+            assert iterable.collect { Pogo p -> p.prop } == ['x', 'y', 'z']
+            assert iterable.collect { it.prop } == ['x', 'y', 'z']
+        '''
+    }
 }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index aecf6fe4ec..6516ecb130 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2695,6 +2695,63 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         }
     }
 
+    @NotYetImplemented // GROOVY-10055
+    void testSelfReferentialTypeParameter2() {
+        assertScript '''
+            abstract class A<Self extends A<Self>> {
+                Self foo(inputs) {
+                    // ...
+                    this
+                }
+            }
+            abstract class B<Self extends B<Self>> extends A<Self> {
+                Self bar(inputs) {
+                    // ...
+                    this
+                }
+            }
+            class C<Self extends C<Self>> extends B<Self> { // see org.testcontainers.containers.PostgreSQLContainer
+                Self baz(inputs) {
+                    // ...
+                    this
+                }
+            }
+
+            new C<>()
+            .foo('x')
+            .bar('y') // error
+            .baz('z') // error
+        '''
+    }
+
+    @NotYetImplemented // GROOVY-10556
+    void testSelfReferentialTypeParameter3() {
+        ['(B) this', 'this as B'].each { self ->
+            assertScript """
+                abstract class A<B extends A<B,X>,X> {
+                    B m() {
+                       $self
+                    }
+                }
+                (new A(){}).m()
+            """
+        }
+    }
+
+    // GROOVY-9968
+    void testSelfReferentialTypeParameter4() {
+        assertScript '''
+            abstract class A<B extends A<?>> implements Iterable<B> {
+            }
+            void m(A iterable) {
+                iterable?.each {
+                    m(it)
+                }
+            }
+            m(null)
+        '''
+    }
+
     // GROOVY-7804
     void testParameterlessClosureToGenericSAMTypeArgumentCoercion() {
         assertScript '''