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/07/18 16:46:57 UTC
[groovy] branch GROOVY_2_5_X updated: GROOVY-9956, GROOVY-10086, GROOVY-10316, GROOVY-10344: STC: CCE diamond
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
The following commit(s) were added to refs/heads/GROOVY_2_5_X by this push:
new e29c22e3e7 GROOVY-9956, GROOVY-10086, GROOVY-10316, GROOVY-10344: STC: CCE diamond
e29c22e3e7 is described below
commit e29c22e3e7ce484ed7636d3cbb032b620e1c68e9
Author: Eric Milles <er...@thomsonreuters.com>
AuthorDate: Mon Jul 18 11:27:42 2022 -0500
GROOVY-9956, GROOVY-10086, GROOVY-10316, GROOVY-10344: STC: CCE diamond
---
.../java/org/codehaus/groovy/ast/GenericsType.java | 88 +++------
.../transform/stc/StaticTypeCheckingSupport.java | 29 +--
.../transform/stc/StaticTypeCheckingVisitor.java | 61 ++++--
.../groovy/transform/stc/GenericsSTCTest.groovy | 215 ++++++++++++++++++---
4 files changed, 271 insertions(+), 122 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/GenericsType.java b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
index 18a16da88c..b8399bf24e 100644
--- a/src/main/java/org/codehaus/groovy/ast/GenericsType.java
+++ b/src/main/java/org/codehaus/groovy/ast/GenericsType.java
@@ -196,21 +196,24 @@ public class GenericsType extends ASTNode {
return name0.equals(getName());
}
if (getLowerBound() != null) {
+ // check for "? super T" vs "T"
if (name0.equals(getLowerBound().getUnresolvedName())) {
return true;
}
} else if (getUpperBounds() != null) {
+ // check for "? extends T" vs "T"
if (name0.equals(getUpperBounds()[0].getUnresolvedName())) {
return true;
}
}
+ // check for "? extends/super X" vs "T extends/super X"
return checkGenerics(classNode);
}
if (isWildcard() || isPlaceholder()) {
// if the generics spec is a wildcard or a placeholder then check the bounds
ClassNode lowerBound = getLowerBound();
if (lowerBound != null) {
- // for a lower bound, perform the upper bound checks with reversed arguments
+ // test bound and type in reverse for lower bound vs upper bound
if (!implementsInterfaceOrIsSubclassOf(lowerBound, classNode)) {
return false;
}
@@ -224,18 +227,15 @@ public class GenericsType extends ASTNode {
return false;
}
}
- // if the provided classnode is a subclass of the upper bound
- // then check that the generic types supplied by the class node are compatible with
- // this generics specification
- // for example, we could have the spec saying List<String> but provided classnode
- // saying List<Integer>
+ // if the provided type is a subclass of the upper bound(s) then
+ // check that the generic types supplied are compatible with this
+ // for example, this spec could be "Type<X>" but type is "Type<Y>"
return checkGenerics(classNode);
}
- // if there are no bounds, the generic type is basically Object, and everything is compatible
+ // if there are no bounds, the generic type is basically Object and everything is compatible
return true;
}
- // last, we could have the spec saying List<String> and a classnode saying List<Integer> so
- // we must check that generics are compatible
+ // not placeholder or wildcard; no covariance allowed for type or bound(s)
return getType().equals(classNode) && compareGenericsWithBound(classNode, type);
}
@@ -294,13 +294,12 @@ public class GenericsType extends ASTNode {
* @return true if generics are compatible
*/
private static boolean compareGenericsWithBound(final ClassNode classNode, final ClassNode bound) {
- if (classNode == null) {
- return false;
- }
- if (bound.getGenericsTypes() == null || (classNode.getGenericsTypes() == null && classNode.redirect().getGenericsTypes() != null)) {
- // if the bound is not using generics or the class node is a raw type, there's nothing to compare with
+ if (classNode == null) return false;
+ if (bound.getGenericsTypes() == null
+ || (classNode.getGenericsTypes() == null && classNode.redirect().getGenericsTypes() != null))
+ // if the bound is not using generics or the class node is a raw type, there's nothing to compare
return true;
- }
+
if (!classNode.equals(bound)) {
// the class nodes are on different types
// in this situation, we must choose the correct execution path : either the bound
@@ -316,8 +315,10 @@ public class GenericsType extends ASTNode {
// class node are not parameterized. This means that we must create a
// new class node with the parameterized types that the current class node
// has defined.
- ClassNode node = GenericsUtils.parameterizeType(classNode, face);
- return compareGenericsWithBound(node, bound);
+ if (face.getGenericsTypes() != null) {
+ face = GenericsUtils.parameterizeType(classNode, face);
+ }
+ return compareGenericsWithBound(face, bound);
}
}
}
@@ -333,7 +334,16 @@ public class GenericsType extends ASTNode {
if (success) return true;
}
}
- return compareGenericsWithBound(getParameterizedSuperClass(classNode), bound);
+ if (classNode.equals(ClassHelper.OBJECT_TYPE)) {
+ return false;
+ }
+ ClassNode superClass = classNode.getUnresolvedSuperClass();
+ if (superClass == null) {
+ superClass = ClassHelper.OBJECT_TYPE;
+ } else if (superClass.getGenericsTypes() != null) {
+ superClass = GenericsUtils.parameterizeType(classNode, superClass);
+ }
+ return compareGenericsWithBound(superClass, bound);
}
GenericsType[] cnTypes = classNode.getGenericsTypes();
@@ -341,7 +351,7 @@ public class GenericsType extends ASTNode {
cnTypes = classNode.redirect().getGenericsTypes();
}
if (cnTypes == null) {
- // may happen if generic type is Foo<T extends Foo> and classnode is Foo -> Foo
+ // may happen if generic type is Foo<T extends Foo> and ClassNode is Foo -> Foo
return true;
}
@@ -452,46 +462,6 @@ public class GenericsType extends ASTNode {
return match;
}
- /**
- * If you have a class which extends a class using generics, returns the superclass with parameterized types. For
- * example, if you have:
- * <code>class MyList<T> extends LinkedList<T>
- * def list = new MyList<String>
- * </code>
- * then the parameterized superclass for MyList<String> is LinkedList<String>
- * @param classNode the class for which we want to return the parameterized superclass
- * @return the parameterized superclass
- */
- private static ClassNode getParameterizedSuperClass(final ClassNode classNode) {
- if (ClassHelper.OBJECT_TYPE.equals(classNode)) return null;
- ClassNode superClass = classNode.getUnresolvedSuperClass();
- if (superClass == null) return ClassHelper.OBJECT_TYPE;
-
- if (!classNode.isUsingGenerics() || !superClass.isUsingGenerics()) {
- return superClass;
- }
-
- GenericsType[] genericsTypes = classNode.getGenericsTypes();
- GenericsType[] redirectGenericTypes = classNode.redirect().getGenericsTypes();
- superClass = superClass.getPlainNodeReference();
- if (genericsTypes == null || redirectGenericTypes == null || superClass.getGenericsTypes() == null) {
- return superClass;
- }
- for (int i = 0, genericsTypesLength = genericsTypes.length; i < genericsTypesLength; i += 1) {
- if (redirectGenericTypes[i].isPlaceholder()) {
- GenericsType genericsType = genericsTypes[i];
- GenericsType[] superGenericTypes = superClass.getGenericsTypes();
- for (int j = 0, superGenericTypesLength = superGenericTypes.length; j < superGenericTypesLength; j += 1) {
- GenericsType superGenericType = superGenericTypes[j];
- if (superGenericType.isPlaceholder() && superGenericType.getName().equals(redirectGenericTypes[i].getName())) {
- superGenericTypes[j] = genericsType;
- }
- }
- }
- }
- return superClass;
- }
-
//--------------------------------------------------------------------------
public static class GenericsTypeName {
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 634f685083..13c56c71ea 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -16,7 +16,6 @@
* specific language governing permissions and limitations
* under the License.
*/
-
package org.codehaus.groovy.transform.stc;
import org.codehaus.groovy.GroovyBugError;
@@ -66,7 +65,6 @@ import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
-import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;
import java.util.UUID;
@@ -159,13 +157,15 @@ import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED;
import static org.codehaus.groovy.syntax.Types.RIGHT_SHIFT_UNSIGNED_EQUAL;
/**
- * Static support methods for {@link StaticTypeCheckingVisitor}.
+ * Support methods for {@link StaticTypeCheckingVisitor}.
*/
public abstract class StaticTypeCheckingSupport {
- protected static final ClassNode Collection_TYPE = makeWithoutCaching(Collection.class);
- protected static final ClassNode Deprecated_TYPE = makeWithoutCaching(Deprecated.class);
+
protected static final ClassNode Matcher_TYPE = makeWithoutCaching(Matcher.class);
protected static final ClassNode ArrayList_TYPE = makeWithoutCaching(ArrayList.class);
+ protected static final ClassNode Collection_TYPE = makeWithoutCaching(Collection.class);
+ protected static final ClassNode Deprecated_TYPE = makeWithoutCaching(Deprecated.class);
+
protected static final ExtensionMethodCache EXTENSION_METHOD_CACHE = new ExtensionMethodCache();
protected static final Map<ClassNode, Integer> NUMBER_TYPES = Collections.unmodifiableMap(
new HashMap<ClassNode, Integer>() {
@@ -1539,7 +1539,7 @@ public abstract class StaticTypeCheckingSupport {
private static Set<GenericsTypeName> extractResolvedPlaceHolders(Map<GenericsTypeName, GenericsType> resolvedMethodGenerics) {
if (resolvedMethodGenerics.isEmpty()) return Collections.EMPTY_SET;
Set<GenericsTypeName> result = new HashSet<GenericsTypeName>();
- for (Entry<GenericsTypeName, GenericsType> entry : resolvedMethodGenerics.entrySet()) {
+ for (Map.Entry<GenericsTypeName, GenericsType> entry : resolvedMethodGenerics.entrySet()) {
GenericsType value = entry.getValue();
if (value.isPlaceholder()) continue;
result.add(entry.getKey());
@@ -1592,7 +1592,7 @@ public abstract class StaticTypeCheckingSupport {
}
private static boolean compatibleConnections(Map<GenericsTypeName, GenericsType> connections, Map<GenericsTypeName, GenericsType> resolvedMethodGenerics, Set<GenericsTypeName> fixedGenericsPlaceHolders) {
- for (Entry<GenericsTypeName, GenericsType> entry : connections.entrySet()) {
+ for (Map.Entry<GenericsTypeName, GenericsType> entry : connections.entrySet()) {
GenericsType resolved = resolvedMethodGenerics.get(entry.getKey());
if (resolved == null) continue;
GenericsType connection = entry.getValue();
@@ -1639,7 +1639,7 @@ public abstract class StaticTypeCheckingSupport {
}
private static void addMissingEntries(Map<GenericsTypeName, GenericsType> connections, Map<GenericsTypeName, GenericsType> resolved) {
- for (Entry<GenericsTypeName, GenericsType> entry : connections.entrySet()) {
+ for (Map.Entry<GenericsTypeName, GenericsType> entry : connections.entrySet()) {
if (resolved.containsKey(entry.getKey())) continue;
GenericsType gt = entry.getValue();
ClassNode cn = gt.getType();
@@ -1981,19 +1981,6 @@ public abstract class StaticTypeCheckingSupport {
return genericsType.getType();
}
- private static void applyContextGenerics(Map<GenericsTypeName, GenericsType> resolvedPlaceholders, Map<GenericsTypeName, GenericsType> placeholdersFromContext) {
- if (placeholdersFromContext == null) return;
- for (Entry<GenericsTypeName, GenericsType> entry : resolvedPlaceholders.entrySet()) {
- GenericsType gt = entry.getValue();
- if (gt.isPlaceholder()) {
- GenericsTypeName name = new GenericsTypeName(gt.getName());
- GenericsType outer = placeholdersFromContext.get(name);
- if (outer == null) continue;
- entry.setValue(outer);
- }
- }
- }
-
private static Map<GenericsTypeName, GenericsType> getGenericsParameterMapOfThis(ClassNode cn) {
if (cn == null) return null;
Map<GenericsTypeName, GenericsType> map = null;
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 ab98570249..6173a262ef 100644
--- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -1058,19 +1058,39 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
}
protected void inferDiamondType(final ConstructorCallExpression cce, final ClassNode lType) {
- ClassNode cceType = cce.getType();
+ ClassNode cceType = cce.getType(), inferredType = lType;
// check if constructor call expression makes use of the diamond operator
if (cceType.getGenericsTypes() != null && cceType.getGenericsTypes().length == 0) {
- ArgumentListExpression argumentListExpression = InvocationWriter.makeArgumentList(cce.getArguments());
- if (argumentListExpression.getExpressions().isEmpty()) {
- adjustGenerics(lType, cceType);
- } else {
- ClassNode type = getType(argumentListExpression.getExpression(0));
+ ArgumentListExpression argumentList = InvocationWriter.makeArgumentList(cce.getArguments());
+ ConstructorNode constructor = cce.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
+ if (constructor != null && !argumentList.getExpressions().isEmpty()) {
+ ClassNode type = GenericsUtils.parameterizeType(cceType, cceType);
+ type = inferReturnTypeGenerics(type, constructor, argumentList);
if (type.isUsingGenerics()) {
- adjustGenerics(type, cceType);
+ // GROOVY-6232, GROOVY-9956: if cce not assignment compatible, process target as additional type witness
+ if (checkCompatibleAssignmentTypes(lType, type, cce) && !GenericsUtils.buildWildcardType(lType).isCompatibleWith(type)) {
+ // allow covariance of each type parameter, but maintain semantics for nested generics
+
+ ClassNode pType = GenericsUtils.parameterizeType(lType, type);
+ GenericsType[] lhs = pType.getGenericsTypes(), rhs = type.getGenericsTypes();
+ if (lhs == null || rhs == null || lhs.length != rhs.length) throw new GroovyBugError(
+ "Parameterization failed: " + prettyPrintType(pType) + " ~ " + prettyPrintType(type));
+
+ boolean allMatch = true;
+ for (int i = 0, n = lhs.length; i < n && allMatch; i += 1) {
+ if (!GenericsUtils.buildWildcardType(getCombinedBoundType(lhs[i])).isCompatibleWith(getCombinedBoundType(rhs[i]))) {
+ allMatch = false;
+ }
+ }
+ if (allMatch) type = pType; // lType proved to be a viable type witness
+ }
+ inferredType = type;
}
}
- // store inferred type on CCE
+ if (inferredType.isGenericsPlaceHolder()) // GROOVY-10344: "T t = new C<>()"
+ inferredType = getCombinedBoundType(inferredType.getGenericsTypes()[0]);
+
+ adjustGenerics(inferredType, cceType);
storeType(cce, cceType);
}
}
@@ -5209,9 +5229,20 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
* argument is {@code List<T>} without explicit type arguments. Knowning the
* method target of "m", {@code T} could be resolved.
*/
- private static void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final Parameter[] parameterArray) {
- for (int i = 0, n = actuals.length; i < n; i += 1) {
+ private void resolvePlaceholdersFromImplicitTypeHints(final ClassNode[] actuals, final ArgumentListExpression argumentList, final Parameter[] parameterArray) {
+ int np = parameterArray.length;
+ for (int i = 0, n = actuals.length; i < n && np > 0; i += 1) {
Expression a = argumentList.getExpression(i);
+ Parameter p = parameterArray[Math.min(i, np - 1)];
+
+ ClassNode at = actuals[i], pt = p.getOriginType();
+ if (!isUsingGenericsOrIsArrayUsingGenerics(pt)) continue;
+ if (i >= (np - 1) && pt.isArray() && !at.isArray()) pt = pt.getComponentType();
+
+ if (a instanceof ConstructorCallExpression) {
+ inferDiamondType((ConstructorCallExpression) a, pt); // GROOVY-10086, GROOVY-10316
+ }
+
// check for method call without type arguments, with a known target
if (!(a instanceof MethodCall) || (a instanceof MethodCallExpression
&& ((MethodCallExpression) a).isUsingGenerics())) continue;
@@ -5219,14 +5250,12 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
if (aNode == null || aNode.getGenericsTypes() == null) continue;
// and unknown generics
- ClassNode at = actuals[i];
if (!GenericsUtils.hasUnresolvedGenerics(at)) continue;
- int np = parameterArray.length;
- Parameter p = parameterArray[Math.min(i, np - 1)];
-
- ClassNode pt = p.getOriginType();
- if (i >= (np - 1) && pt.isArray() && !at.isArray()) pt = pt.getComponentType();
+ while (!at.equals(pt) && !at.equals(OBJECT_TYPE)) {
+ ClassNode sc = GenericsUtils.getSuperClass(at, pt);
+ at = applyGenericsContext(GenericsUtils.extractPlaceholders(at), sc);
+ }
// try to resolve placeholder(s) in argument type using parameter type
diff --git a/src/test/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
index 13bc2ff199..f76b57c149 100644
--- a/src/test/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/transform/stc/GenericsSTCTest.groovy
@@ -41,21 +41,24 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
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>'
+ ''',
+ '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>'
+ ''',
+ '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 = []
list.add(1)
- ''', "[Static type checking] - Cannot find matching method java.util.List#add(int)"
+ ''',
+ 'Cannot find matching method java.util.List#add(int)'
}
void testAddOnList2() {
@@ -81,7 +84,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
List<String> list = []
list << 1
- ''', 'Cannot call <T> java.util.List <String>#leftShift(T) with arguments [int]'
+ ''',
+ 'Cannot call <T> java.util.List <String>#leftShift(T) with arguments [int]'
}
void testAddOnList2UsingLeftShift() {
@@ -129,14 +133,16 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
List<Integer> list = new LinkedList<>()
list.add 'Hello'
- ''', 'Cannot find matching method java.util.LinkedList#add(java.lang.String). Please check if the declared type is correct and if the method exists.'
+ ''',
+ 'Cannot find matching method java.util.LinkedList#add(java.lang.String). Please check if the declared type is correct and if the method exists.'
}
void testAddOnListWithDiamondAndWrongTypeUsingLeftShift() {
shouldFailWithMessages '''
List<Integer> list = new LinkedList<>()
list << 'Hello'
- ''', 'Cannot call <T> java.util.LinkedList <java.lang.Integer>#leftShift(T) with arguments [java.lang.String]'
+ ''',
+ 'Cannot call <T> java.util.LinkedList <java.lang.Integer>#leftShift(T) with arguments [java.lang.String]'
}
void testAddOnListWithDiamondAndNullUsingLeftShift() {
@@ -281,26 +287,114 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
void testDiamondInferrenceFromConstructor1() {
assertScript '''
- Set< Long > s2 = new HashSet<>()
+ class Foo<U> {
+ U method() { }
+ }
+ Foo<Integer> foo = new Foo<>()
+ Integer result = foo.method()
'''
}
- void testDiamondInferrenceFromConstructorWithoutAssignment() {
+ void testDiamondInferrenceFromConstructor2() {
assertScript '''
- new HashSet<>(Arrays.asList(0L,0L));
+ Set<Long> set = new HashSet<>()
'''
}
- void testDiamondInferrenceFromConstructor2() {
- shouldFailWithMessages '''
- Set< Number > s3 = new HashSet<>(Arrays.asList(0L,0L));
- ''', 'Cannot assign java.util.HashSet <java.lang.Long> to: java.util.Set <Number>'
+ void testDiamondInferrenceFromConstructor3() {
+ assertScript '''
+ new HashSet<>(Arrays.asList(0L))
+ '''
+
+ assertScript '''
+ Set<Number> set = new HashSet<>(Arrays.asList(0L))
+ '''
+
+ // not diamond inference, but tests compatible assignment
+ assertScript '''
+ Set<Number> set = new HashSet<Number>(Arrays.asList(0L))
+ '''
+
+ assertScript '''
+ Set<? super Number> set = new HashSet<>(Arrays.asList(0L))
+ '''
+
+ assertScript '''
+ Set<? extends Number> set = new HashSet<>(Arrays.asList(0L))
+ '''
}
- void testDiamondInferrenceFromConstructor3() {
+ // GROOVY-7419
+ void testDiamondInferrenceFromConstructor4() {
+ assertScript '''
+ Map<Thread.State, Object> map = new EnumMap<>(Thread.State)
+ assert map.size() == 0
+ assert map.isEmpty()
+ '''
+ }
+
+ // GROOVY-9956
+ void testDiamondInferrenceFromConstructor8() {
+ assertScript '''
+ @groovy.transform.TupleConstructor(defaults=false)
+ class C<T> {
+ T p
+ }
+ interface I { }
+ class D implements I { }
+
+ C<I> ci = new C<I>(new D())
+ ci = new C<>(new D()) // infers C<D> on RHS
+ '''
+ }
+
+ void testDiamondInferrenceFromConstructor9() {
assertScript '''
- Set<Number> s4 = new HashSet<Number>(Arrays.asList(0L,0L))
+ abstract class A<X> { }
+ @groovy.transform.TupleConstructor(defaults=false)
+ class C<T> extends A<T> {
+ T p
+ }
+ interface I { }
+ class D implements I { }
+
+ A<I> ai = new C<>(new D())
'''
+
+ shouldFailWithMessages '''
+ abstract class A<X> { }
+ @groovy.transform.TupleConstructor(defaults=false)
+ class C<T> extends A<T> {
+ T p
+ }
+ interface I { }
+ class D implements I { }
+
+ A<String> ax = new C<>(new D())
+ ''',
+ 'Incompatible generic argument types. Cannot assign C <D> to: A <String>'
+
+ shouldFailWithMessages '''
+ Set<List<String>> strings = new HashSet<>([new ArrayList<Number>()])
+ ''',
+ 'Incompatible generic argument types. Cannot assign java.util.HashSet <java.util.ArrayList> to: java.util.Set <List>'
+ }
+
+ // GROOVY-10086
+ void testDiamondInferrenceFromConstructor12() {
+ shouldFailWithMessages '''
+ @groovy.transform.TupleConstructor(defaults=false)
+ class C<T> {
+ T p
+ }
+ class D {
+ }
+ void m(int i, C<D>... zeroOrMore) {
+ D d = zeroOrMore[0].p
+ }
+ m(0, new C<>(1))
+ ''',
+ 'Cannot call', 'm(int, C <D>[]) with arguments [int, C <java.lang.Integer>]'
}
// GROOVY-10324
@@ -314,6 +408,86 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-10310
+ void testDiamondInferrenceFromConstructor21() {
+ assertScript '''
+ @groovy.transform.TupleConstructor
+ class A<T> {
+ T f
+ }
+ class B<T> {
+ }
+ def <T> A<T> m(T t, B<? extends T> b_of_t) {
+ new A<>(t)
+ }
+ def x = 'x'
+ m(x, new B<>()) // Cannot call m(T,B<? extends T>) with arguments...
+ '''
+ }
+
+ // GROOVY-10344
+ void testDiamondInferrenceFromConstructor22() {
+ assertScript '''
+ class C<X,Y> {
+ }
+ def <T extends C<? extends Number, ? extends Number>> T m(T t) {
+ return t
+ }
+ def x = m(new C<>())
+ assert x instanceof C
+ '''
+ }
+
+ @NotYetImplemented // GROOVY-10230
+ void testDiamondInferrenceFromConstructor23() {
+ assertScript '''
+ class A {
+ def <T extends C<Number,Number>> T m(T t) {
+ return t
+ }
+ }
+ class B extends A {
+ @Override
+ def <T extends C<Number,Number>> T m(T t) {
+ T x = null; super.m(true ? t : x)
+ }
+ }
+ class C<X,Y> {
+ }
+ def x = new B().m(new C<>())
+ assert x instanceof C
+ '''
+ }
+
+ // GROOVY-10351
+ void testDiamondInferrenceFromConstructor24() {
+ assertScript '''
+ class C<T> {
+ C(T one, D<T,? extends T> two) {
+ }
+ }
+ class D<U,V> {
+ }
+ D<Integer,? extends Integer> d_of_i_and_i = null
+ C<Integer> c_of_i = new C<>(1,d_of_i_and_i) // 3 witnesses for T
+ '''
+ }
+
+ // GROOVY-10368
+ void testDiamondInferrenceFromConstructor25() {
+ ['T', 'T extends Number', 'T extends Object'].each {
+ assertScript """
+ class C<$it> {
+ C(p) {
+ }
+ }
+ void m(C<Integer> c) {
+ }
+ m(new C<>(null)) // Cannot call m(C<Integer>) with arguments [C<# extends Number>]
+ """
+ }
+ }
+
// GROOVY-10367
void testDiamondInferrenceFromConstructor26() {
assertScript '''
@@ -2056,17 +2230,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}
- // GROOVY-6232
- void testDiamond() {
- assertScript '''
- class Foo<T>{ Foo(T a, T b){} }
- def bar() {
- Foo<Object> f = new Foo<>("a",new Object())
- }
- bar()
- '''
- }
-
// GROOVY-6233
void testConstructorArgumentsAgainstGenerics() {
shouldFailWithMessages '''