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/09/03 16:28:45 UTC

[groovy] branch master updated: GROOVY-10734: STC: `Type::instanceMethod` specifies first param type

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

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


The following commit(s) were added to refs/heads/master by this push:
     new 9bffe17a68 GROOVY-10734: STC: `Type::instanceMethod` specifies first param type
9bffe17a68 is described below

commit 9bffe17a6831fedd7eeb62ace9b8a920b1451278
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Sep 3 11:17:37 2022 -0500

    GROOVY-10734: STC: `Type::instanceMethod` specifies first param type
---
 .../transform/stc/StaticTypeCheckingVisitor.java   | 32 +++++++++++++++-------
 .../transform/stc/MethodReferenceTest.groovy       | 18 ++++++++++++
 2 files changed, 40 insertions(+), 10 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 378e63cfc8..2fb02586d5 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -924,7 +924,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             if (source instanceof ClosureExpression) {
                 inferParameterAndReturnTypesOfClosureOnRHS(target, (ClosureExpression) source);
             } else if (source instanceof MethodReferenceExpression) {
-                LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(target);
+                LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(target, (MethodReferenceExpression) source);
 
                 inferParameterAndReturnTypesOfClosureOnRHS(target, lambdaExpression);
                 source.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
@@ -3684,7 +3684,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                     newArgumentExpressions.add(argumentExpression);
                 } else {
                     methodReferencePositions.add(i);
-                    newArgumentExpressions.add(constructLambdaExpressionForMethodReference(paramType));
+                    newArgumentExpressions.add(constructLambdaExpressionForMethodReference(paramType, (MethodReferenceExpression) argumentExpression));
                 }
             }
         }
@@ -3700,14 +3700,26 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
     }
 
-    private LambdaExpression constructLambdaExpressionForMethodReference(final ClassNode functionalInterfaceType) {
-        int nParameters = findSAM(functionalInterfaceType).getParameters().length;
-        Parameter[] parameters = new Parameter[nParameters];
-        for (int i = 0; i < nParameters; i += 1) {
-            parameters[i] = new Parameter(dynamicType(), "p" + i);
+    private LambdaExpression constructLambdaExpressionForMethodReference(final ClassNode functionalInterfaceType, final MethodReferenceExpression methodReference) {
+        Parameter[] parameters = findSAM(functionalInterfaceType).getParameters();
+        int nParameters = parameters.length;
+        if (nParameters > 0) {
+            ClassNode firstParamType = dynamicType();
+            // GROOVY-10734: Type::instanceMethod has implied first param
+            List<MethodNode> candidates = methodReference.getNodeMetaData(MethodNode.class);
+            if (candidates != null && !candidates.isEmpty()) {
+                ClassNode objExpType = getType(methodReference.getExpression());
+                if (isClassClassNodeWrappingConcreteType(objExpType)
+                        && candidates.stream().allMatch(mn -> !mn.isStatic())) {
+                    firstParamType = objExpType.getGenericsTypes()[0].getType();
+                }
+            }
+            parameters = new Parameter[nParameters];
+            for (int i = 0; i < nParameters; i += 1) {
+                parameters[i] = new Parameter(i == 0 ? firstParamType : dynamicType(), "p" + i);
+            }
         }
-
-        return new LambdaExpression(parameters, EmptyStatement.INSTANCE);
+        return new LambdaExpression(parameters, GENERATED_EMPTY_STATEMENT);
     }
 
     /**
@@ -5634,7 +5646,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                 ClassNode[] paramTypes = applyGenericsContext(samTypeConnections, extractTypesFromParameters(parameters));
                 ClassNode[] matchTypes = candidates.stream()
                         .map(candidate -> collateMethodReferenceParameterTypes(mp, candidate))
-                        .filter(candidate -> checkSignatureSuitability(candidate, paramTypes))
+                        .filter(signature -> checkSignatureSuitability(signature, paramTypes))
                         .findFirst().orElse(null); // TODO: order signatures by param distance
                 if (matchTypes != null) {
                     Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 8758987fc3..443e1a8126 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -216,6 +216,24 @@ final class MethodReferenceTest {
         '''
     }
 
+    @Test // class::instanceMethod -- GROOVY-10734
+    void testFunctionCI8() {
+        assertScript shell, '''
+            class C {
+                String p
+            }
+            @CompileStatic
+            Map test(Collection<C> items) {
+                items.stream().collect(
+                    Collectors.groupingBy(C::getP) // Failed to find the expected method[getP(Object)] in the type[C]
+                )
+            }
+            def map = test([new C(p:'foo'), new C(p:'bar'), new C(p:'foo')])
+            assert map.foo.size() == 2
+            assert map.bar.size() == 1
+        '''
+    }
+
     @Test // class::instanceMethod -- GROOVY-9974
     void testPredicateCI() {
         assertScript shell, '''