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 2023/06/15 15:23:19 UTC

[groovy] 01/02: GROOVY-10972: SC: select from closest matches for method reference

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

commit 3c70397f12f2f1406e68fe935638f8c44fdc26ca
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Apr 19 10:10:40 2023 -0500

    GROOVY-10972: SC: select from closest matches for method reference
    
    3_0_X backport
---
 ...StaticTypesMethodReferenceExpressionWriter.java | 24 +++++++++++++++++-----
 .../transform/stc/MethodReferenceTest.groovy       |  1 -
 2 files changed, 19 insertions(+), 6 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 2e7ee12c87..6df30b0ecd 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
@@ -42,6 +42,7 @@ import org.objectweb.asm.Handle;
 import org.objectweb.asm.Opcodes;
 
 import java.util.Arrays;
+import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 
@@ -55,6 +56,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.GenericsUtils.extractPlaceholders;
 import static org.codehaus.groovy.ast.tools.ParameterUtils.parametersCompatible;
+import static org.codehaus.groovy.transform.stc.StaticTypeCheckingSupport.allParametersAndArgumentsMatch;
 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;
@@ -245,7 +247,10 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
     private MethodNode findMethodRefMethod(final String methodName, final Parameter[] samParameters, final Expression typeOrTargetRef, final ClassNode typeOrTargetRefType) {
         List<MethodNode> methods = findVisibleMethods(methodName, typeOrTargetRefType);
 
-        methods.removeIf(method -> {
+        ClassNode[] samSignature = Arrays.stream(samParameters).map(Parameter::getType).toArray(ClassNode[]::new);
+
+        // GROOVY-10972: select from closest matches
+        int[] distances = methods.stream().mapToInt(method -> {
             Parameter[] parameters = method.getParameters();
             if (isTypeReferringInstanceMethod(typeOrTargetRef, method)) {
                 // there is an implicit parameter for "String::length"
@@ -263,15 +268,24 @@ public class StaticTypesMethodReferenceExpressionWriter extends MethodReferenceE
                 Map<GenericsType.GenericsTypeName, GenericsType> spec = extractPlaceholders(typeOrTargetRefType);
                 parameters = Arrays.stream(parameters).map(p -> new Parameter(resolveClassNodeGenerics(spec, null, p.getType()), p.getName())).toArray(Parameter[]::new);
             }
-            return !parametersCompatible(samParameters, parameters);
-        });
 
+            if (parametersCompatible(samParameters, parameters))
+                return allParametersAndArgumentsMatch(parameters, samSignature);
+
+            return -1; // no match
+        }).toArray();
+
+        int i = 0, bestDistance = Arrays.stream(distances).filter(d -> d >= 0).min().orElse(0);
+        for (Iterator<MethodNode> it = methods.iterator(); it.hasNext(); ) {
+            it.next(); if (distances[i++] != bestDistance) it.remove();
+        }
+
+        if (methods.isEmpty()) return null;
+        if (methods.size() == 1) return methods.get(0);
         return chooseMethodRefMethod(methods, typeOrTargetRef, typeOrTargetRefType);
     }
 
     private MethodNode chooseMethodRefMethod(final List<MethodNode> methods, final Expression typeOrTargetRef, final ClassNode typeOrTargetRefType) {
-        if (methods.isEmpty()) return null;
-        if (methods.size() == 1) return methods.get(0);
         return methods.stream().max(comparingInt((MethodNode mn) -> {
             int score = 9;
             for (ClassNode cn = typeOrTargetRefType; cn != null && !cn.equals(mn.getDeclaringClass()); cn = cn.getSuperClass()) {
diff --git a/src/test/groovy/transform/stc/MethodReferenceTest.groovy b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
index 84f038bca1..50618d53f6 100644
--- a/src/test/groovy/transform/stc/MethodReferenceTest.groovy
+++ b/src/test/groovy/transform/stc/MethodReferenceTest.groovy
@@ -474,7 +474,6 @@ final class MethodReferenceTest {
         '''
     }
 
-    @NotYetImplemented
     @Test // instance::instanceMethod -- GROOVY-10972
     void testFunctionII4() {
         assertScript imports + '''