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/05 23:46:32 UTC
[groovy] branch master 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 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 75520486cc GROOVY-10742: SC: error for `void` method reference if need return value
75520486cc is described below
commit 75520486ccd7f12b4f4c1e93cba6e738eedd0aa7
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, '''