This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY-11669 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit cd150946b1e0155e0e86ddc34b050de771223f58 Author: Eric Milles <[email protected]> AuthorDate: Sat May 17 23:42:14 2025 -0500 GROOVY-11669: SC: support `ClassLiteral::javaLangClassMethod` --- ...StaticTypesMethodReferenceExpressionWriter.java | 24 ++++++++++++- .../transform/stc/MethodReferenceTest.groovy | 41 ++++++++++++++++++++-- 2 files changed, 62 insertions(+), 3 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 fa7c4d8263..c526caa7b4 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 @@ -59,6 +59,7 @@ 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.GenericsUtils.makeClassSafe0; import static org.codehaus.groovy.ast.tools.ParameterUtils.isVargs; import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersCompatible; import static org.codehaus.groovy.runtime.ArrayGroovyMethods.last; @@ -116,10 +117,16 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE Map<MethodNode,MethodNode> bridgeMethods = typeOrTargetRefType.redirect().getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_BRIDGE_METHODS); if (bridgeMethods != null) methodRefMethod = bridgeMethods.getOrDefault(methodRefMethod, methodRefMethod); // bridge may not have been generated } + if (methodRefMethod == null && isClassExpression) { + var classValue = varX("_class_", typeOrTargetRefType); + var classClass = makeClassSafe0(ClassHelper.CLASS_Type, new GenericsType(typeOrTargetRefType)); + methodRefMethod = findMethodRefMethod(methodRefName, parametersWithExactType, classValue, classClass); + if (methodRefMethod != null) methodRefMethod = addSyntheticMethodForClassReference(methodRefMethod, typeOrTargetRefType); + } } validate(methodReferenceExpression, typeOrTargetRefType, methodRefName, methodRefMethod, parametersWithExactType, - resolveClassNodeGenerics(extractPlaceholders(functionalType), null, abstractMethod.getReturnType())); + resolveClassNodeGenerics(extractPlaceholders(functionalType), null, abstractMethod.getReturnType())); if (isBridgeMethod(methodRefMethod)) { targetIsArgument = true; // GROOVY-11301, GROOVY-11365 @@ -242,6 +249,21 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE return delegateMethod; } + private MethodNode addSyntheticMethodForClassReference(final MethodNode mn, final ClassNode classType) { + MethodCallExpression methodCall = callX(classX(classType), mn.getName(), new ArgumentListExpression(mn.getParameters())); + methodCall.setImplicitThis(false); + methodCall.setMethodTarget(mn); + methodCall.putNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET, mn); + + String methodName = "class$" + classType.getNameWithoutPackage() + "$" + mn.getName() + "$" + System.nanoTime(); + + ClassNode returnType = resolveClassNodeGenerics(Map.of(new GenericsType.GenericsTypeName("T"), new GenericsType(classType)), null, mn.getReturnType()); + + MethodNode delegateMethod = addSyntheticMethod(methodName, returnType, methodCall, mn.getParameters(), mn.getExceptions()); + delegateMethod.putNodeMetaData(StaticCompilationMetadataKeys.STATIC_COMPILE_NODE, Boolean.TRUE); + return delegateMethod; + } + private MethodNode addSyntheticMethodForVariadicReference(final MethodNode mn, final int samParameters, final boolean isStaticTarget) { Parameter[] parameters = new Parameter[samParameters]; Expression arguments, receiver; diff --git a/src/test/groovy/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/groovy/transform/stc/MethodReferenceTest.groovy index 74288f1eb4..2446bd8093 100644 --- a/src/test/groovy/groovy/transform/stc/MethodReferenceTest.groovy +++ b/src/test/groovy/groovy/transform/stc/MethodReferenceTest.groovy @@ -918,6 +918,33 @@ final class MethodReferenceTest { ''' } + // GROOVY-11669 + @Test // class::classMethod + void testFunctionCC1() { + assertScript shell, ''' + @CompileStatic + void test() { + [1,2,3].stream().map(Number::cast).collect(Collectors.toList()) + } + + test() + ''' + } + + // GROOVY-11669 + @Test // class::classMethod + void testFunctionCC2() { + def err = shouldFail shell, ''' + @CompileStatic + void test() { + [1,2,3].stream().map(String::cast).collect(Collectors.toList()) + } + + test() + ''' + assert err =~ /ClassCastException: Cannot cast java.lang.Integer to java.lang.String/ + } + @Test // class::new void testFunctionCN1() { assertScript shell, ''' @@ -1422,13 +1449,23 @@ final class MethodReferenceTest { test() ''' + assertScript shell, ''' + @CompileStatic + void test() { + Supplier<String> s = Object::toString + def result = s.get() + assert result == 'class java.lang.Object' + } + + test() + ''' def err = shouldFail shell, ''' @CompileStatic void test() { - Supplier<String> s = Object::toString // all options require an object + BinaryOperator<String> s = Object::toString } ''' - assert err.message.contains("Failed to find class method 'toString()' for the type: java.lang.Object") + assert err.message.contains("Failed to find class method 'toString(java.lang.String,java.lang.String)' or instance method 'toString(java.lang.String)' for the type: java.lang.Object") } // GROOVY-10859
