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/04/08 22:21:45 UTC

[groovy] branch GROOVY_3_0_X updated: GROOVY-10576: "?" source and target "? extends Object"

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

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


The following commit(s) were added to refs/heads/GROOVY_3_0_X by this push:
     new 2f0c277306 GROOVY-10576: "?" source and target "? extends Object"
2f0c277306 is described below

commit 2f0c27730693f99b0fe6c2cc0a2bda1c382c84ea
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Fri Apr 8 11:59:21 2022 -0500

    GROOVY-10576: "?" source and target "? extends Object"
---
 .../java/org/codehaus/groovy/ast/GenericsType.java |   4 +-
 .../groovy/transform/stc/GenericsSTCTest.groovy    | 164 ++++++++++++++-------
 2 files changed, 112 insertions(+), 56 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index cfb40470dd..cd3db14428 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -441,8 +441,8 @@ public class GenericsType extends ASTNode {
                                                 match = gt.checkGenerics(classNodeType.getLowerBound());
                                             } else if (classNodeType.getUpperBounds() != null) {
                                                 match = gt.checkGenerics(classNodeType.getUpperBounds()[0]);
-                                            } else {
-                                                match = false; // "?" (from Comparable<?>) does not satisfy anything
+                                            } else { // GROOVY-10576: "?" vs "? extends Object" (citation required) or no match
+                                                match = (!gt.isPlaceholder() && !gt.isWildcard() && ClassHelper.OBJECT_TYPE.equals(gt.getType()));
                                             }
                                         } else {
                                             match = implementsInterfaceOrIsSubclassOf(classNodeType.getType(), gt.getType());
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 7057a06c46..f09d895c25 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -1529,44 +1529,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         'Cannot call A <String, Integer>#<init>(java.lang.Class <String>, java.lang.Class <Integer>) with arguments [java.lang.Class <java.lang.Integer>, java.lang.Class <java.lang.String>]'
     }
 
-    void testMethodCallWithMapParameterUnbounded() {
-        assertScript """
-            import static ${this.class.name}.isEmpty
-            class C {
-                Map<String, ?> map = new HashMap()
-            }
-            assert isEmpty(new C().map)
-        """
-    }
-
-    // GROOVY-9460
-    void testMethodCallWithClassParameterUnbounded() {
-        assertScript '''
-            class Bar {
-                static void baz(Class<?> target) {
-                }
-            }
-            class Foo<X> { // cannot be "T" because that matches type parameter in Class
-                void test(Class<X> c) {
-                    Bar.baz(c) // Cannot call Bar#baz(Class<?>) with arguments [Class<X>]
-                }
-            }
-            new Foo<String>().test(String.class)
-        '''
-    }
-
-    // GROOVY-10525
-    void testMethodCallWithClassParameterUnbounded2() {
-        assertScript '''
-            @Grab('javax.validation:validation-api:1.1.0.Final')
-            import javax.validation.Validator
-
-            void test(Object bean, List<Class<?>> types, Validator validator) {
-                validator.validate(bean, types as Class<?>[])
-            }
-        '''
-    }
-
     void testConstructorCallWithClassParameterUsingClassLiteralArg() {
         assertScript '''
             class A {}
@@ -1593,21 +1555,21 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         '''
     }
 
-    void testPutMethodWithPrimitiveValue() {
+    void testPutWithPrimitiveValue() {
         assertScript '''
             def map = new HashMap<String, Integer>()
             map.put('hello', 1)
         '''
     }
 
-    void testPutAtMethodWithPrimitiveValue() {
+    void testPutAtWithPrimitiveValue() {
         assertScript '''
             def map = new HashMap<String, Integer>()
             map['hello'] = 1
         '''
     }
 
-    void testPutMethodWithWrongValueType() {
+    void testPutWithWrongValueType() {
         shouldFailWithMessages '''
             def map = new HashMap<String, Integer>()
             map.put('hello', new Object())
@@ -1615,6 +1577,99 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
         'Cannot find matching method java.util.HashMap#put(java.lang.String, java.lang.Object). Please check if the declared type is correct and if the method exists.'
     }
 
+    void testPutAtWithWrongValueType() {
+        shouldFailWithMessages '''
+            def map = new HashMap<String, Integer>()
+            map['hello'] = new Object()
+        ''',
+        'Cannot call <K,V> java.util.HashMap <String, Integer>#putAt(java.lang.String, java.lang.Integer) with arguments [java.lang.String, java.lang.Object]'
+    }
+
+    // GROOVY-9069
+    void testPutAtWithWrongValueType2() {
+        shouldFailWithMessages '''
+            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 = [:]
+                    maps.put(key, value)
+                    maps[key] = value
+                }
+            }
+        ''',
+        'Cannot find matching method java.util.Map#put(java.lang.String, java.util.LinkedHashMap <String, List>). Please check if the declared type is correct and if the method exists.',
+        'Cannot call <K,V> java.util.Map <String, Map>#putAt(java.lang.String, java.util.Map <String, List>) with arguments [java.lang.String, java.util.LinkedHashMap <String, List>]'
+    }
+
+    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)
+                }
+            }
+        '''
+    }
+
+    // GROOVY-10576
+    void testPutAllWithMapParameterUnbounded() {
+        assertScript '''
+            class C {
+                Map<String,Object> map
+                void test(Map<String,?> m) {
+                    map.putAll(m) // Cannot call Map#putAll(Map<? extends String, ? extends Object>) with arguments [Map<String, ?>]
+                }
+            }
+            def obj = new C(map:[:])
+            obj.test(foo:'bar')
+            def map = obj.map
+
+            assert map == [foo:'bar']
+        '''
+    }
+
+    void testMethodCallWithMapParameterUnbounded() {
+        assertScript """
+            import static ${this.class.name}.isEmpty
+            class C {
+                Map<String,?> map = new HashMap()
+            }
+            assert isEmpty(new C().map)
+        """
+    }
+
+    // GROOVY-9460
+    void testMethodCallWithClassParameterUnbounded() {
+        assertScript '''
+            class Bar {
+                static void baz(Class<?> target) {
+                }
+            }
+            class Foo<X> { // cannot be "T" because that matches type parameter in Class
+                void test(Class<X> c) {
+                    Bar.baz(c) // Cannot call Bar#baz(Class<?>) with arguments [Class<X>]
+                }
+            }
+            new Foo<String>().test(String.class)
+        '''
+    }
+
+    // GROOVY-10525
+    void testMethodCallWithClassParameterUnbounded2() {
+        assertScript '''
+            @Grab('javax.validation:validation-api:1.1.0.Final')
+            import javax.validation.Validator
+
+            void test(Object bean, List<Class<?>> types, Validator validator) {
+                validator.validate(bean, types as Class<?>[])
+            }
+        '''
+    }
+
     void testShouldComplainAboutToInteger() {
         String code = '''
             class Test {
@@ -1869,22 +1924,22 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     void testAddAllWithCollectionShouldBeAllowed() {
         assertScript '''import org.codehaus.groovy.transform.stc.ExtensionMethodNode
             List<String> list = ['a','b','c']
-            Collection<String> e = list.findAll { it }
+            Collection<String> strings = list.findAll { it }
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 def dmt = node.rightExpression.getNodeMetaData(DIRECT_METHOD_CALL_TARGET)
-                assert dmt instanceof ExtensionMethodNode == false
+                assert dmt !instanceof ExtensionMethodNode
+                assert dmt.declaringClass == LIST_TYPE
                 assert dmt.name == 'addAll'
-                assert dmt.declaringClass == make(List)
             })
-            boolean r = list.addAll(e)
+            boolean r = list.addAll(strings)
         '''
     }
 
     void testAddAllWithCollectionShouldNotBeAllowed() {
         shouldFailWithMessages '''
             List<String> list = ['a','b','c']
-            Collection<Integer> e = (Collection<Integer>) [1,2,3]
-            boolean r = list.addAll(e)
+            Collection<Integer> numbers = (Collection<Integer>) [1,2,3]
+            boolean r = list.addAll(numbers)
         ''',
         'Cannot call java.util.List <java.lang.String>#addAll(java.util.Collection <? extends java.lang.String>) with arguments [java.util.Collection <Integer>]'
     }
@@ -1959,14 +2014,14 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
     }
 
     // GROOVY-5559: related behaviour
-    void testGStringString() {
+    void testGStringVsString() {
         assertScript '''
             int i = 1
             @ASTTest(phase=INSTRUCTION_SELECTION, value={
                 assert node.getNodeMetaData(INFERRED_TYPE) == GSTRING_TYPE
             })
-            def str = "foo$i"
-            assert str == 'foo1'
+            def s = "i=$i"
+            assert s == 'i=1'
         '''
     }
 
@@ -2246,16 +2301,17 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
             def test() {
                 @ASTTest(phase=INSTRUCTION_SELECTION, value={
                     def type = node.getNodeMetaData(INFERRED_TYPE)
-                    assert type == make(List)
-                    assert type.genericsTypes.length==1
+                    assert type.equals(LIST_TYPE)
+                    assert type.genericsTypes.length == 1
                     assert type.genericsTypes[0].type == GSTRING_TYPE
                 })
                 List<GString> dates = ["${new Date()-1}", "${new Date()}", "${new Date()+1}"]
                 dates*.toUpperCase()
+
                 @ASTTest(phase=INSTRUCTION_SELECTION, value={
                     def type = node.getNodeMetaData(INFERRED_TYPE)
-                    assert type == make(List)
-                    assert type.genericsTypes.length==1
+                    assert type.equals(LIST_TYPE)
+                    assert type.genericsTypes.length == 1
                     assert type.genericsTypes[0].type == GSTRING_TYPE
                 })
                 List<GString> copied = []