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/06 00:29:06 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-10742: SC: error for `void` method reference if need return value

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 a2e0f6b178 GROOVY-10742: SC: error for `void` method reference if need return value
a2e0f6b178 is described below

commit a2e0f6b17831cc19274cc5aeb837639b4e32fa56
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Sep 5 18:41:51 2022 -0500

    GROOVY-10742: SC: error for `void` method reference if need return value
---
 ...StaticTypesMethodReferenceExpressionWriter.java | 44 ++++++++++------------
 .../transform/stc/MethodReferenceTest.groovy       | 20 +++++++---
 2 files changed, 34 insertions(+), 30 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
index 536b53c5c9..b9382a9027 100644
--- a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
+++ b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticTypesMethodReferenceExpressionWriter.java
@@ -54,10 +54,12 @@ import static org.codehaus.groovy.ast.tools.GeneralUtils.classX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.ctorX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.nullX;
 import static org.codehaus.groovy.ast.tools.GeneralUtils.returnS;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.extractPlaceholders;
 import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersCompatible;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.filterMethodsByVisibility;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.findDGMMethodsForClassNode;
 import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.isAssignableTo;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.resolveClassNodeGenerics;
 
 /**
  * Generates bytecode for method reference expressions in statically-compiled code.
@@ -72,17 +74,14 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
 
     @Override
     public void writeMethodReferenceExpression(final MethodReferenceExpression methodReferenceExpression) {
-        ClassNode functionalInterfaceType = getFunctionalInterfaceType(methodReferenceExpression);
-        if (!ClassHelper.isFunctionalInterface(functionalInterfaceType)) {
-            // generate the default bytecode; most likely a method closure
+        ClassNode  functionalInterfaceType = getFunctionalInterfaceType(methodReferenceExpression);
+        MethodNode abstractMethod = ClassHelper.findSAM(functionalInterfaceType);
+        if (abstractMethod == null || !functionalInterfaceType.isInterface()) {
+            // generate the default bytecode -- most likely a method closure
             super.writeMethodReferenceExpression(methodReferenceExpression);
             return;
         }
 
-        ClassNode redirect = functionalInterfaceType.redirect();
-        MethodNode abstractMethod = ClassHelper.findSAM(redirect);
-        String abstractMethodDesc = createMethodDescriptor(abstractMethod);
-
         ClassNode classNode = controller.getClassNode();
         Expression typeOrTargetRef = methodReferenceExpression.getExpression();
         boolean isClassExpression = (typeOrTargetRef instanceof ClassExpression);
@@ -103,7 +102,8 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
             methodRefMethod = findMethodRefMethod(methodRefName, parametersWithExactType, typeOrTargetRef, typeOrTargetRefType);
         }
 
-        validate(methodReferenceExpression, typeOrTargetRef, typeOrTargetRefType, methodRefName, parametersWithExactType, methodRefMethod);
+        validate(methodReferenceExpression, typeOrTargetRefType, methodRefName, methodRefMethod, parametersWithExactType,
+                resolveClassNodeGenerics(extractPlaceholders(functionalInterfaceType), null, abstractMethod.getReturnType()));
 
         if (isExtensionMethod(methodRefMethod)) {
             ExtensionMethodNode extensionMethodNode = (ExtensionMethodNode) methodRefMethod;
@@ -148,7 +148,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
                 createAbstractMethodDesc(functionalInterfaceType, typeOrTargetRef),
                 createBootstrapMethod(classNode.isInterface(), false),
                 createBootstrapMethodArguments(
-                        abstractMethodDesc,
+                        createMethodDescriptor(abstractMethod),
                         referenceKind,
                         isConstructorReference ? classNode : typeOrTargetRefType,
                         methodRefMethod,
@@ -157,25 +157,21 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         );
 
         if (isClassExpression) {
-            controller.getOperandStack().push(redirect);
+            controller.getOperandStack().push(functionalInterfaceType);
         } else {
-            controller.getOperandStack().replace(redirect, 1);
+            controller.getOperandStack().replace(functionalInterfaceType, 1);
         }
     }
 
-    private void validate(final MethodReferenceExpression methodReferenceExpression, final Expression typeOrTargetRef, final ClassNode typeOrTargetRefType, final String methodRefName, final Parameter[] parametersWithExactType, final MethodNode methodRefMethod) {
-        if (methodRefMethod == null) {
-            addFatalError("Failed to find the expected method["
-                    + methodRefName + "("
-                    + Arrays.stream(parametersWithExactType)
-                            .map(e -> e.getType().getText())
-                            .collect(Collectors.joining(","))
-                    + ")] in the type[" + typeOrTargetRefType.getText() + "]", methodReferenceExpression);
-        } else if (parametersWithExactType.length > 0 && isTypeReferingInstanceMethod(typeOrTargetRef, methodRefMethod)) {
-            ClassNode firstParameterType = parametersWithExactType[0].getType();
-            if (!isAssignableTo(firstParameterType, typeOrTargetRefType)) {
-                throw new RuntimeParserException("Invalid receiver type: " + firstParameterType.getText() + " is not compatible with " + typeOrTargetRefType.getText(), typeOrTargetRef);
-            }
+    private void validate(final MethodReferenceExpression methodReference, final ClassNode targetType, final String methodName, final MethodNode methodNode, final Parameter[] samParameters, final ClassNode samReturnType) {
+        if (methodNode == null) {
+            String error = String.format("Failed to find the expected method[%s(%s)] in the type[%s]",
+                    methodName, Arrays.stream(samParameters).map(e -> e.getType().getText()).collect(Collectors.joining(",")), targetType.getText());
+            addFatalError(error, methodReference);
+        } else if (methodNode.isVoidMethod() && !samReturnType.equals(ClassHelper.VOID_TYPE)) {
+            addFatalError("Invalid return type: void is not convertible to " + samReturnType.getText(), methodReference);
+        } else if (samParameters.length > 0 && isTypeReferingInstanceMethod(methodReference.getExpression(), methodNode) && !isAssignableTo(samParameters[0].getType(), targetType)) {
+            throw new RuntimeParserException("Invalid receiver type: " + samParameters[0].getType().getText() + " is not compatible with " + targetType.getText(), methodReference.getExpression());
         }
     }
 
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index f2ea2d9e62..57527ef31c 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -100,7 +100,6 @@ final class MethodReferenceTest extends GroovyTestCase {
 
             p()
         '''
-
         assert err =~ /Invalid receiver type: java.lang.Integer is not compatible with java.lang.String/
     }
 
@@ -662,7 +661,6 @@ final class MethodReferenceTest extends GroovyTestCase {
 
             p()
         '''
-
         assert err =~ /Invalid receiver type: java.lang.Integer is not compatible with java.lang.String/
     }
 
@@ -737,7 +735,6 @@ final class MethodReferenceTest extends GroovyTestCase {
                 [1.0G, 2.0G, 3.0G].stream().reduce(0.0G, BigDecimal::addx)
             }
         '''
-
         assert err.contains('Failed to find the expected method[addx(java.math.BigDecimal,java.math.BigDecimal)] in the type[java.math.BigDecimal]')
     }
 
@@ -751,10 +748,23 @@ final class MethodReferenceTest extends GroovyTestCase {
                 Function<String,String> reference = String::toLowerCaseX
             }
         '''
-
         assert err.contains('Failed to find the expected method[toLowerCaseX(java.lang.String)] in the type[java.lang.String]')
     }
 
+    // GROOVY-10742
+    void testVoidMethodSelection() {
+        def err = shouldFail '''
+            import java.util.function.Function
+
+            void foo(bar) { }
+            @groovy.transform.CompileStatic
+            void test() {
+                Function<Object,String> f = this::foo
+            }
+        '''
+        assert err =~ /Invalid return type: void is not convertible to java.lang.String/
+    }
+
     // GROOVY-10269
     void testNotFunctionalInterface() {
         def err = shouldFail '''
@@ -771,7 +781,6 @@ final class MethodReferenceTest extends GroovyTestCase {
                 baz(this::foo) // not yet supported!
             }
         '''
-
         assert err =~ /The argument is a method reference, but the parameter type is not a functional interface/
     }
 
@@ -791,7 +800,6 @@ final class MethodReferenceTest extends GroovyTestCase {
                 }
             }
         '''
-
         assert err =~ /The argument is a method reference, but the parameter type is not a functional interface/
     }
 }