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/12/01 22:51:07 UTC

[groovy] branch master updated: GROOVY-10742, GROOVY-10858: STC: return type of method pointer/reference

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 798903b2ef GROOVY-10742, GROOVY-10858: STC: return type of method pointer/reference
798903b2ef is described below

commit 798903b2ef9089ebf8b39ce96eda8b654ae8cdb8
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Thu Dec 1 16:50:36 2022 -0600

    GROOVY-10742, GROOVY-10858: STC: return type of method pointer/reference
---
 .../transform/stc/StaticTypeCheckingSupport.java   |  2 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 49 ++++++++++------------
 .../transform/stc/MethodReferenceTest.groovy       | 18 ++++++--
 3 files changed, 38 insertions(+), 31 deletions(-)

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 57eef99ea9..48cf4afe26 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1385,7 +1385,7 @@ public abstract class StaticTypeCheckingSupport {
             GenericsType gt = GenericsUtils.buildWildcardType(parameterType);
             if (!gt.isCompatibleWith(argumentType)) {
                 boolean samCoercion = isSAMType(parameterType) && argumentType.equals(CLOSURE_TYPE);
-                if (!samCoercion) return false;
+                if (!samCoercion) return false; // else assume parameters and return checked earlier
             }
         } else if (parameterType.isArray() && argumentType.isArray()) {
             // verify component type
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 d0cd9d77df..3ef7d60c4b 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2408,9 +2408,10 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
             String nameText = nameExpr.getText();
 
             if ("new".equals(nameText)) {
-                ClassNode receiverType = getType(expression.getExpression());
-                if (isClassClassNodeWrappingConcreteType(receiverType)) {
-                    storeType(expression, wrapClosureType(receiverType));
+                ClassNode type = getType(expression.getExpression());
+                if (isClassClassNodeWrappingConcreteType(type)){
+                    type = type.getGenericsTypes()[0].getType();
+                    storeType(expression,wrapClosureType(type));
                 }
                 return;
             }
@@ -2454,7 +2455,8 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                             storeType(expression, closureType);
                         });
                 expression.putNodeMetaData(MethodNode.class, candidates);
-            } else if (!(expression instanceof MethodReferenceExpression)) {
+            } else if (!(expression instanceof MethodReferenceExpression)
+                    || this.getClass() == StaticTypeCheckingVisitor.class) {
                 ClassNode type = wrapTypeIfNecessary(getType(expression.getExpression()));
                 if (isClassClassNodeWrappingConcreteType(type)) type = type.getGenericsTypes()[0].getType();
                 addStaticTypeError("Cannot find matching method " + prettyPrintTypeName(type) + "#" + nameText + ". Please check if the declared type is correct and if the method exists.", nameExpr);
@@ -3674,13 +3676,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         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();
+            ClassNode sourceExprType = getType(methodReference.getExpression());
+            if (isClassClassNodeWrappingConcreteType(sourceExprType) && !"new".equals(methodReference.getMethodName().getText())) {
+                // GROOVY-10734: Type::instanceMethod has implied first param
+                List<MethodNode> candidates = methodReference.getNodeMetaData(MethodNode.class);
+                if (candidates != null && !candidates.isEmpty() && candidates.stream().allMatch(mn ->
+                        !(mn instanceof ExtensionMethodNode ? ((ExtensionMethodNode) mn).isStaticExtension() : mn.isStatic()))) {
+                    firstParamType = sourceExprType.getGenericsTypes()[0].getType();
                 }
             }
             parameters = new Parameter[nParameters];
@@ -4560,28 +4562,23 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
     }
 
     private ClassNode inferSAMTypeGenericsInAssignment(final ClassNode samType, final MethodNode abstractMethod, final ClassNode closureType, final ClosureExpression closureExpression) {
-        // if the sam type or closure type do not provide generics information,
-        // we cannot infer anything, thus we simply return the provided samUsage
-        GenericsType[] samTypeGenerics = samType.getGenericsTypes();
-        GenericsType[] closureGenerics = closureType.getGenericsTypes();
-        if (samTypeGenerics == null || closureGenerics == null) return samType;
-
-        // extract the generics from the return type
         Map<GenericsTypeName, GenericsType> connections = new HashMap<>();
-        extractGenericsConnections(connections, wrapTypeIfNecessary(getInferredReturnType(closureExpression)), abstractMethod.getReturnType());
 
-        // next we get the block parameter types and set the generics
-        // information just like before
-        // TODO: add vargs handling
+        // extract generics from the closure return type
+        ClassNode closureReturnType = isClosureWithType(closureType)
+                ? getCombinedBoundType(closureType.getGenericsTypes()[0])
+                : wrapTypeIfNecessary(getInferredReturnType(closureExpression));
+        extractGenericsConnections(connections, closureReturnType, abstractMethod.getReturnType());
+
+        // extract generics from the closure parameters
         if (closureExpression.isParameterSpecified()) {
             Parameter[] closureParams = closureExpression.getParameters();
-            Parameter[] methodParams = abstractMethod.getParameters();
+            Parameter[]  methodParams =    abstractMethod.getParameters();
             for (int i = 0, n = Math.min(closureParams.length, methodParams.length); i < n; i += 1) {
-                ClassNode closureParamType = closureParams[i].getType();
-                ClassNode methodParamType = methodParams[i].getType();
-                extractGenericsConnections(connections, closureParamType, methodParamType);
+                extractGenericsConnections(connections, closureParams[i].getType(), methodParams[i].getType());
             }
         }
+
         return applyGenericsContext(connections, samType.redirect());
     }
 
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 0a9c5d3ae7..e155747d2a 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -18,6 +18,7 @@
  */
 package groovy.transform.stc
 
+import groovy.test.NotYetImplemented
 import org.junit.Test
 
 import static groovy.test.GroovyAssert.assertScript
@@ -603,7 +604,7 @@ final class MethodReferenceTest {
         assertScript shell, '''
             @CompileStatic
             void test() {
-                Function<String, Integer> f = Integer::new
+                Function<String, Integer> f = Integer::new // deprcated; parseInt/valueOf
                 def result = ["1", "2", "3"].stream().map(f).collect(Collectors.toList())
                 assert result == [1, 2, 3]
             }
@@ -725,6 +726,7 @@ final class MethodReferenceTest {
         '''
     }
 
+    @NotYetImplemented
     @Test // class::staticMethod
     void testFunctionCS4() {
         assertScript shell, '''
@@ -810,7 +812,7 @@ final class MethodReferenceTest {
         '''
     }
 
-    @groovy.test.NotYetImplemented
+    @NotYetImplemented
     @Test // class::staticMethod
     void testFunctionCS8() {
         assertScript shell, '''
@@ -983,7 +985,7 @@ final class MethodReferenceTest {
         '''
     }
 
-    @Test // GROOVY-10742
+    @Test // GROOVY-10742, GROOVY-10858
     void testIncompatibleReturnType() {
         def err = shouldFail shell, '''
             void foo(bar) {
@@ -993,7 +995,15 @@ final class MethodReferenceTest {
                 Function<Object,String> f = this::foo
             }
         '''
-        assert err =~ /Invalid return type: void is not convertible to java.lang.String/
+        assert err =~ /Cannot assign java.util.function.Function<java.lang.Object, java.lang.Void> to: java.util.function.Function<java.lang.Object, java.lang.String>/
+
+        err = shouldFail shell, '''
+            @CompileStatic
+            void test() {
+                Function<Object,Number> f = Object::toString
+            }
+        '''
+        assert err =~ /Cannot assign java.util.function.Function<java.lang.Object, java.lang.String> to: java.util.function.Function<java.lang.Object, java.lang.Number>/
     }
 
     @Test // GROOVY-10269