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/23 20:48:35 UTC

[groovy] branch master updated: GROOVY-8788: STC: prefer closer parameter match over receiver-type match

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

emilles 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 14946ff27a GROOVY-8788: STC: prefer closer parameter match over receiver-type match
14946ff27a is described below

commit 14946ff27a60d74b5d07c1b9a63f02624283c863
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sun Aug 21 19:36:17 2022 -0500

    GROOVY-8788: STC: prefer closer parameter match over receiver-type match
---
 .../groovy/runtime/DefaultGroovyMethods.java       |   4 +-
 .../transform/stc/StaticTypeCheckingSupport.java   | 106 +++++++-------
 .../transform/stc/StaticTypeCheckingVisitor.java   |  13 +-
 .../org.codehaus.groovy.runtime.ExtensionModule    |   2 +-
 src/test/groovy/bugs/Groovy8629Bug.groovy          |   5 +-
 .../stc/ArraysAndCollectionsSTCTest.groovy         |  12 +-
 .../stc/FieldsAndPropertiesSTCTest.groovy          |  76 +++++++---
 .../groovy/transform/stc/GenericsSTCTest.groovy    |  35 +++--
 .../groovy/transform/stc/STCAssignmentTest.groovy  |  15 +-
 .../transform/stc/STCExtensionMethodsTest.groovy   | 157 +++++++++++++++++++--
 .../transform/stc/TypeInferenceSTCTest.groovy      |  12 --
 .../sc/FieldsAndPropertiesStaticCompileTest.groovy |  12 +-
 .../classgen/asm/sc/bugs/Groovy7325Bug.groovy      |  41 +++---
 .../groovy/transform/DelegateTransformTest.groovy  |   6 +-
 14 files changed, 325 insertions(+), 171 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..4029823165 100644
--- a/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
+++ b/src/main/java/org/codehaus/groovy/runtime/DefaultGroovyMethods.java
@@ -8290,8 +8290,8 @@ 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 = [1:10]
+     * assert map[1] == 10</pre>
      *
      * @param self a Map
      * @param key  an Object as a key for the map
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 9e929b55b0..847f481294 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1031,47 +1031,38 @@ public abstract class StaticTypeCheckingSupport {
      *
      * @return zero or more results
      */
-    public static List<MethodNode> chooseBestMethod(final ClassNode receiver, final Collection<MethodNode> methods, final ClassNode... argumentTypes) {
+    public static List<MethodNode> chooseBestMethod(final ClassNode receiver, Collection<MethodNode> methods, final ClassNode... argumentTypes) {
         if (!asBoolean(methods)) {
             return Collections.emptyList();
         }
 
+        // GROOVY-8965: type disjunction
+        boolean duckType = receiver instanceof UnionTypeClassNode;
+        if (methods.size() > 1 && !methods.iterator().next().isConstructor())
+            methods = removeCovariantsAndInterfaceEquivalents(methods, duckType);
+
+        Set<MethodNode> bestMethods = new HashSet<>(); // choose best method(s) for each possible receiver
+        for (ClassNode rcvr : duckType ? ((UnionTypeClassNode) receiver).getDelegates() : new ClassNode[]{receiver}) {
+            bestMethods.addAll(chooseBestMethods(rcvr, methods, argumentTypes));
+        }
+        return new LinkedList<>(bestMethods); // assumes caller wants remove to be inexpensive
+    }
+
+    private static List<MethodNode> chooseBestMethods(final ClassNode receiver, Collection<MethodNode> methods, final ClassNode[] argumentTypes) {
         int bestDist = Integer.MAX_VALUE;
-        List<MethodNode> bestChoices = new LinkedList<>();
-        boolean duckType = receiver instanceof UnionTypeClassNode; // GROOVY-8965: type disjunction
-        boolean noCulling = methods.size() <= 1 || "<init>".equals(methods.iterator().next().getName());
-        Iterable<MethodNode> candidates = noCulling ? methods : removeCovariantsAndInterfaceEquivalents(methods, duckType);
-
-        for (MethodNode candidate : candidates) {
-            MethodNode safeNode = candidate;
-            ClassNode[] safeArgs = argumentTypes;
-            boolean isExtensionMethod = candidate instanceof ExtensionMethodNode;
-            if (isExtensionMethod) {
-                int nArgs = argumentTypes.length;
-                safeArgs = new ClassNode[nArgs + 1];
-                System.arraycopy(argumentTypes, 0, safeArgs, 1, nArgs);
-                safeArgs[0] = receiver; // prepend self-type as first argument
-                safeNode = ((ExtensionMethodNode) candidate).getExtensionMethodNode();
-            }
-
-            /* TODO: corner case
-                class B extends A {}
-                Animal foo(A a) {}
-                Person foo(B b) {}
-
-                B b = new B()
-                Person p = foo(b)
-            */
-
-            ClassNode declaringClass = candidate.getDeclaringClass();
+        List<MethodNode> bestMethods = new ArrayList<>();
+
+        // phase 1: argument-parameter distance classifier
+        for (MethodNode method : methods) {
+            ClassNode declaringClass = method.getDeclaringClass();
             ClassNode actualReceiver = receiver != null ? receiver : declaringClass;
 
             Map<GenericsType, GenericsType> spec;
-            if (candidate.isStatic()) {
+            if (method.isStatic()) {
                 spec = Collections.emptyMap(); // none visible
             } else {
                 spec = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(declaringClass, actualReceiver);
-                GenericsType[] methodGenerics = candidate.getGenericsTypes();
+                GenericsType[] methodGenerics = method.getGenericsTypes();
                 if (methodGenerics != null) { // GROOVY-10322: remove hidden type parameters
                     for (int i = 0, n = methodGenerics.length; i < n && !spec.isEmpty(); i += 1) {
                         for (Iterator<GenericsType> it = spec.keySet().iterator(); it.hasNext(); ) {
@@ -1080,34 +1071,49 @@ public abstract class StaticTypeCheckingSupport {
                     }
                 }
             }
+            Parameter[] parameters = makeRawTypes(method.getParameters(), spec);
 
-            Parameter[] params = makeRawTypes(safeNode.getParameters(), spec);
-            int dist = measureParametersAndArgumentsDistance(params, safeArgs);
-            if (dist >= 0) {
-                dist += getClassDistance(declaringClass, actualReceiver);
-                dist += getExtensionDistance(isExtensionMethod);
+            int dist = measureParametersAndArgumentsDistance(parameters, argumentTypes);
+            if (dist >= 0 && dist <= bestDist) {
                 if (dist < bestDist) {
                     bestDist = dist;
-                    bestChoices.clear();
-                    bestChoices.add(candidate);
-                } else if (dist == bestDist) {
-                    bestChoices.add(candidate);
+                    bestMethods.clear();
                 }
+                bestMethods.add(method);
             }
         }
-        if (bestChoices.size() > 1 && !duckType) {
-            // GROOVY-6849: prefer extension method in case of ambiguity
-            List<MethodNode> onlyExtensionMethods = new LinkedList<>();
-            for (MethodNode choice : bestChoices) {
-                if (choice instanceof ExtensionMethodNode) {
-                    onlyExtensionMethods.add(choice);
+
+        // phase 2: receiver-provider distance classifier
+        if (bestMethods.size() > 1 && receiver != null) {
+            methods = bestMethods;
+            bestDist = Integer.MAX_VALUE;
+            bestMethods = new ArrayList<>();
+            for (MethodNode method : methods) {
+                int dist = getClassDistance(method.getDeclaringClass(), receiver);
+                if (dist <= bestDist) {
+                    if (dist < bestDist) {
+                        bestDist = dist;
+                        bestMethods.clear();
+                    }
+                    bestMethods.add(method);
                 }
             }
-            if (onlyExtensionMethods.size() == 1) {
-                return onlyExtensionMethods;
+        }
+
+        // phase 3: prefer extension method in case of tie
+        if (bestMethods.size() > 1) {
+            List<MethodNode> extensionMethods = new ArrayList<>();
+            for (MethodNode method : bestMethods) {
+                if (method instanceof ExtensionMethodNode) {
+                    extensionMethods.add(method);
+                }
+            }
+            if (extensionMethods.size() == 1) {
+                bestMethods = extensionMethods;
             }
         }
-        return bestChoices;
+
+        return bestMethods;
     }
 
     private static int measureParametersAndArgumentsDistance(final Parameter[] parameters, final ClassNode[] argumentTypes) {
@@ -1168,10 +1174,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/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index 35efb8b896..d791fddc1c 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -827,11 +827,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
                         enclosingExpressionRHS.visit(this);
                     }
                     ClassNode[] arguments = {rType, getType(enclosingExpressionRHS)};
-                    List<MethodNode> nodes = findMethod(lType.redirect(), "putAt", arguments);
-                    if (nodes.size() == 1) {
-                        typeCheckMethodsWithGenericsOrFail(lType, arguments, nodes.get(0), enclosingExpressionRHS);
-                    } else if (nodes.isEmpty()) {
+                    List<MethodNode> methods = findMethod(lType, "putAt", arguments);
+                    if (methods.isEmpty()) {
                         addNoMatchingMethodError(lType, "putAt", arguments, enclosingBinaryExpression);
+                    } else if (methods.size() == 1) {
+                        typeCheckMethodsWithGenericsOrFail(lType, arguments, methods.get(0), enclosingExpressionRHS);
                     }
                 }
             }
@@ -4520,6 +4520,11 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
         }
 
         if (isArrayOp(op)) {
+            if (isOrImplements(left, MAP_TYPE) && isStringType(right)) { // GROOVY-5700, GROOVY-8788
+                PropertyExpression prop = propX(leftExpression, rightExpression); // m['xx'] -> m.xx
+                return existsProperty(prop, !typeCheckingContext.isTargetOfEnclosingAssignment(expr))
+                            ? getType(prop) : getTypeForMapPropertyExpression(left, prop);
+            }
             Expression copy = binX(leftExpression, expr.getOperation(), rightExpression);
             copy.setSourcePosition(expr); // do not propagate BINARY_EXP_TARGET, etc.
             MethodNode method = findMethodOrFail(copy, left, "getAt", rightRedirect);
diff --git a/src/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule b/src/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule
index 4272f3f83b..93aff1ffec 100644
--- a/src/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule
+++ b/src/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule
@@ -21,5 +21,5 @@
 // TODO remove dup with src/spec/test-resources/META-INF/groovy/org.codehaus.groovy.runtime.ExtensionModule
 moduleName=Test module
 moduleVersion=1.0-test
-extensionClasses=support.MaxRetriesExtension,org.codehaus.groovy.runtime.m12n.TestStringExtension,org.codehaus.groovy.runtime.m12n.Groovy6496Extension,org.codehaus.groovy.runtime.m12n.TestPrimitiveWrapperExtension,org.codehaus.groovy.runtime.m12n.TestLocalDateTimeExtension
+extensionClasses=groovy.transform.stc.STCExtensionMethodsTest$Groovy8788,support.MaxRetriesExtension,org.codehaus.groovy.runtime.m12n.TestStringExtension,org.codehaus.groovy.runtime.m12n.Groovy6496Extension,org.codehaus.groovy.runtime.m12n.TestPrimitiveWrapperExtension,org.codehaus.groovy.runtime.m12n.TestLocalDateTimeExtension
 staticExtensionClasses=support.StaticStringExtension,org.codehaus.groovy.runtime.m12n.TestStaticStringExtension
diff --git a/src/test/groovy/bugs/Groovy8629Bug.groovy b/src/test/groovy/bugs/Groovy8629Bug.groovy
index 3b56d81bc1..643cca4219 100644
--- a/src/test/groovy/bugs/Groovy8629Bug.groovy
+++ b/src/test/groovy/bugs/Groovy8629Bug.groovy
@@ -62,8 +62,8 @@ public class Groovy8629Bug extends GroovyTestCase {
                 @Override
                 IntegerPair next() {
                     String key = keyIterator.next()
-                    IntegerPair comp = new IntegerPair(m1[key], m2[key])
-                    return comp
+                    IntegerPair pair = new IntegerPair(m1.get(key), m2.get(key))
+                    return pair
                 }
 
                 @Override
@@ -87,5 +87,4 @@ public class Groovy8629Bug extends GroovyTestCase {
 
         '''
     }
-
 }
diff --git a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index c39f0df451..d80b4559ad 100644
--- a/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -803,14 +803,12 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-6131
-    void testArraySetShouldGenerateBytecode() {
+    void testCollectionPutAt() {
         shouldFailWithMessages '''
-            void addToCollection(Collection coll, int index, val) {
-                coll[index] = val
+            void addToCollection(Collection coll, int index, value) {
+                coll[index] = value
             }
-            def list = ['a']
-            addToCollection(list, 0, 'b')
-            assert list == ['b']
+            addToCollection(['a'], 0, 'b')
         ''',
         'Cannot find matching method java.util.Collection#putAt(int, java.lang.Object)'
     }
@@ -818,7 +816,7 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
     // GROOVY-6266
     void testMapKeyGenerics() {
         assertScript '''
-            HashMap<String,List<List>> map = new HashMap<String,List<List>>()
+            Map<String,List<List>> map = new HashMap<String,List<List>>()
             map.get('key',[['val1'],['val2']])
             assert map.'key'[0] == ['val1']
         '''
diff --git a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
index ed471189ce..4f8c904d36 100644
--- a/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
+++ b/src/test/groovy/transform/stc/FieldsAndPropertiesSTCTest.groovy
@@ -133,16 +133,6 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         'Cannot find matching method C#setX(<unknown parameter type>).'
     }
 
-    void testMapDotPropertySyntax() {
-        assertScript '''
-            HashMap map = [:]
-            map['a'] = 1
-            map.b = 2
-            assert map.get('a') == 1
-            assert map.get('b') == 2
-        '''
-    }
-
     void testInferenceFromFieldType() {
         assertScript '''
             class A {
@@ -576,33 +566,73 @@ class FieldsAndPropertiesSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    // GROOVY-5700
-    void testInferenceOfMapDotProperty() {
+    // GROOVY-5001, GROOVY-5491, GROOVY-6144, GROOVY-8788
+    void testReadMapProperty() {
+        assertScript '''
+            class A { }
+            class B { }
+            class HM extends HashMap<String,A> {
+                B b = new B()
+            }
+
+            def map = new HM()
+            map.put('a', new A())
+            assert map.get('a') != null
+            assert map.get('b') == null
+
+            A a = map.a
+            B b = map.b
+            a = map['a']
+            b = map['b']
+            assert a instanceof A
+            assert b instanceof B
+        '''
+    }
+
+    void testWriteMapProperty() {
+        assertScript '''
+            def map = [:]
+            map['a'] = 1
+            map.b = 2
+            assert map.get('a') == 1
+            assert map.get('b') == 2
+        '''
+    }
+
+    // GROOVY-5700, GROOVY-8788
+    void testInferenceOfMapSubProperty() {
         assertScript '''
-            def m = [retries: 10]
+            def map = [key: 123]
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
             })
-            def r1 = m['retries']
+            def val = map['key']
+            assert val == 123
+        '''
+    }
 
+    // GROOVY-5700
+    void testInferenceOfMapDotProperty() {
+        assertScript '''
+            def map = [key: 123]
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
             })
-            def r2 = m.retries
+            def val = map.key
+            assert val == 123
         '''
     }
 
     void testInferenceOfListDotProperty() {
-        assertScript '''class Foo { int x }
-            def list = [new Foo(x:1), new Foo(x:2)]
+        assertScript '''
+            class C { int x }
+            def list = [new C(x:1), new C(x:2)]
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
-                def iType = node.getNodeMetaData(INFERRED_TYPE)
-                assert iType == make(List)
-                assert iType.isUsingGenerics()
-                assert iType.genericsTypes[0].type == Integer_TYPE
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.toString(false) == 'java.util.List<java.lang.Integer>'
             })
-            def r2 = list.x
-            assert r2 == [ 1,2 ]
+            def x = list.x
+            assert x == [1,2]
         '''
     }
 
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index d6c7788f1c..9727aa89e5 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -2004,18 +2004,26 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     void testPutAtWithWrongValueType() {
+        shouldFailWithMessages '''
+            def map = new HashMap<Integer, Integer>()
+            map[123] = new Object()
+        ''',
+        'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.lang.Integer, java.lang.Integer>, int, java.lang.Object]'
+    }
+
+    // GROOVY-8788
+    void testPutAtWithWrongValueType2() {
         shouldFailWithMessages '''
             def map = new HashMap<String, Integer>()
-            map['hello'] = new Object()
+            map['x'] = new Object()
         ''',
-        'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.lang.String, java.lang.Integer>, java.lang.String, java.lang.Object]'
+        'Cannot assign value of type java.lang.Object to variable of type java.lang.Integer'
     }
 
     // GROOVY-9069
-    void testPutAtWithWrongValueType2() {
+    void testPutAtWithWrongValueType3() {
         shouldFailWithMessages '''
-            class ConfigAttribute {
-            }
+            class ConfigAttribute { }
             void test(Map<String, Map<String, List<String>>> maps) {
                 maps.each { String key, Map<String, List<String>> map ->
                     Map<String, List<ConfigAttribute>> value = [:]
@@ -2024,18 +2032,15 @@ 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() {
         assertScript '''
             void test(Map<String, Map<String, List<String>>> maps) {
                 maps.each { String key, Map<String, List<String>> map ->
                     Map<String, List<String>> value = [:]
-                    // populate value
-                    maps[key] = value
                     maps.put(key, value)
+                    maps[key] = value
                 }
             }
         '''
@@ -2802,9 +2807,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 assert type.genericsTypes[1].type == DATE
             })
             Map<Date, Date> map = new HashMap<>()
-            map.put('foo', new Date())
+            map.put(123, new Date())
         ''',
-        'Cannot find matching method java.util.HashMap#put(java.lang.String, java.util.Date). Please check if the declared type is correct and if the method exists.'
+        'Cannot find matching method java.util.HashMap#put(int, java.util.Date). Please check if the declared type is correct and if the method exists.'
     }
     void testInferDiamondForAssignmentWithDatesAndIllegalKeyUsingSquareBracket() {
         shouldFailWithMessages '''
@@ -2822,9 +2827,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 assert type.genericsTypes[1].type == DATE
             })
             Map<Date, Date> map = new HashMap<>()
-            map['foo'] = new Date()
+            map[123] = new Date()
         ''',
-        'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.util.Date, java.util.Date>, java.lang.String, java.util.Date]'
+        'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.util.Date, java.util.Date>, int, java.util.Date]'
     }
     void testInferDiamondForAssignmentWithDatesAndIllegalValueUsingPut() {
         shouldFailWithMessages '''
diff --git a/src/test/groovy/transform/stc/STCAssignmentTest.groovy b/src/test/groovy/transform/stc/STCAssignmentTest.groovy
index c573582bcc..9df4022ee1 100644
--- a/src/test/groovy/transform/stc/STCAssignmentTest.groovy
+++ b/src/test/groovy/transform/stc/STCAssignmentTest.groovy
@@ -1241,21 +1241,14 @@ class STCAssignmentTest extends StaticTypeCheckingTestCase {
                 void setProperty(String name, value) {
                     if (value instanceof File) {
                         value = new File(value, 'bar.txt')
-                    }
-                    else if (value instanceof URL) {
+                    } else if (value instanceof URL) {
                         value = value.toURI()
-                    }
-                    else if (value instanceof InputStream) {
+                    } else if (value instanceof InputStream) {
                         value = new BufferedInputStream(value)
-                    }
-                    else if (value instanceof GString) {
+                    } else if (value instanceof GString) {
                         value = value.toString()
                     }
-                    if (mvm[name]) {
-                        mvm[name].add value
-                    } else {
-                        mvm.put(name, [value])
-                    }
+                    mvm.computeIfAbsent(name, k -> []).add(value)
                 }
             }
             new M().setProperty('foo', 'bar')
diff --git a/src/test/groovy/transform/stc/STCExtensionMethodsTest.groovy b/src/test/groovy/transform/stc/STCExtensionMethodsTest.groovy
index fa538aa799..3254e09df1 100644
--- a/src/test/groovy/transform/stc/STCExtensionMethodsTest.groovy
+++ b/src/test/groovy/transform/stc/STCExtensionMethodsTest.groovy
@@ -25,33 +25,29 @@ import static org.codehaus.groovy.runtime.m12n.ExtensionModuleHelperForTests.doI
  */
 class STCExtensionMethodsTest extends StaticTypeCheckingTestCase {
 
+    /**
+     * @see org.codehaus.groovy.runtime.m12n.TestStaticStringExtension
+     */
     void testStaticExtensionMethod() {
         assertScript '''
             assert String.answer() == 42
         '''
     }
 
+    /**
+     * @see org.codehaus.groovy.runtime.m12n.TestStringExtension
+     */
     void testNonStaticExtensionMethod() {
         assertScript '''
             def str = 'This is a string'
-            // reverseToUpperCase is a usnit test extension method
             assert str.reverseToUpperCase() == str.toUpperCase().reverse()
         '''
     }
 
-    // GROOVY-7953
-    void testExtensionPropertyWithPrimitiveReceiver() {
-        assertScript '''
-            assert 4.even
-        '''
-    }
-
-    void testExtensionMethodWithGenericsAndPrimitiveReceiver() {
-        assertScript '''
-            assert 2d.groovy6496(2d) == 2d
-        '''
-    }
-
+    /**
+     * @see org.codehaus.groovy.runtime.m12n.TestStringExtension
+     * @see org.codehaus.groovy.runtime.m12n.TestStaticStringExtension
+     */
     void testShouldFindExtensionMethodWithGrab() {
         doInFork 'groovy.transform.stc.StaticTypeCheckingTestCase', '''
             def impl = new MetaClassImpl(String)
@@ -79,4 +75,137 @@ class STCExtensionMethodsTest extends StaticTypeCheckingTestCase {
             """
         '''
     }
+
+    /**
+     * GROOVY-7953
+     *
+     * @see org.codehaus.groovy.runtime.m12n.TestPrimitiveWrapperExtension
+     */
+    void testExtensionPropertyWithPrimitiveReceiver() {
+        assertScript '''
+            assert 4.even
+            assert 4.isEven()
+            assert !5.isEven()
+        '''
+    }
+
+    /**
+     * GROOVY-6496
+     *
+     * @see org.codehaus.groovy.runtime.m12n.Groovy6496Extension
+     */
+    void testExtensionMethodWithGenericsAndPrimitiveReceiver() {
+        assertScript '''
+            assert 2d.groovy6496(2d) == 2d
+        '''
+    }
+
+    //--------------------------------------------------------------------------
+
+    static class Groovy8788 {
+        static class A {
+            def m1(Object o) {1}
+            def m2(String s) {1}
+            def m4(String s) {4}
+            def m5(String s) {4}
+            def m6(String s) {4}
+        }
+        static class B extends A {
+            def m1(String s) {2}
+            def m2(Object o) {2}
+        }
+        static class C extends B {
+        }
+
+        static m3(A self, String s) {1}
+        static m3(B self, Object o) {2}
+        static m3(B self, String s) {3}
+
+        static m4(A self, String s) {1}
+        static m4(B self, Object o) {2}
+
+        static m5(A self, String s) {1}
+        static m5(B self, Object o) {2}
+
+        static m6(B self, Object o) {2}
+    }
+
+    // GROOVY-8788
+    void testMethodSelection1() {
+        assertScript """import ${Groovy8788.name}.*
+            def a = new A()
+            assert a.m1(new Object()) == 1
+            assert a.m1(new String()) == 1
+            def b = new B()
+            assert b.m1(new Object()) == 1
+            assert b.m1(new String()) == 2
+        """
+    }
+
+    // GROOVY-8788
+    void testMethodSelection2() {
+        assertScript """import ${Groovy8788.name}.*
+            def a = new A()
+            assert a.m2(new String()) == 1
+            def b = new B()
+            assert b.m2(new Object()) == 2
+            assert b.m2(new String()) == 1
+        """
+
+        shouldFailWithMessages """import ${Groovy8788.name}.*
+            def a = new A()
+            a.m2(new Object())
+        """,
+        'Cannot find matching method','A#m2(java.lang.Object)'
+    }
+
+    // GROOVY-8788
+    void testMethodSelection3() {
+        assertScript """import ${Groovy8788.name}.*
+            def a = new A()
+            assert a.m3(new String()) == 1
+            def b = new B()
+            assert b.m3(new Object()) == 2
+            assert b.m3(new String()) == 3
+        """
+    }
+
+    // GROOVY-8788
+    void testMethodSelection4() {
+        assertScript """import ${Groovy8788.name}.*
+            def a = new A()
+            assert a.m4(new String()) == 1
+            def b = new B()
+            assert b.m4(new Object()) == 2
+            assert b.m4(new String()) == 1
+        """
+    }
+
+    // GROOVY-8788
+    void testMethodSelection5() {
+        assertScript """import ${Groovy8788.name}.*
+            def a = new A()
+            assert a.m5(new String()) == 1
+            def b = new B()
+            assert b.m5(new Object()) == 2
+            assert b.m5(new String()) == 1
+            def c = new C()
+            assert c.m5(new Object()) == 2
+            assert c.m5(new String()) == 1
+        """
+    }
+
+    // GROOVY-8788
+    void testMethodSelection6() {
+        assertScript """import ${Groovy8788.name}.*
+            def a = new A()
+            assert a.m6(new String()) == 4
+            def b = new B()
+            assert b.m6(new Object()) == 2
+            assert b.m6(new String()) == 4
+            def c = new C()
+            assert c.m6(new Object()) == 2
+            assert c.m6(new String()) == 4
+        """
+    }
 }
diff --git a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
index af7526fcb9..ef48abf961 100644
--- a/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -1018,18 +1018,6 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testInferMapValueType() {
-        assertScript '''
-            Map<String, Integer> map = new HashMap<String,Integer>()
-            map['foo'] = 123
-            map['bar'] = 246
-            Integer foo = map['foo']
-            assert foo == 123
-            Integer bar = map.get('bar')
-            assert bar == 246
-        '''
-    }
-
     // GROOVY-5522
     void testTypeInferenceWithArrayAndFind() {
         assertScript '''
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
index af1dc40472..b15d7890cd 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/FieldsAndPropertiesStaticCompileTest.groovy
@@ -18,6 +18,7 @@
  */
 package org.codehaus.groovy.classgen.asm.sc
 
+import groovy.test.NotYetImplemented
 import groovy.transform.stc.FieldsAndPropertiesSTCTest
 
 final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCTest implements StaticCompilationTestSupport {
@@ -31,16 +32,21 @@ final class FieldsAndPropertiesStaticCompileTest extends FieldsAndPropertiesSTCT
         '''
     }
 
-    void testGetAtFromStaticMap() {
+    void testStaticMapGetAt() {
         assertScript '''
             class Foo {
-                public static Map CLASSES = [:]
+                public static Map CLASSES = [key:'value']
             }
             String foo = 'key'
-            Foo.CLASSES[foo]
+            assert Foo.CLASSES[foo] == 'value'
         '''
     }
 
+    @Override @NotYetImplemented
+    void testReadMapProperty() {
+        super.testReadMapProperty() // Access to HM#a is forbidden
+    }
+
     // GROOVY-5561
     void testShouldNotThrowAccessForbidden() {
         assertScript '''
diff --git a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7325Bug.groovy b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7325Bug.groovy
index 49ea6ed365..d12cdd665a 100644
--- a/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7325Bug.groovy
+++ b/src/test/org/codehaus/groovy/classgen/asm/sc/bugs/Groovy7325Bug.groovy
@@ -16,43 +16,42 @@
  *  specific language governing permissions and limitations
  *  under the License.
  */
-
-
 package org.codehaus.groovy.classgen.asm.sc.bugs
 
 import groovy.transform.stc.StaticTypeCheckingTestCase
 import org.codehaus.groovy.classgen.asm.sc.StaticCompilationTestSupport
 
-class Groovy7325Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
+final class Groovy7325Bug extends StaticTypeCheckingTestCase implements StaticCompilationTestSupport {
 
     void testGenericIdentityWithClosure() {
         assertScript '''
-public static <T> T identity(T self) { self }
-
-@ASTTest(phase=INSTRUCTION_SELECTION,value={
-    assert node.rightExpression.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
-})
-Integer i = identity(2)
-
-@ASTTest(phase=INSTRUCTION_SELECTION,value={
-    assert node.rightExpression.getNodeMetaData(INFERRED_TYPE) == CLOSURE_TYPE
-})
-Closure c = identity {'foo'}
-'''
+            static <T> T itself(T self) { self }
+
+            @ASTTest(phase=INSTRUCTION_SELECTION,value={
+                assert node.rightExpression.getNodeMetaData(INFERRED_TYPE) == Integer_TYPE
+            })
+            Integer i = itself(2)
+
+            @ASTTest(phase=INSTRUCTION_SELECTION,value={
+                assert node.rightExpression.getNodeMetaData(INFERRED_TYPE) == CLOSURE_TYPE
+            })
+            Closure c = itself {'foo'}
+        '''
     }
 
     void testShouldNotThrowIllegalAccessToProtectedData() {
-        shouldFailWithMessages('''
+        shouldFailWithMessages '''
             class Test {
-              final Set<String> HISTORY = [] as HashSet
+                final Set<String> HISTORY = [] as HashSet
 
-              Set<String> getHistory() {
-                return HISTORY.clone() as HashSet<String>
-              }
+                Set<String> getHistory() {
+                    return HISTORY.clone() as HashSet<String>
+                }
             }
 
             Test test = new Test()
             println test.history
-        ''', 'Method clone is protected in java.lang.Object')
+        ''',
+        'Method clone is protected in java.lang.Object'
     }
 }
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] []'
         '''
     }