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/08/17 19:47:58 UTC

[groovy] 01/01: GROOVY-8788: STC: prefer closer parameter match over self-type match

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

emilles pushed a commit to branch GROOVY-8788
in repository https://gitbox.apache.org/repos/asf/groovy.git

commit 488f8a8f336efae32349317f219a8f8df8994114
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Aug 17 14:47:33 2022 -0500

    GROOVY-8788: STC: prefer closer parameter match over self-type match
---
 .../groovy/runtime/DefaultGroovyMethods.java       | 27 ++++++++++++++++++++--
 .../transform/stc/StaticTypeCheckingSupport.java   | 24 ++++++++++++++-----
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  4 ++--
 .../groovy/transform/DelegateTransformTest.groovy  |  6 ++---
 4 files changed, 48 insertions(+), 13 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
index 6695419a25..2f7d71171c 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -8290,8 +8290,11 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
 
     /**
      * Support the subscript operator for a Map.
-     * <pre class="groovyTestCase">def map = [a:10]
-     * assert map["a"] == 10</pre>
+     * <pre class="groovyTestCase">
+     * def map = [:]
+     * map.put(1,10)
+     * assert map[1] == 10
+     * </pre>
      *
      * @param self a Map
      * @param key  an Object as a key for the map
@@ -8302,6 +8305,26 @@ public class DefaultGroovyMethods extends DefaultGroovyMethodsSupport {
         return self.get(key);
     }
 
+    /**
+     * Prevent {@link #getAt(Object,String)} for Map.
+     * <pre class="groovyTestCase">
+     * def map = [a:10]
+     * assert map["a"] == 10
+     *
+     * class HM extends HashMap {
+     *   String a = 'x'
+     * }
+     * map = new HM()
+     * map.put("a",1)
+     * assert map["a"] == 1 // not 'x'
+     * </pre>
+     *
+     * @since 5.0.0
+     */
+    public static <K,V> V getAt(Map<K,V> self, String key) {
+        return self.get(key);
+    }
+
     /**
      * Returns a new <code>Map</code> containing all entries from <code>left</code> and <code>right</code>,
      * giving precedence to <code>right</code>.  Any keys appearing in both Maps
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 4d4090fc82..de55e6992b 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1084,8 +1084,9 @@ public abstract class StaticTypeCheckingSupport {
             Parameter[] params = makeRawTypes(safeNode.getParameters(), spec);
             int dist = measureParametersAndArgumentsDistance(params, safeArgs);
             if (dist >= 0) {
-                dist += getClassDistance(declaringClass, actualReceiver);
-                dist += getExtensionDistance(isExtensionMethod);
+                if (!isExtensionMethod) { // GROOVY-6849, GROOVY-8788, et al.
+                    dist += (1 + getClassDistance(declaringClass, actualReceiver));
+                }
                 if (dist < bestDist) {
                     bestDist = dist;
                     bestChoices.clear();
@@ -1103,6 +1104,21 @@ public abstract class StaticTypeCheckingSupport {
                     onlyExtensionMethods.add(choice);
                 }
             }
+            if (onlyExtensionMethods.size() > 1) {
+                // GROOVY-8788: prefer closer parameter match over closer self-type match
+                bestDist = Integer.MAX_VALUE; List<MethodNode> bestExtensions = new LinkedList<>();
+                for (MethodNode extension : onlyExtensionMethods) { // exclude self-type from distance checking
+                    int dist = measureParametersAndArgumentsDistance(extension.getParameters(), argumentTypes);
+                    if (dist < bestDist) {
+                        bestDist = dist;
+                        bestExtensions.clear();
+                        bestExtensions.add(extension);
+                    } else if (dist == bestDist) {
+                        bestExtensions.add(extension);
+                    }
+                }
+                onlyExtensionMethods = bestExtensions;
+            }
             if (onlyExtensionMethods.size() == 1) {
                 return onlyExtensionMethods;
             }
@@ -1169,10 +1185,6 @@ public abstract class StaticTypeCheckingSupport {
         return getDistance(actualReceiverForDistance, declaringClassForDistance);
     }
 
-    private static int getExtensionDistance(final boolean isExtensionMethodNode) {
-        return isExtensionMethodNode ? 0 : 1;
-    }
-
     private static Parameter[] makeRawTypes(final Parameter[] parameters, final Map<GenericsType, GenericsType> genericsPlaceholderAndTypeMap) {
         return Arrays.stream(parameters).map(param -> {
             String name = param.getType().getUnresolvedName();
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index a22068beec..2595990a69 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2024,8 +2024,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 }
             }
         ''',
-        'Cannot find matching method java.util.Map#put(java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>). Please check if the declared type is correct and if the method exists.',
-        'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.Map<java.lang.String, java.util.Map<java.lang.String, java.util.List<java.lang.String>>>, java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>]'
+        'Cannot find matching method java.util.Map#put(java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>)',
+        'Cannot assign java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>> to: java.util.Map<java.lang.String, java.util.List<java.lang.String>>'
     }
 
     void testPutAtWithWrongValueType3() {
diff --git a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
index 7ad281e6ce..1e20c733f9 100644
--- a/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
+++ b/src/test/org/codehaus/groovy/transform/DelegateTransformTest.groovy
@@ -112,12 +112,12 @@ final class DelegateTransformTest {
 
             def ms = new MapSet()
             assert ms.size() == 1
-            assert ms.toString() == '{a=1} [2, 3, 4]'
+            assert ms.toString() == '[a:1] [2, 3, 4]'
             ms.remove(3)
             assert ms.size() == 1
-            assert ms.toString() == '{a=1} [2, 4]'
+            assert ms.toString() == '[a:1] [2, 4]'
             ms.clear()
-            assert ms.toString() == '{a=1} []'
+            assert ms.toString() == '[a:1] []'
         '''
     }