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 2021/03/21 18:59:40 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-9977: share logic for functional interface assignment

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

emilles 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 67e5987  GROOVY-9977: share logic for functional interface assignment
67e5987 is described below

commit 67e59877e906dc4211794f869f896ab07aada6f0
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Mar 21 13:30:36 2021 -0500

    GROOVY-9977: share logic for functional interface assignment
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 51 ++++++++++++----------
 src/test/groovy/transform/stc/LambdaTest.groovy    | 37 ++++++++++++++++
 2 files changed, 66 insertions(+), 22 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 abe3fce..e6e3fd2 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -745,25 +745,15 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 if (ensureValidSetter(expression, leftExpression, rightExpression, setterInfo)) {
                     return;
                 }
+                lType = getType(leftExpression);
             } else {
                 lType = getType(leftExpression);
-                boolean isFunctionalInterface = isFunctionalInterface(lType);
-                if (isFunctionalInterface && rightExpression instanceof MethodReferenceExpression) {
-                    LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lType);
-                    if (op == ASSIGN) {
-                        inferParameterAndReturnTypesOfClosureOnRHS(lType, lambdaExpression);
-                    }
-                    rightExpression.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
-                    rightExpression.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
-
-                } else if (op == ASSIGN && isFunctionalInterface && rightExpression instanceof ClosureExpression) {
-                    inferParameterAndReturnTypesOfClosureOnRHS(lType, (ClosureExpression) rightExpression);
+                if (op == ASSIGN && isFunctionalInterface(lType)) {
+                    processFunctionalInterfaceAssignment(lType, rightExpression);
                 }
-
                 rightExpression.visit(this);
             }
 
-            if (lType == null) lType = getType(leftExpression);
             ClassNode rType = isNullConstant(rightExpression) && !isPrimitiveType(lType)
                     ? UNKNOWN_PARAMETER_TYPE // null to primitive type is handled elsewhere
                     : getInferredTypeFromTempInfo(rightExpression, getType(rightExpression));
@@ -913,6 +903,18 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
+    private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
+        if (rhsExpression instanceof ClosureExpression) {
+            inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) rhsExpression);
+        } else if (rhsExpression instanceof MethodReferenceExpression) {
+            LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lhsType);
+
+            inferParameterAndReturnTypesOfClosureOnRHS(lhsType, lambdaExpression);
+            rhsExpression.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
+            rhsExpression.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
+        }
+    }
+
     private void inferParameterAndReturnTypesOfClosureOnRHS(final ClassNode lhsType, final ClosureExpression rhsExpression) {
         Tuple2<ClassNode[], ClassNode> typeInfo = GenericsUtils.parameterizeSAM(lhsType);
         Parameter[] closureParameters = getParametersSafe(rhsExpression);
@@ -1815,7 +1817,9 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         try {
             typeCheckingContext.isInStaticContext = node.isInStaticContext();
             currentProperty = node;
-            super.visitProperty(node);
+            visitAnnotations(node);
+            visitClassCodeContainer(node.getGetterBlock());
+            visitClassCodeContainer(node.getSetterBlock());
         } finally {
             currentProperty = null;
             typeCheckingContext.isInStaticContext = osc;
@@ -1828,19 +1832,22 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         try {
             typeCheckingContext.isInStaticContext = node.isInStaticContext();
             currentField = node;
-            super.visitField(node);
+            visitAnnotations(node);
             Expression init = node.getInitialExpression();
             if (init != null) {
-                FieldExpression left = new FieldExpression(node);
-                BinaryExpression bexp = assignX(left, init, node);
-                ClassNode lType = getType(node), rType = getType(init);
-                typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
-
+                ClassNode lType = getType(node);
+                if (isFunctionalInterface(lType)) { // GROOVY-9977
+                    processFunctionalInterfaceAssignment(lType, init);
+                }
+                init.visit(this);
+                ClassNode rType = getType(init);
                 if (init instanceof ConstructorCallExpression) {
                     inferDiamondType((ConstructorCallExpression) init, lType);
-                } else if (init instanceof ClosureExpression && isFunctionalInterface(lType)) {
-                    inferParameterAndReturnTypesOfClosureOnRHS(lType, (ClosureExpression) init);
                 }
+
+                FieldExpression left = new FieldExpression(node);
+                BinaryExpression bexp = assignX(left, init, node);
+                typeCheckAssignment(bexp, left, lType, init, getResultType(lType, ASSIGN, rType, bexp));
             }
         } finally {
             currentField = null;
diff --git a/src/test/groovy/transform/stc/LambdaTest.groovy b/src/test/groovy/transform/stc/LambdaTest.groovy
index af9458c..9d6763e 100644
--- a/src/test/groovy/transform/stc/LambdaTest.groovy
+++ b/src/test/groovy/transform/stc/LambdaTest.groovy
@@ -294,6 +294,43 @@ final class LambdaTest {
     }
 
     @Test
+    void testComparator1() {
+        assertScript '''
+            @groovy.transform.CompileStatic class T {
+                Comparator<Integer> c = (Integer a, Integer b) -> Integer.compare(a, b)
+            }
+            def t = new T()
+            assert t.c.compare(0,0) == 0
+        '''
+
+        def err = shouldFail '''
+            @groovy.transform.CompileStatic class T {
+                Comparator<Integer> c = (int a, int b) -> Integer.compare(a, b)
+            }
+        '''
+        assert err =~ /Cannot assign java.util.Comparator <int> to: java.util.Comparator <Integer>/
+    }
+
+    @Test // GROOVY-9977
+    void testComparator2() {
+        assertScript '''
+            @groovy.transform.CompileStatic
+            class T {
+                Comparator<Integer> c = (a, b) -> Integer.compare(a, b)
+
+                static void m1() {
+                    Comparator<Integer> x = (a, b) -> Integer.compare(a, b)
+                }
+                void m2() {
+                    Comparator<Integer> y = (a, b) -> Integer.compare(a, b)
+                }
+            }
+            def t = new T()
+            assert t.c.compare(0,0) == 0
+        '''
+    }
+
+    @Test
     void testFunctionWithLocalVariables() {
         assertScript '''
             import groovy.transform.CompileStatic