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:14:59 UTC

[groovy] branch GROOVY_2_5_X updated (ca789f4 -> 2c034e7)

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

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


    from ca789f4  GROOVY-10484: `@NamedVariant`: bind `@NamedParam` into `@NamedParams`
     new 0d54bbc  GROOVY-9902: STC: allow method checking with partial generic information
     new 9227a0c  GROOVY-6095, GROOVY-9338: check a wildcard's bounds instead of base type
     new 2c034e7  GROOVY-10010: check GString element vs String collection for method call

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.


Summary of changes:
 .../java/org/codehaus/groovy/ast/GenericsType.java |  42 +++++--
 .../codehaus/groovy/control/ResolveVisitor.java    |  10 +-
 .../transform/stc/StaticTypeCheckingSupport.java   |  11 +-
 .../transform/stc/StaticTypeCheckingVisitor.java   |  18 +--
 src/test/groovy/bugs/Groovy6786Bug.groovy          |  39 +++----
 .../bugs/{Groovy8446.groovy => Groovy9338.groovy}  |  41 +++----
 src/test/groovy/transform/stc/BugsSTCTest.groovy   |  23 +++-
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 126 ++++++++++++++++-----
 8 files changed, 199 insertions(+), 111 deletions(-)
 copy src/test/groovy/bugs/{Groovy8446.groovy => Groovy9338.groovy} (55%)

[groovy] 02/03: GROOVY-6095, GROOVY-9338: check a wildcard's bounds instead of base type

Posted by em...@apache.org.
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 9227a0c5300d38a1473c7f7a0a812a3ed9aea39c
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Sat Dec 14 15:00:45 2019 -0600

    GROOVY-6095, GROOVY-9338: check a wildcard's bounds instead of base type
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/ast/GenericsType.java
---
 .../java/org/codehaus/groovy/ast/GenericsType.java | 24 +++++++--
 .../codehaus/groovy/control/ResolveVisitor.java    | 10 +---
 src/test/groovy/bugs/Groovy9338.groovy             | 60 ++++++++++++++++++++++
 src/test/groovy/transform/stc/BugsSTCTest.groovy   | 23 ++++++---
 4 files changed, 100 insertions(+), 17 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index 271fb1f..1853a10 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -428,7 +428,15 @@ public class GenericsType extends ASTNode {
                                                     gt = classNodePlaceholders.get(gtn);
                                                 }
                                             }
-                                            match = implementsInterfaceOrIsSubclassOf(gt.getType(), classNodeType.getType());
+                                            if (classNodeType.isWildcard()) {
+                                                if (classNodeType.getLowerBound() != null || classNodeType.getUpperBounds() != null) {
+                                                    match = classNodeType.new GenericsTypeMatcher().checkGenerics(gt.getType());
+                                                } else {
+                                                    match = false; // "?" (from Comparable<?>) does not satisfy anything
+                                                }
+                                            } else {
+                                                match = implementsInterfaceOrIsSubclassOf(gt.getType(), classNodeType.getType());
+                                            }
                                         }
                                         if (match && redirectBoundType.upperBounds!=null) {
                                             for (ClassNode upperBound : redirectBoundType.upperBounds) {
@@ -441,8 +449,18 @@ public class GenericsType extends ASTNode {
                                                         gt = classNodePlaceholders.get(gtn);
                                                     }
                                                 }
-                                                match = implementsInterfaceOrIsSubclassOf(classNodeType.getType(), gt.getType())
-                                                         || classNodeType.isCompatibleWith(gt.getType()); // workaround for GROOVY-6095
+                                                // GROOVY-6095, GROOVY-9338
+                                                if (classNodeType.isWildcard()) {
+                                                    if (classNodeType.getLowerBound() != null) {
+                                                        match = gt.new GenericsTypeMatcher().checkGenerics(classNodeType.getLowerBound());
+                                                    } else if (classNodeType.getUpperBounds() != null) {
+                                                        match = gt.new GenericsTypeMatcher().checkGenerics(classNodeType.getUpperBounds()[0]);
+                                                    } else {
+                                                        match = false; // "?" (from Comparable<?>) does not satisfy anything
+                                                    }
+                                                } else {
+                                                    match = implementsInterfaceOrIsSubclassOf(classNodeType.getType(), gt.getType());
+                                                }
                                                 if (!match) break;
                                             }
                                         }
diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index 00ebd63..da13d8e 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -1613,15 +1613,9 @@ public class ResolveVisitor extends ClassCodeExpressionTransformer {
             } else {
                 if (!isWild) {
                     if (toDealWithGenerics) {
-                        GenericsType originalGt = genericParameterNames.get(gtn);
-                        genericParameterNames.put(gtn, type);
                         type.setPlaceholder(true);
-
-                        if (null == originalGt) {
-                            classNode.setRedirect(ClassHelper.OBJECT_TYPE);
-                        } else {
-                            classNode.setRedirect(originalGt.getType());
-                        }
+                        GenericsType last = genericParameterNames.put(gtn, type);
+                        classNode.setRedirect(last != null ? last.getType().redirect() : ClassHelper.OBJECT_TYPE);
                     }
                 }
             }
diff --git a/src/test/groovy/bugs/Groovy9338.groovy b/src/test/groovy/bugs/Groovy9338.groovy
new file mode 100644
index 0000000..7e02b38
--- /dev/null
+++ b/src/test/groovy/bugs/Groovy9338.groovy
@@ -0,0 +1,60 @@
+/*
+ *  Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ */
+package groovy.bugs
+
+import groovy.transform.CompileStatic
+import org.junit.Test
+
+import static groovy.test.GroovyAssert.shouldFail
+
+@CompileStatic
+final class Groovy9338 {
+
+    @Test
+    void testGenericsUnsatisfied1() {
+        def err = shouldFail '''
+            void meth(Class<? extends CharSequence> c) {
+                print c.simpleName
+            }
+            @groovy.transform.CompileStatic
+            void test() {
+                def c = (Class<?>) String.class\n
+                meth(c)
+            }
+            test()
+        '''
+        assert err =~ /Cannot call \w+#meth\(java.lang.Class <\S+ extends java.lang.CharSequence>\) with arguments \[java.lang.Class <\?>\]/
+    }
+
+    @Test
+    void testGenericsUnsatisfied2() {
+        def err = shouldFail '''
+            void meth(Class<? super CharSequence> c) {
+                print c.simpleName
+            }
+            @groovy.transform.CompileStatic
+            void test() {
+                def c = (Class<?>) String.class\n
+                meth(c)
+            }
+            test()
+        '''
+        assert err =~ /Cannot call \w+#meth\(java.lang.Class <\S+ super java.lang.CharSequence>\) with arguments \[java.lang.Class <\?>\]/
+    }
+}
diff --git a/src/test/groovy/transform/stc/BugsSTCTest.groovy b/src/test/groovy/transform/stc/BugsSTCTest.groovy
index bbfa7db..8d3f2eb 100644
--- a/src/test/groovy/transform/stc/BugsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/BugsSTCTest.groovy
@@ -185,13 +185,24 @@ class BugsSTCTest extends StaticTypeCheckingTestCase {
 
     void testGroovy7477NullGenericsType() {
         assertScript '''
-        class L<E> extends ArrayList<E> {
-            boolean removeIf(Comparator<? super E> filter) { }
-        }
-        L<String> items = ['foo', 'bar'] as L<String>
-        items.removeIf({a, b -> 1} as Comparator<?>)
-        assert items
+            class L<E> extends ArrayList<E> {
+                boolean removeIf(Comparator<? super E> filter) {
+                }
+            }
+            def items = ['foo', 'bar'] as L<String>
+            items.removeIf({a, b -> 1} as Comparator<String>)
+            assert items
         '''
+
+        shouldFailWithMessages '''
+            class L<E> extends ArrayList<E> {
+                boolean removeIf(Comparator<? super E> filter) {
+                }
+            }
+            L<String> items = ['foo', 'bar'] as L<String>
+            items.removeIf({a, b -> 1} as Comparator<?>)
+            assert items
+        ''', 'Cannot call L <String>#removeIf(java.util.Comparator <java.lang.Object super java.lang.String>) with arguments [java.util.Comparator <?>]'
     }
 
     void testGroovy5482ListsAndFlowTyping() {

[groovy] 01/03: GROOVY-9902: STC: allow method checking with partial generic information

Posted by em...@apache.org.
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 0d54bbc5c7f344fb1cef02a10dd848914169970a
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Wed Jan 20 20:46:54 2021 -0600

    GROOVY-9902: STC: allow method checking with partial generic information
    
    Conflicts:
    	src/main/java/org/codehaus/groovy/ast/GenericsType.java
    	src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
---
 .../java/org/codehaus/groovy/ast/GenericsType.java | 16 ++++-----
 .../transform/stc/StaticTypeCheckingSupport.java   | 11 +++---
 src/test/groovy/bugs/Groovy6786Bug.groovy          | 39 +++++++++------------
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 40 ++++++++++++++++++++++
 4 files changed, 68 insertions(+), 38 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index c67db97..271fb1f 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -384,15 +384,15 @@ public class GenericsType extends ASTNode {
                         GenericsTypeName gtn = new GenericsTypeName(redirectBoundType.getName());
                         match = name.equals(gtn);
                         if (!match) {
-                            GenericsType genericsType = boundPlaceHolders.get(gtn);
-                            if (genericsType != null) {
-                                if (genericsType.isPlaceholder()) {
+                            GenericsType boundGenericsType = boundPlaceHolders.get(gtn);
+                            if (boundGenericsType != null) {
+                                if (boundGenericsType.isPlaceholder()) {
                                     match = true;
-                                } else if (genericsType.isWildcard()) {
-                                    if (genericsType.getUpperBounds() != null) { // multiple bounds not allowed for ?
-                                        match = redirectBoundType.isCompatibleWith(genericsType.getUpperBounds()[0]);
-                                    } else if (genericsType.getLowerBound() != null) {
-                                        match = redirectBoundType.isCompatibleWith(genericsType.getLowerBound());
+                                } else if (boundGenericsType.isWildcard()) {
+                                    if (boundGenericsType.getUpperBounds() != null) { // multiple bounds not allowed for ?
+                                        match = classNodeType.isCompatibleWith(boundGenericsType.getUpperBounds()[0]);
+                                    } else if (boundGenericsType.getLowerBound() != null) {
+                                        match = classNodeType.isCompatibleWith(boundGenericsType.getLowerBound());
                                     } else {
                                         match = true;
                                     }
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 2e9d221..d7b7f72 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -1449,19 +1449,16 @@ public abstract class StaticTypeCheckingSupport {
     }
 
     protected static boolean typeCheckMethodsWithGenerics(ClassNode receiver, ClassNode[] arguments, MethodNode candidateMethod) {
-        if (isUsingUncheckedGenerics(receiver)) {
-            return true;
-        }
-        if (CLASS_Type.equals(receiver)
+        boolean isExtensionMethod = candidateMethod instanceof ExtensionMethodNode;
+        if (!isExtensionMethod
                 && receiver.isUsingGenerics()
-                && !candidateMethod.getDeclaringClass().equals(receiver)
-                && !(candidateMethod instanceof ExtensionMethodNode)) {
+                && receiver.equals(CLASS_Type)
+                && !candidateMethod.getDeclaringClass().equals(CLASS_Type)) {
             return typeCheckMethodsWithGenerics(receiver.getGenericsTypes()[0].getType(), arguments, candidateMethod);
         }
         // both candidate method and receiver have generic information so a check is possible
         GenericsType[] genericsTypes = candidateMethod.getGenericsTypes();
         boolean methodUsesGenerics = (genericsTypes != null && genericsTypes.length > 0);
-        boolean isExtensionMethod = candidateMethod instanceof ExtensionMethodNode;
         if (isExtensionMethod && methodUsesGenerics) {
             ClassNode[] dgmArgs = new ClassNode[arguments.length + 1];
             dgmArgs[0] = receiver;
diff --git a/src/test/groovy/bugs/Groovy6786Bug.groovy b/src/test/groovy/bugs/Groovy6786Bug.groovy
index d8611e8..f125a79 100644
--- a/src/test/groovy/bugs/Groovy6786Bug.groovy
+++ b/src/test/groovy/bugs/Groovy6786Bug.groovy
@@ -18,24 +18,22 @@
  */
 package groovy.bugs
 
-import groovy.transform.NotYetImplemented
 import groovy.transform.stc.StaticTypeCheckingTestCase
 
 class Groovy6786Bug extends StaticTypeCheckingTestCase {
-    
+
     void testGenericAddAll() {
         assertScript '''
-
             public class Class1<ENTITY> {
-            
+
                 Container<ENTITY> container;
-            
+
                 void refresh() {
                     def items = findAllItems();
-                    
+
                     container.addAll(items);
                 }
-            
+
                 Collection<ENTITY> findAllItems() {
                     return null;
                 }
@@ -44,32 +42,30 @@ class Groovy6786Bug extends StaticTypeCheckingTestCase {
                 void addAll(Collection<? extends ENTITY> collection);
             }
             new Class1()
-
         '''
     }
 
     void testGuavaCacheBuilderLikeGenerics() {
         assertScript '''
             class Class1 {
-            
                 protected LoadingCache<String, Integer> cache;
-            
+
                 Class1(CacheLoader<String, Integer> cacheLoader) {
                     this.cache = CacheBuilder.newBuilder().build(cacheLoader);
                 }
             }
-            
+
             class CacheLoader<K, V> {}
-            
+
             class LoadingCache<K, V> {}
-            
+
             class CacheBuilder<K, V> {
                 public static CacheBuilder<Object, Object> newBuilder() {
                     return new CacheBuilder<Object, Object>();
                 }
-            
+
                 public <K1 extends K, V1 extends V> LoadingCache<K1, V1> build(CacheLoader<? super K1, V1> loader) {
-                    return new LoadingCache<K1, V1>();  
+                    return new LoadingCache<K1, V1>();
                 }
             }
             new Class1(null)
@@ -83,9 +79,9 @@ class Groovy6786Bug extends StaticTypeCheckingTestCase {
                     return false;
                 }
             }
-            
+
             abstract class AbstractExtendedManager<T> extends AbstractManager<T> {}
-                        
+
             class ConcreteManager extends AbstractExtendedManager<String> {
                 @Override
                  protected boolean update(String item){
@@ -99,20 +95,18 @@ class Groovy6786Bug extends StaticTypeCheckingTestCase {
     void testIfWithInstanceOfAndAnotherConditionAfter() {
         assertScript '''
             class Class1 {
-            
-            
                 Class1() {
                     Object obj = null;
-                    
+
                     if(obj instanceof String && someCondition() && testMethod(obj)) {
                         println "Hello!"
                     }
                 }
-                
+
                 boolean someCondition() {
                     return true;
                 }
-                
+
                 boolean testMethod(String arg) {
                     return true;
                 }
@@ -120,5 +114,4 @@ class Groovy6786Bug extends StaticTypeCheckingTestCase {
             new Class1();
         '''
     }
-    
 }
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index dabe166..6ac3534 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1042,6 +1042,46 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         ''', '#printEqual(T, java.util.List <T>) with arguments [int, java.util.List <java.lang.String>]'
     }
 
+    // GROOVY-9902
+    void testIncompatibleArgumentsForGenericArgument_IncludingDelegation() {
+        shouldFailWithMessages '''
+            class Holder<Unknown> {
+                TypedProperty<Number, Unknown> numberProperty = prop(Number)
+                TypedProperty<String, Unknown> stringProperty = prop(String)
+
+                def <T> TypedProperty<T, Unknown> prop(Class<T> clazz) {
+                    return new TypedProperty<T, Unknown>(clazz: clazz)
+                }
+
+                // Note: type argument of Holder cannot be supplied to value attribute of @DelegatesTo
+                def <T> T of(@DelegatesTo(value=Holder, strategy=Closure.DELEGATE_FIRST) Closure<T> c) {
+                    this.with(c)
+                }
+            }
+
+            class TypedProperty<X, Y> {
+                Class<X> clazz
+
+                void eq(X x) {
+                    assert x.class == clazz : "x.class is ${x.class} not ${clazz}"
+                }
+            }
+
+            void test(Holder<Object> h) {
+                h.stringProperty.eq("${0}") // STC error
+                h.of { // <-- 2nd type parameter discarded
+                    stringProperty.eq(1234) // expect STC error
+                    numberProperty.eq("xx") // expect STC error
+                }
+            }
+
+            test(new Holder<Object>())
+        ''',
+        'Cannot call TypedProperty <String, Object>#eq(java.lang.String) with arguments [groovy.lang.GString]',
+        'Cannot call TypedProperty <String, Unknown>#eq(java.lang.String) with arguments [int]',
+        'Cannot call TypedProperty <Number, Unknown>#eq(java.lang.Number) with arguments [java.lang.String]'
+    }
+
     void testGroovy5748() {
         assertScript '''
             interface IStack<T> {

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

Posted by em...@apache.org.
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