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 = []