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 <[email protected]>
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, '''