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/03/01 17:15:02 UTC

[groovy] 03/03: GROOVY-10010: check GString element vs String collection for method call

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

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

commit 2c034e74fcd87453429eb7555c8b18f76c6ca660
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Apr 5 14:13:34 2021 -0500

    GROOVY-10010: check GString element vs String collection for method call
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/ast/GenericsType.java
    	src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
---
 .../java/org/codehaus/groovy/ast/GenericsType.java |  2 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   | 18 +++--
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 86 +++++++++++++++-------
 3 files changed, 70 insertions(+), 36 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index 1853a10..6f40ec0 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -464,7 +464,7 @@ public class GenericsType extends ASTNode {
                                                 if (!match) break;
                                             }
                                         }
-                                        return match;
+                                        continue; // GROOVY-10010
                                     } else if (classNodePlaceholders.containsKey(name)) {
                                         redirectBoundType = classNodePlaceholders.get(name);
                                     }
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 edcc351..c310bf2 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -5434,16 +5434,18 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
 
     protected boolean typeCheckMethodsWithGenericsOrFail(ClassNode receiver, ClassNode[] arguments, MethodNode candidateMethod, Expression location) {
         if (!typeCheckMethodsWithGenerics(receiver, arguments, candidateMethod)) {
-            Map<GenericsTypeName, GenericsType> classGTs = GenericsUtils.extractPlaceholders(receiver);
-            ClassNode[] ptypes = new ClassNode[candidateMethod.getParameters().length];
-            final Parameter[] parameters = candidateMethod.getParameters();
-            for (int i = 0; i < parameters.length; i++) {
-                final Parameter parameter = parameters[i];
-                ClassNode type = parameter.getType();
-                ptypes[i] = fullyResolveType(type, classGTs);
+            Map<GenericsTypeName, GenericsType> spec = GenericsUtils.extractPlaceholders(receiver);
+            Parameter[] parameters = candidateMethod.getParameters();
+            ClassNode[] paramTypes = new ClassNode[parameters.length];
+            for (int i = 0, n = parameters.length; i < n; i += 1) {
+                paramTypes[i] = fullyResolveType(parameters[i].getType(), spec);
+                // GROOVY-10010: check for List<String> parameter and ["foo","$bar"] argument
+                if (i < arguments.length && hasGStringStringError(paramTypes[i], arguments[i], location)) {
+                    return false;
+                }
             }
             addStaticTypeError("Cannot call " + toMethodGenericTypesString(candidateMethod) + receiver.toString(false) + "#" +
-                    toMethodParametersString(candidateMethod.getName(), ptypes) +
+                    toMethodParametersString(candidateMethod.getName(), paramTypes) +
                     " with arguments " + formatArgumentList(arguments), location);
             return false;
         }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 6ac3534..cfdd1c3 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -26,24 +26,31 @@ import org.codehaus.groovy.tools.javac.JavaAwareCompilationUnit
  */
 class GenericsSTCTest extends StaticTypeCheckingTestCase {
 
-    void testDeclaration() {
+    void testDeclaration1() {
         assertScript '''
             List test = new LinkedList<String>()
         '''
     }
 
-    void testDeclaration5() {
+    void testDeclaration2() {
         assertScript '''
             Map<String,Integer> obj = new HashMap<String,Integer>()
         '''
     }
 
-    void testDeclaration6() {
+    void testDeclaration3() {
         shouldFailWithMessages '''
             Map<String,String> obj = new HashMap<String,Integer>()
         ''', 'Incompatible generic argument types. Cannot assign java.util.HashMap <String, Integer> to: java.util.Map <String, String>'
     }
 
+    // GROOVY-10010: check beyond first wildcard
+    void testDeclaration4() {
+        shouldFailWithMessages '''
+            Map<? extends CharSequence,String> obj = new HashMap<String,Integer>()
+        ''', 'Incompatible generic argument types. Cannot assign java.util.HashMap <String, Integer> to: java.util.Map <? extends java.lang.CharSequence, String>'
+    }
+
     void testAddOnList() {
         shouldFailWithMessages '''
             List<String> list = []
@@ -664,34 +671,59 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    // GROOVY-5559
+    // GROOVY-5559, GROOVY-10010
     void testGStringInListShouldNotBeConsideredAsAString() {
-        assertScript '''import org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode as LUB
-        def bar = 1
-        @ASTTest(phase=INSTRUCTION_SELECTION, value={
-            assert node.getNodeMetaData(INFERRED_TYPE) == LIST_TYPE
-            assert node.getNodeMetaData(INFERRED_TYPE).genericsTypes[0].type instanceof LUB
-        })
-        def list = ["foo", "$bar"]
+        String base = '''import org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode as LUB
+            def bar = 1
         '''
 
-        shouldFailWithMessages '''import org.codehaus.groovy.ast.tools.WideningCategories.LowestUpperBoundClassNode as LUB
-        def bar = 1
-        @ASTTest(phase=INSTRUCTION_SELECTION, value={
-            assert node.getNodeMetaData(INFERRED_TYPE) == LIST_TYPE
-            assert node.getNodeMetaData(INFERRED_TYPE).genericsTypes[0].type instanceof LUB
-        })
-        List<String> list = ["foo", "$bar"]
-        ''', 'You are trying to use a GString'
+        assertScript base + '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                assert node.getNodeMetaData(INFERRED_TYPE) == LIST_TYPE
+                assert node.getNodeMetaData(INFERRED_TYPE).genericsTypes[0].type instanceof LUB
+            })
+            def list = ["foo", "$bar"]
+        '''
 
-        shouldFailWithMessages '''
-        def bar = 1
-        @ASTTest(phase=INSTRUCTION_SELECTION, value={
-            assert node.getNodeMetaData(INFERRED_TYPE) == LIST_TYPE
-            assert node.getNodeMetaData(INFERRED_TYPE).genericsTypes[0].type == GSTRING_TYPE
-        })
-        List<String> list = ["$bar"] // single element means no LUB
-        ''', 'You are trying to use a GString'
+        shouldFailWithMessages base + '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                assert node.getNodeMetaData(INFERRED_TYPE) == LIST_TYPE
+                assert node.getNodeMetaData(INFERRED_TYPE).genericsTypes[0].type instanceof LUB
+            })
+            List<String> list = ["foo", "$bar"]
+        ''', 'You are trying to use a GString in place of a String'
+
+        shouldFailWithMessages base + '''
+            @ASTTest(phase=INSTRUCTION_SELECTION, value={
+                assert node.getNodeMetaData(INFERRED_TYPE) == LIST_TYPE
+                assert node.getNodeMetaData(INFERRED_TYPE).genericsTypes[0].type == GSTRING_TYPE // single element means no LUB
+            })
+            List<String> list = ["$bar"]
+        ''', 'You are trying to use a GString in place of a String'
+
+        shouldFailWithMessages base + '''
+            void m(List<String> list) {}
+            m(["foo", "$bar"])
+        ''', 'You are trying to use a GString in place of a String'
+    }
+
+    // GROOVY-5559, GROOVY-10010
+    void testGStringInMapShouldNotBeConsideredAsAString() {
+        String base = 'def bar = 123'
+
+        shouldFailWithMessages base + '''
+            Map<String,String> map = [x:"foo", y:"$bar"]
+        ''', 'You are trying to use a GString in place of a String'
+
+        shouldFailWithMessages base + '''
+            void m(Map<?,String> map) {}
+            m([x:"foo", y:"$bar"])
+        ''', 'You are trying to use a GString in place of a String'
+
+        shouldFailWithMessages base + '''
+            void m(Map<?,String> map) {}
+            m(x:"foo", y:"$bar")
+        ''', 'You are trying to use a GString in place of a String'
     }
 
     // GROOVY-5559: related behaviour