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/11/02 00:21:28 UTC

[groovy] branch GROOVY_2_5_X updated: GROOVY-7141: STC: infer param types for closure map to SAM-type coercion

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


The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
     new 94d025d105 GROOVY-7141: STC: infer param types for closure map to SAM-type coercion
94d025d105 is described below

commit 94d025d1058f5922a0849089d9da94556005113c
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Tue Nov 1 17:39:03 2022 -0500

    GROOVY-7141: STC: infer param types for closure map to SAM-type coercion
    
    2_5_X backport
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 25 +++++++++++++----
 .../stc/ClosureParamTypeInferenceSTCTest.groovy    | 32 +++++++++++++++++++++-
 2 files changed, 50 insertions(+), 7 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 0bb117352f..f3d39449ff 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -647,7 +647,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             }
             if (variable != null) {
                 ClassNode inferredType = getInferredTypeFromTempInfo(variable, (ClassNode) variable.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE));
-                if (inferredType != null && !inferredType.getName().equals(ClassHelper.OBJECT) && !inferredType.equals(accessedVariable.getType())) {
+                if (inferredType != null && !inferredType.getName().equals(ClassHelper.OBJECT) && !inferredType.equals(accessedVariable.getOriginType())) {
                     vexp.putNodeMetaData(StaticTypesMarker.INFERRED_RETURN_TYPE, inferredType);
                 }
             }
@@ -1033,6 +1033,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
         if (rhsExpression instanceof ClosureExpression) {
             MethodNode abstractMethod = findSAM(lhsType);
+            ClosureExpression closure = (ClosureExpression) rhsExpression;
             Map<GenericsType, GenericsType> mappings = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(abstractMethod.getDeclaringClass(), lhsType);
 
             ClassNode[] samParameterTypes = extractTypesFromParameters(abstractMethod.getParameters());
@@ -1042,13 +1043,18 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 }
             }
 
-            Parameter[] closureParameters = getParametersSafe((ClosureExpression) rhsExpression);
-            if (closureParameters.length == samParameterTypes.length || (1 == samParameterTypes.length && hasImplicitParameter((ClosureExpression) rhsExpression))) {
-                for (int i = 0; i < closureParameters.length; i += 1) {
+            Parameter[] closureParameters = getParametersSafe(closure);
+            if (samParameterTypes.length == 1 && hasImplicitParameter(closure)) {
+                Variable it = closure.getVariableScope().getDeclaredVariable("it"); // GROOVY-7141
+                closureParameters = new Parameter[] {it instanceof Parameter ? (Parameter) it : new Parameter(DYNAMIC_TYPE, "")};
+            }
+
+            int n = closureParameters.length;
+            if (n == samParameterTypes.length) {
+                for (int i = 0; i < n; i += 1) {
                     Parameter parameter = closureParameters[i];
                     if (parameter.isDynamicTyped()) {
                         parameter.setType(samParameterTypes[i]);
-                        parameter.setOriginType(samParameterTypes[i]);
                     }
                 }
             } else {
@@ -1061,6 +1067,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 returnType = GenericsUtils.findActualTypeByGenericsPlaceholderName(returnType.getUnresolvedName(), mappings);
             }
             storeInferredReturnType(rhsExpression, returnType);
+
+        } else if (rhsExpression instanceof MapExpression) { // GROOVY-7141
+            List<MapEntryExpression> spec = ((MapExpression) rhsExpression).getMapEntryExpressions();
+            if (spec.size() == 1 && spec.get(0).getValueExpression() instanceof ClosureExpression
+                    && findSAM(lhsType).getName().equals(spec.get(0).getKeyExpression().getText())) {
+                processFunctionalInterfaceAssignment(lhsType, spec.get(0).getValueExpression());
+            }
         }
     }
 
@@ -5918,7 +5931,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             this.parameter = parameter;
             ClassNode inferredType = getNodeMetaData(StaticTypesMarker.INFERRED_TYPE);
             if (inferredType == null) {
-                setNodeMetaData(StaticTypesMarker.INFERRED_TYPE, parameter.getOriginType());
+                setNodeMetaData(StaticTypesMarker.INFERRED_TYPE, parameter.getType());
             }
         }
 
diff --git a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
index 2246d15f42..44bfbfea47 100644
--- a/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosureParamTypeInferenceSTCTest.groovy
@@ -1181,7 +1181,37 @@ class ClosureParamTypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testInferenceWithSAMTypeCoercion() {
+    // GROOVY-7141
+    void testInferenceWithSAMTypeCoercion1() {
+        String sam = '''
+            @FunctionalInterface
+            interface I {
+                String foo(String s)
+            }
+        '''
+        assertScript sam + '''
+            def impl = [foo: { it.toUpperCase() }] as I
+            String result = impl.foo('bar')
+            assert result == 'BAR'
+        '''
+        assertScript sam + '''
+            def impl = [foo: { s -> s.toUpperCase() }] as I
+            String result = impl.foo('bar')
+            assert result == 'BAR'
+        '''
+        assertScript sam + '''
+            def impl = [foo: { String s -> s.toUpperCase() }] as I
+            String result = impl.foo('bar')
+            assert result == 'BAR'
+        '''
+        assertScript '''
+            def impl = [apply: { it.toUpperCase() }] as java.util.function.Function<String,String>
+            String result = impl.apply('bar')
+            assert result == 'BAR'
+        '''
+    }
+
+    void testInferenceWithSAMTypeCoercion2() {
         assertScript '''import java.util.concurrent.Callable
             interface Action<T> {
                 void execute(T thing)