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:04:49 UTC

[groovy] branch GROOVY_4_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_4_0_X
in repository https://gitbox.apache.org/repos/asf/groovy.git


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new f66135ee3d GROOVY-10742: SC: error for `void` method reference if need return value
f66135ee3d is described below

commit f66135ee3d94cf29075ec74afbce562bd495b188
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       | 13 +++++++
 2 files changed, 33 insertions(+), 24 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 1e4b3fdaa3..6a3ec1ffd4 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
@@ -52,12 +52,14 @@ 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.GeneralUtils.varX;
+import static org.codehaus.groovy.ast.tools.GenericsUtils.extractPlaceholders;
 import static org.codehaus.groovy.ast.tools.ParameterUtils.isVargs;
 import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersCompatible;
 import static org.codehaus.groovy.runtime.DefaultGroovyMethods.last;
 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);
@@ -104,7 +103,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;
@@ -169,7 +169,7 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
         try {
             Handle bootstrapMethod = createBootstrapMethod(classNode.isInterface(), false);
             Object[] bootstrapArgs = createBootstrapMethodArguments(
-                    abstractMethodDesc,
+                    createMethodDescriptor(abstractMethod),
                     referenceKind,
                     methodRefMethod.getDeclaringClass(),
                     methodRefMethod,
@@ -181,25 +181,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 && isTypeReferringInstanceMethod(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() && !ClassHelper.isPrimitiveVoid(samReturnType)) {
+            addFatalError("Invalid return type: void is not convertible to " + samReturnType.getText(), methodReference);
+        } else if (samParameters.length > 0 && isTypeReferringInstanceMethod(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 443e1a8126..ef9688f559 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -877,6 +877,19 @@ final class MethodReferenceTest {
         '''
     }
 
+    @Test // GROOVY-10742
+    void testVoidMethodSelection() {
+        def err = shouldFail shell, '''
+            void foo(bar) {
+            }
+            @CompileStatic
+            void test() {
+                Function<Object,String> f = this::foo
+            }
+        '''
+        assert err =~ /Invalid return type: void is not convertible to java.lang.String/
+    }
+
     @Test // GROOVY-10269
     void testNotFunctionalInterface() {
         def err = shouldFail shell, '''