You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2023/01/17 22:30:50 UTC

[groovy] branch master updated: GROOVY-10905: Improve matching implicit arg closures to SAM types

This is an automated email from the ASF dual-hosted git repository.

paulk 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 e29b9288ee GROOVY-10905: Improve matching implicit arg closures to SAM types
e29b9288ee is described below

commit e29b9288ee10d6ebe0443722294021f64371fbad
Author: Paul King <pa...@asert.com.au>
AuthorDate: Mon Jan 16 18:52:51 2023 +1000

    GROOVY-10905: Improve matching implicit arg closures to SAM types
---
 .../codehaus/groovy/runtime/MetaClassHelper.java   |  8 +++++++-
 .../transform/stc/StaticTypeCheckingSupport.java   | 10 ++++++++--
 .../groovy/transform/stc/ClosuresSTCTest.groovy    | 22 ++++++++++++++++++++++
 3 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/MetaClassHelper.java b/src/main/java/org/codehaus/groovy/runtime/MetaClassHelper.java
index df51723ea2..3e4b3fec8d 100644
--- a/src/main/java/org/codehaus/groovy/runtime/MetaClassHelper.java
+++ b/src/main/java/org/codehaus/groovy/runtime/MetaClassHelper.java
@@ -346,7 +346,13 @@ public class MetaClassHelper {
             Method sam;
             for (Class<?> c = ReflectionCache.autoboxType(argument); c != null && c != parameterClass; c = c.getSuperclass()) {
                 if (c == Closure.class && parameterClass.isInterface() && (sam = getSAMMethod(parameterClass)) != null) {
-                    if (getParameterCount(argument) == sam.getParameterCount()) objectDistance -= 1; // GROOVY-9881
+                    // In the case of multiple overloads, give preference to equal parameter count
+                    // with fuzzy matching of length for implicit arg Closures
+                    int paramCount = getParameterCount(argument);
+                    int samParamCount = sam.getParameterCount();
+                    if ((paramCount == samParamCount) ||                                   // GROOVY-9881
+                        (paramCount == -1 && (samParamCount == 0 || samParamCount == 1)))  // GROOVY-10905
+                        objectDistance -= 1;
                     objectDistance += 5; // ahead of Object but behind GroovyObjectSupport
                     break;
                 }
diff --git a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 1d37c913bb..e113687537 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -953,9 +953,15 @@ public abstract class StaticTypeCheckingSupport {
             if (receiver.implementsInterface(compare)) {
                 return dist + getMaximumInterfaceDistance(receiver, compare);
             } else if (receiver.equals(CLOSURE_TYPE) && (sam = findSAM(compare)) != null) {
-                // GROOVY-9881: in case of multiple overloads, give preference to equal parameter count
+                // In the case of multiple overloads, give preference to equal parameter count
+                // with fuzzy matching of length for implicit arg Closures
                 Integer closureParamCount = receiver.getNodeMetaData(StaticTypesMarker.CLOSURE_ARGUMENTS);
-                if (closureParamCount != null && closureParamCount == sam.getParameters().length) dist -= 1;
+                if (closureParamCount != null) {
+                    int samParamCount = sam.getParameters().length;
+                    if ((closureParamCount == samParamCount) ||                                  // GROOVY-9881
+                        (closureParamCount == -1 && (samParamCount == 0 || samParamCount == 1))) // GROOVY-10905
+                        dist -= 1;
+                }
 
                 return dist + 13; // GROOVY-9852: @FunctionalInterface vs Object
             }
diff --git a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
index 570697e5c6..53b5bbf549 100644
--- a/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ClosuresSTCTest.groovy
@@ -1047,4 +1047,26 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
         'Cannot assign value of type java.lang.Class<java.lang.Integer> to variable of type int',
         "named param 'bar' has type 'java.lang.Class<java.lang.Number>' but expected 'java.lang.Number'"
     }
+
+    // GROOVY-10905
+    void testImplicitArgClosureMatchesSamMethodWithOneArg() {
+        assertScript '''
+        def method1(java.util.function.IntUnaryOperator unary) { '1a' }
+        def method1(java.util.function.IntBinaryOperator binary) { '1b' }
+        assert method1{ x -> } == '1a'
+        assert method1{ x, y -> } == '1b'
+        assert method1{ } == '1a'
+        '''
+    }
+
+    // GROOVY-10905
+    void testImplicitArgClosureMatchesSamMethodWithZeroArgs() {
+        assertScript '''
+        def method2(java.util.function.IntSupplier supplier) { '2a' }
+        def method2(java.util.function.IntBinaryOperator binary) { '2b' }
+        assert method2{ -> } == '2a'
+        assert method2{ x, y -> } == '2b'
+        assert method2{ } == '2a'
+        '''
+    }
 }