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