Skip to content
Permalink
Browse files
GROOVY-10576: "?" source and target "? extends Object"
  • Loading branch information
eric-milles committed Apr 8, 2022
1 parent 4545a8b commit 07a5cd4527a477677c13723cd24fa95bcda72c7e
Show file tree
Hide file tree
Showing 2 changed files with 63 additions and 46 deletions.
@@ -412,8 +412,8 @@ private static boolean compareGenericsWithBound(final ClassNode classNode, final
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() && isObjectType(gt.getType()));
}
} else {
match = implementsInterfaceOrIsSubclassOf(classNodeType.getType(), gt.getType());
@@ -1598,44 +1598,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'Cannot call A#<init>(java.lang.Class<java.lang.String>, java.lang.Class<java.lang.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 {}
@@ -1662,29 +1624,29 @@ 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())
''',
'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 testPutAtMethodWithWrongValueType() {
void testPutAtWithWrongValueType() {
shouldFailWithMessages '''
def map = new HashMap<String, Integer>()
map['hello'] = new Object()
@@ -1693,7 +1655,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}

// GROOVY-9069
void testPutAtMethodWithWrongValueType2() {
void testPutAtWithWrongValueType2() {
shouldFailWithMessages '''
class ConfigAttribute {
}
@@ -1709,7 +1671,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'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 testPutAtMethodWithWrongValueType3() {
void testPutAtWithWrongValueType3() {
assertScript '''
void test(Map<String, Map<String, List<String>>> maps) {
maps.each { String key, Map<String, List<String>> map ->
@@ -1722,6 +1684,61 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}

// 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() {
shouldFailWithMessages '''
class Test {

0 comments on commit 07a5cd4

Please sign in to comment.