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 2024/01/12 20:42:56 UTC

(groovy) branch GROOVY_4_0_X updated: GROOVY-11276: STC: drop type args after applying generics connections

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

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


The following commit(s) were added to refs/heads/GROOVY_4_0_X by this push:
     new c3f55d96cb GROOVY-11276: STC: drop type args after applying generics connections
c3f55d96cb is described below

commit c3f55d96cb23213a2d532a709c66471c5a021d15
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Jan 12 13:19:25 2024 -0600

    GROOVY-11276: STC: drop type args after applying generics connections
    
    GROOVY-5482, GROOVY-8609
---
 .../codehaus/groovy/ast/tools/GenericsUtils.java   |  3 +-
 .../transform/stc/StaticTypeCheckingSupport.java   | 83 +++++++++-------------
 src/test/groovy/bugs/Groovy8609Bug.groovy          |  6 +-
 src/test/groovy/transform/stc/BugsSTCTest.groovy   | 25 +++----
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 45 +++++++-----
 .../groovy/transform/stc/MethodCallsSTCTest.groovy |  2 +-
 6 files changed, 75 insertions(+), 89 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
index 0192d3510d..c094dda188 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/GenericsUtils.java
@@ -303,8 +303,7 @@ public class GenericsUtils {
         }
         if (temp instanceof DecompiledClassNode // GROOVY-10461: check without resolving supers
                         ? ((DecompiledClassNode) temp).isParameterized() : temp.isUsingGenerics()) {
-            ClassNode result = ClassHelper.makeWithoutCaching(temp.getName());
-            result.setRedirect(temp);
+            ClassNode result = temp.getPlainNodeReference();
             result.setGenericsTypes(null);
             result.setUsingGenerics(false);
             while (dims > 0) { dims -= 1;
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 b875193285..63b986797f 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -60,7 +60,6 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.HashMap;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.LinkedList;
@@ -1051,7 +1050,7 @@ public abstract class StaticTypeCheckingSupport {
         Iterable<MethodNode> candidates = noCulling ? methods : removeCovariantsAndInterfaceEquivalents(methods, duckType);
 
         for (MethodNode candidate : candidates) {
-            MethodNode safeNode = candidate;
+            MethodNode  safeNode = candidate;
             ClassNode[] safeArgs = argumentTypes;
             boolean isExtensionMethod = candidate instanceof ExtensionMethodNode;
             if (isExtensionMethod) {
@@ -1062,35 +1061,11 @@ public abstract class StaticTypeCheckingSupport {
                 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();
             ClassNode actualReceiver = receiver != null ? receiver : declaringClass;
+            Parameter[] simpleParams = getSafeParameters(safeNode, declaringClass, actualReceiver);
 
-            Map<GenericsType, GenericsType> spec;
-            if (candidate.isStatic()) {
-                spec = Collections.emptyMap(); // none visible
-            } else {
-                spec = GenericsUtils.makeDeclaringAndActualGenericsTypeMapOfExactType(declaringClass, actualReceiver);
-                GenericsType[] methodGenerics = candidate.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(); ) {
-                            if (it.next().getName().equals(methodGenerics[i].getName())) it.remove();
-                        }
-                    }
-                }
-            }
-
-            Parameter[] params = makeRawTypes(safeNode.getParameters(), spec);
-            int dist = measureParametersAndArgumentsDistance(params, safeArgs);
+            int dist = measureParametersAndArgumentsDistance(simpleParams, safeArgs);
             if (dist >= 0) {
                 dist += getClassDistance(declaringClass, actualReceiver);
                 dist += getExtensionDistance(isExtensionMethod);
@@ -1118,6 +1093,37 @@ public abstract class StaticTypeCheckingSupport {
         return bestChoices;
     }
 
+    private static Parameter[] getSafeParameters(final MethodNode methodNode, final ClassNode declaringClass, final ClassNode actualReceiver) {
+        Parameter[] params = methodNode.getParameters();
+        if (params.length > 0) {
+            Map<GenericsTypeName, GenericsType> spec;
+            if (methodNode.isStatic()) {
+                spec = Collections.emptyMap(); // not visible
+            } else {
+                extractGenericsConnections(spec = new HashMap<>(), actualReceiver, declaringClass);
+
+                GenericsType[] methodGenerics = methodNode.getGenericsTypes();
+                if (methodGenerics != null) { // GROOVY-10322: remove hidden type parameters
+                    for (GenericsType tp : methodGenerics) spec.remove(new GenericsTypeName(tp.getName()));
+                }
+            }
+
+            params = params.clone();
+
+            for (int i = 0; i < params.length; i += 1) {
+                ClassNode t = params[i].getOriginType(), x=t;
+                while (x.isArray()) x = x.getComponentType();
+                if (x.isGenericsPlaceHolder()) {
+                    // GROOVY-7204, GROOVY-8059, GROOVY-8609, GROOVY-10820, GROOVY-11276: "T" to "Foo" (via spec) or erasure
+                    params[i] = new Parameter(GenericsUtils.nonGeneric(applyGenericsContext(spec, t)), params[i].getName());
+                } else if (x.getGenericsTypes() != null) { // reduce "List<Type>" to just "List"
+                    params[i] = new Parameter(GenericsUtils.nonGeneric(t), params[i].getName());
+                }
+            }
+        }
+        return params;
+    }
+
     private static int measureParametersAndArgumentsDistance(final Parameter[] parameters, final ClassNode[] argumentTypes) {
         int dist = -1;
         if (parameters.length == argumentTypes.length) {
@@ -1180,27 +1186,6 @@ public abstract class StaticTypeCheckingSupport {
         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();
-            Optional<GenericsType> value = genericsPlaceholderAndTypeMap.entrySet().stream()
-                .filter(e -> e.getKey().getName().equals(name)).findFirst().map(Map.Entry::getValue);
-            ClassNode type = value.map(gt -> !gt.isPlaceholder() ? gt.getType() : makeRawType(gt.getType())).orElseGet(() -> makeRawType(param.getType()));
-
-            return new Parameter(type, param.getName());
-        }).toArray(Parameter[]::new);
-    }
-
-    private static ClassNode makeRawType(final ClassNode receiver) {
-        if (receiver.isArray()) {
-            return makeRawType(receiver.getComponentType()).makeArray();
-        }
-        ClassNode raw = receiver.getPlainNodeReference();
-        raw.setUsingGenerics(false);
-        raw.setGenericsTypes(null);
-        return raw;
-    }
-
     private static List<MethodNode> removeCovariantsAndInterfaceEquivalents(final Collection<MethodNode> collection, final boolean disjoint) {
         List<MethodNode> list = new ArrayList<>(new LinkedHashSet<>(collection)), toBeRemoved = new ArrayList<>();
         for (int i = 0, n = list.size(); i < n - 1; i += 1) {
diff --git a/src/test/groovy/bugs/Groovy8609Bug.groovy b/src/test/groovy/bugs/Groovy8609Bug.groovy
index 28e6543b5b..28bb9232eb 100644
--- a/src/test/groovy/bugs/Groovy8609Bug.groovy
+++ b/src/test/groovy/bugs/Groovy8609Bug.groovy
@@ -98,7 +98,7 @@ final class Groovy8609Bug extends GroovyTestCase {
                 }
             }
         '''
-        assert err.contains('Cannot find matching method A#getFirstRecord(java.util.ArrayList<java.util.TreeMap<java.lang.String, java.lang.Integer>>)')
+        assert err.contains('Cannot call A#getFirstRecord(java.util.ArrayList<java.util.HashMap<java.lang.String, java.lang.Integer>>) with arguments [java.util.ArrayList<java.util.TreeMap<java.lang.String, java.lang.Integer>>]')
     }
 
     void testUpperBoundWithGenericsThroughWrongType2() {
@@ -118,7 +118,7 @@ final class Groovy8609Bug extends GroovyTestCase {
                 }
             }
         '''
-        assert err.contains('Cannot find matching method A#getFirstRecord(java.util.ArrayList<java.util.HashMap<java.lang.String, java.lang.Long>>)')
+        assert err.contains('Cannot call A#getFirstRecord(java.util.ArrayList<java.util.HashMap<java.lang.String, java.lang.Integer>>) with arguments [java.util.ArrayList<java.util.HashMap<java.lang.String, java.lang.Long>>]')
     }
 
     void testUpperBoundWithGenericsThroughWrongType3() {
@@ -138,6 +138,6 @@ final class Groovy8609Bug extends GroovyTestCase {
                 }
             }
         '''
-        assert err.contains('Cannot find matching method A#getFirstRecord(java.util.ArrayList<java.util.HashMap<java.lang.StringBuffer, java.lang.Integer>>)')
+        assert err.contains('Cannot call A#getFirstRecord(java.util.ArrayList<java.util.HashMap<java.lang.String, java.lang.Integer>>) with arguments [java.util.ArrayList<java.util.HashMap<java.lang.StringBuffer, java.lang.Integer>>]')
     }
 }
diff --git a/src/test/groovy/transform/stc/BugsSTCTest.groovy b/src/test/groovy/transform/stc/BugsSTCTest.groovy
index 82f9895e80..41ded10f05 100644
--- a/src/test/groovy/transform/stc/BugsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/BugsSTCTest.groovy
@@ -208,22 +208,15 @@ class BugsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     void testGroovy5482ListsAndFlowTyping() {
-        assertScript '''
-        class StaticGroovy2 {
-            def bar() {
-
-                def foo = [new Date(), 1, new C()]
-                foo.add( 2 ) // Compiles
-                foo.add( new Date() )
-                foo.add( new C() )
-
-                foo = [new Date(), 1]
-                foo.add( 2 ) // Does not compile
-            }
-        }
-        class C{
-        }
-        new StaticGroovy2()'''
+        assertScript '''class C {}
+            def foo = [new Date(), 1, new C()]
+            foo.add(new Date())
+            foo.add(new C())
+            foo.add(2)
+
+            foo = [new Date(), 1]
+            foo.add(2) // STC err
+        '''
     }
 
     void testClosureThisObjectDelegateOwnerProperty() {
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 1eb3b788d2..341c1cdf15 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -885,16 +885,13 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         assertScript '''
             interface I<U> extends groovy.transform.stc.Groovy11257<U> {}
             def <V> I<V> forge(V v) { return new I(){} }
-            void test() {
-                @ASTTest(phase=INSTRUCTION_SELECTION, value={
-                    def type = node.getNodeMetaData(INFERRED_TYPE)
-                    assert type.toString(false) == 'java.util.Optional<java.math.BigInteger>'
-                })
-                def opt = forge(1G).thing
-                def num = opt.orElse(42G).intValue()
-            }
 
-            test()
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                def type = node.getNodeMetaData(INFERRED_TYPE)
+                assert type.toString(false) == 'java.util.Optional<java.math.BigInteger>'
+            })
+            def opt = forge(1G).thing
+            def num = opt.orElse(42G).intValue()
         '''
     }
 
@@ -2302,9 +2299,15 @@ 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]'
+
         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]'
     }
@@ -2322,18 +2325,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>>)',
+        'Cannot call java.util.Map#put(java.lang.String, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) with arguments [java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>]',
         '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>>]'
-    }
 
-    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
                 }
             }
         '''
@@ -2520,6 +2520,15 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         }
     }
 
+    // GROOVY-11276
+    void testShouldUseMethodGenericType4a() {
+        assertScript '''
+            def map = new HashMap<String,List<String>>()
+            map.put('foo', Collections.emptyList())
+            map.put('bar', [])
+        '''
+    }
+
     // GROOVY-9751
     void testShouldUseMethodGenericType5() {
         assertScript '''
@@ -4612,7 +4621,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 assert method.parameters[0].type == make(Collection)
             })
             def result = ['a','bb','ccc'].inject(0) { int acc, String str -> acc += str.length(); acc }
-            assert  result == 6
+            assert result == 6
         '''
     }
 
@@ -4624,8 +4633,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
                 assert method.name == 'inject'
                 assert method.parameters[0].type == make(Collection)
             })
-            def result = DefaultGroovyMethods.inject(['a','bb','ccc'],0, { int acc, String str -> acc += str.length(); acc })
-            assert  result == 6
+            def result = DefaultGroovyMethods.inject(['a','bb','ccc'], 0, { int acc, String str -> acc += str.length(); acc })
+            assert result == 6
         '''
     }
 
diff --git a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
index 907dcc4c6f..f4dd96cf9a 100644
--- a/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/MethodCallsSTCTest.groovy
@@ -209,7 +209,7 @@ class MethodCallsSTCTest extends StaticTypeCheckingTestCase {
             String[] args = ['a','b','c']
             assert c.identity(args) == args
         ''',
-        'Cannot call groovy.transform.stc.MethodCallsSTCTest$MyMethodCallTestClass2#identity(java.lang.Integer[]) with arguments [java.lang.String[]]'
+        'Cannot find matching method groovy.transform.stc.MethodCallsSTCTest$MyMethodCallTestClass2#identity(java.lang.String[])'
     }
 
     // GROOVY-8909