You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by pa...@apache.org on 2026/05/02 05:47:50 UTC
(groovy) branch master updated: GROOVY-11770: StackOverflowError processing generics for kubernetes-client library (cycle guard instead of try/catch)
This is an automated email from the ASF dual-hosted git repository.
paulk-asert pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new 42b9d17edf GROOVY-11770: StackOverflowError processing generics for kubernetes-client library (cycle guard instead of try/catch)
42b9d17edf is described below
commit 42b9d17edfbccb8d8af261683c487a5fac0eeca9
Author: Paul King <pa...@asert.com.au>
AuthorDate: Tue Apr 28 16:25:01 2026 +1000
GROOVY-11770: StackOverflowError processing generics for kubernetes-client library (cycle guard instead of try/catch)
---
.../groovy/ast/tools/WideningCategories.java | 75 ++++++++-
.../groovy/transform/stc/GenericsSTCTest.groovy | 175 ++++++++++++---------
2 files changed, 169 insertions(+), 81 deletions(-)
diff --git a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
index 3c5ae2c807..1d5d38c25c 100644
--- a/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
+++ b/src/main/java/org/codehaus/groovy/ast/tools/WideningCategories.java
@@ -207,6 +207,10 @@ public class WideningCategories {
* @since 2.0.0
*/
public static ClassNode lowestUpperBound(final ClassNode a, final ClassNode b) {
+ return lowestUpperBound(new LowestUpperBoundContext(), a, b);
+ }
+
+ private static ClassNode lowestUpperBound(final LowestUpperBoundContext ctx, final ClassNode a, final ClassNode b) {
ClassNode lub = lowestUpperBound(a, b, null, null);
if (lub == null || !lub.isUsingGenerics()
|| lub.isGenericsPlaceHolder()) { // GROOVY-10330
@@ -222,20 +226,20 @@ public class WideningCategories {
// plus the interfaces
ClassNode superClass = lub.getSuperClass();
if (superClass.redirect().getGenericsTypes() != null) {
- superClass = parameterizeLowestUpperBound(superClass, a, b, lub);
+ superClass = parameterizeLowestUpperBound(ctx, superClass, a, b, lub);
}
ClassNode[] interfaces = lub.getInterfaces().clone();
for (int i = 0, n = interfaces.length; i < n; i += 1) {
ClassNode icn = interfaces[i];
if (icn.redirect().getGenericsTypes() != null) {
- interfaces[i] = parameterizeLowestUpperBound(icn, a, b, lub);
+ interfaces[i] = parameterizeLowestUpperBound(ctx, icn, a, b, lub);
}
}
return new LowestUpperBoundClassNode(lub.getUnresolvedName(), superClass, interfaces);
} else {
- return parameterizeLowestUpperBound(lub, a, b, lub);
+ return parameterizeLowestUpperBound(ctx, lub, a, b, lub);
}
}
@@ -246,13 +250,14 @@ public class WideningCategories {
*
* For example, if LUB is Set<T> and a is Set<String> and b is Set<StringBuffer>, this
* will return a LUB which parameterized type matches Set<? extends CharSequence>
+ * @param ctx tracks (t1, t2) pairs whose LUB is currently being computed, so this method can detect recursive calls (GROOVY-11770)
* @param lub the type to be parameterized
* @param a parameterized type a
* @param b parameterized type b
* @param fallback if we detect a recursive call, use this LUB as the parameterized type instead of computing a value
* @return the class node representing the parameterized lowest upper bound
*/
- private static ClassNode parameterizeLowestUpperBound(final ClassNode lub, final ClassNode a, final ClassNode b, final ClassNode fallback) {
+ private static ClassNode parameterizeLowestUpperBound(final LowestUpperBoundContext ctx, final ClassNode lub, final ClassNode a, final ClassNode b, final ClassNode fallback) {
if (a.toString(false).equals(b.toString(false))) return lub;
// a common super type exists, all we have to do is to parameterize
// it according to the types provided by the two class nodes
@@ -273,11 +278,16 @@ public class WideningCategories {
if (areEqualWithGenerics(t1, isPrimitiveType(a)?getWrapper(a):a) && areEqualWithGenerics(t2, isPrimitiveType(b)?getWrapper(b):b)) {
// "String implements Comparable<String>" and "StringBuffer implements Comparable<StringBuffer>"
basicType = fallback; // do not loop
+ } else if (ctx.isExpanding(t1, t2)) {
+ // GROOVY-11770: recursion guard for an already-expanding type pair, or depth cap reached
+ // (e.g. LUB(B, D) where B extends A<W<B>>, D extends A<W<D>>)
+ basicType = fallback;
} else {
+ ctx.enter(t1, t2);
try {
- basicType = lowestUpperBound(t1, t2);
- } catch (StackOverflowError ignore) {
- basicType = fallback; // best we can do for now
+ basicType = lowestUpperBound(ctx, t1, t2);
+ } finally {
+ ctx.exit(t1, t2);
}
}
if (agt[i].isWildcard() || bgt[i].isWildcard() || !t1.equals(t2)) {
@@ -289,6 +299,57 @@ public class WideningCategories {
return GenericsUtils.makeClassSafe0(lub, lubGTs);
}
+ /**
+ * Tracks pairs of types whose LUB is currently being computed by
+ * {@link #lowestUpperBound(ClassNode, ClassNode)}, so the recursion can
+ * break cycles caused by F-bounded type parameters that route a subtype
+ * back through itself (GROOVY-11770). Pair equality is identity-based and
+ * order-insensitive, since LUB is symmetric. A depth cap acts as a
+ * backstop in case generics rewriting yields fresh {@code ClassNode}
+ * instances for logically identical types, which would defeat identity
+ * tracking.
+ */
+ private static final class LowestUpperBoundContext {
+ private static final int MAX_DEPTH = 64;
+ private final Set<TypePair> inflight = new HashSet<>();
+ private int depth;
+
+ boolean isExpanding(final ClassNode a, final ClassNode b) {
+ return depth >= MAX_DEPTH || inflight.contains(new TypePair(a, b));
+ }
+
+ void enter(final ClassNode a, final ClassNode b) {
+ inflight.add(new TypePair(a, b));
+ depth++;
+ }
+
+ void exit(final ClassNode a, final ClassNode b) {
+ inflight.remove(new TypePair(a, b));
+ depth--;
+ }
+
+ private static final class TypePair {
+ final ClassNode a, b;
+
+ TypePair(final ClassNode a, final ClassNode b) {
+ this.a = a;
+ this.b = b;
+ }
+
+ @Override
+ public boolean equals(final Object o) {
+ if (!(o instanceof TypePair)) return false;
+ TypePair p = (TypePair) o;
+ return (a == p.a && b == p.b) || (a == p.b && b == p.a);
+ }
+
+ @Override
+ public int hashCode() {
+ return System.identityHashCode(a) ^ System.identityHashCode(b);
+ }
+ }
+ }
+
private static ClassNode findGenericsTypeHolderForClass(ClassNode source, final ClassNode target) {
if (isPrimitiveType(source)) source = getWrapper(source);
if (source.equals(target)) {
diff --git a/src/test/groovy/groovy/transform/stc/GenericsSTCTest.groovy b/src/test/groovy/groovy/transform/stc/GenericsSTCTest.groovy
index 59dceeb46d..4c7f9b8625 100644
--- a/src/test/groovy/groovy/transform/stc/GenericsSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/GenericsSTCTest.groovy
@@ -48,7 +48,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
Map<String,String> obj = new HashMap<String,Integer>()
''',
- 'Incompatible generic argument types. Cannot assign java.util.HashMap<java.lang.String, java.lang.Integer> to: java.util.Map<java.lang.String, java.lang.String>'
+ 'Incompatible generic argument types. Cannot assign java.util.HashMap<java.lang.String, java.lang.Integer> to: java.util.Map<java.lang.String, java.lang.String>'
}
@Test
@@ -57,7 +57,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
Map<? extends CharSequence,String> obj = new HashMap<String,Integer>()
''',
- 'Incompatible generic argument types. Cannot assign java.util.HashMap<java.lang.String, java.lang.Integer> to: java.util.Map<? extends java.lang.CharSequence, java.lang.String>'
+ 'Incompatible generic argument types. Cannot assign java.util.HashMap<java.lang.String, java.lang.Integer> to: java.util.Map<? extends java.lang.CharSequence, java.lang.String>'
}
@Test
@@ -87,7 +87,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
List<String> list = []
list.add(1)
''',
- 'Cannot call java.util.ArrayList#add(java.lang.String) with arguments [int]'
+ 'Cannot call java.util.ArrayList#add(java.lang.String) with arguments [int]'
}
// GROOVY-9074
@@ -97,13 +97,13 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
List<?> list = []
list.add(1)
''',
- 'Cannot call java.util.ArrayList#add(capture-of ?) with arguments [int]'
+ 'Cannot call java.util.ArrayList#add(capture-of ?) with arguments [int]'
shouldFailWithMessages '''
List<? extends Number> list = []
list.add(1)
''',
- 'Cannot call java.util.ArrayList#add(capture-of ? extends java.lang.Number) with arguments [int]'
+ 'Cannot call java.util.ArrayList#add(capture-of ? extends java.lang.Number) with arguments [int]'
}
@Test
@@ -125,7 +125,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
List<String> list = []
list << 1
''',
- 'Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#leftShift(java.util.List<T>, T) with arguments [java.util.ArrayList<java.lang.String>, int]'
+ 'Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#leftShift(java.util.List<T>, T) with arguments [java.util.ArrayList<java.lang.String>, int]'
}
@Test
@@ -134,7 +134,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
List<? extends Number> list = []
list << 1
''',
- 'Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#leftShift(java.util.List<T>, T) with arguments [java.util.ArrayList<? extends java.lang.Number>, int]'
+ 'Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#leftShift(java.util.List<T>, T) with arguments [java.util.ArrayList<? extends java.lang.Number>, int]'
}
@Test
@@ -176,7 +176,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
List<Integer> list = new LinkedList<>()
list.add 'Hello'
''',
- 'Cannot call java.util.LinkedList#add(java.lang.Integer) with arguments [java.lang.String]'
+ 'Cannot call java.util.LinkedList#add(java.lang.Integer) with arguments [java.lang.String]'
}
@Test
@@ -185,7 +185,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
List<Integer> list = new LinkedList<>()
list << 'Hello'
''',
- 'Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#leftShift(java.util.List<T>, T) with arguments [java.util.LinkedList<java.lang.Integer>, java.lang.String]'
+ 'Cannot call <T> org.codehaus.groovy.runtime.DefaultGroovyMethods#leftShift(java.util.List<T>, T) with arguments [java.util.LinkedList<java.lang.Integer>, java.lang.String]'
}
@Test
@@ -209,7 +209,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
int a = 42
def b = (a < new Object())
''',
- 'Cannot call java.lang.Integer#compareTo(java.lang.Integer) with arguments [java.lang.Object]'
+ 'Cannot call java.lang.Integer#compareTo(java.lang.Integer) with arguments [java.lang.Object]'
}
@Test
@@ -333,7 +333,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Number number = Optional.of(42).orElse(Double.NaN)
assert number.intValue() == 42
''',
- 'Cannot call java.util.Optional#orElse(java.lang.Integer) with arguments [double]'
+ 'Cannot call java.util.Optional#orElse(java.lang.Integer) with arguments [double]'
}
@Test
@@ -351,7 +351,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Number number = Optional.ofNullable((Integer) null).orElse(42d)
assert number.intValue() == 42
''',
- 'Cannot call java.util.Optional#orElse(java.lang.Integer) with arguments [double]'
+ 'Cannot call java.util.Optional#orElse(java.lang.Integer) with arguments [double]'
}
@Test
@@ -372,7 +372,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
return x // List<E>
}
''',
- 'Incompatible generic argument types.' // Cannot assign java.util.ArrayList<java.lang.Object> to: java.util.List<java.lang.String>
+ 'Incompatible generic argument types.'
+ // Cannot assign java.util.ArrayList<java.lang.Object> to: java.util.List<java.lang.String>
assertScript '''
@ASTTest(phase=INSTRUCTION_SELECTION, value={
@@ -642,18 +643,18 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
List<CharSequence> list = ['x'].collect()
''',
- 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
+ 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
shouldFailWithMessages '''
List<CharSequence> list = ['x'].stream().toList()
''',
- 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
+ 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
shouldFailWithMessages '''
import static java.util.stream.Collectors.toList
List<CharSequence> list = ['x'].stream().collect(toList())
''',
- 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
+ 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.String> to: java.util.List<java.lang.CharSequence>'
assertScript '''
import static java.util.stream.Collectors.toList
@@ -676,7 +677,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
chars()
}
''',
- 'Cannot return value of type java.lang.CharSequence for method returning java.util.List'
+ 'Cannot return value of type java.lang.CharSequence for method returning java.util.List'
}
// GROOVY-10098
@@ -722,7 +723,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
def mmap = (ListMultimap<String, Integer>) ArrayListMultimap.create()
Map<String, Set<Integer>> map = mmap.asMap()
''',
- 'Cannot assign java.util.Map<java.lang.String, java.util.Collection<java.lang.Integer>> to: java.util.Map<java.lang.String, java.util.Set<java.lang.Integer>>'
+ 'Cannot assign java.util.Map<java.lang.String, java.util.Collection<java.lang.Integer>> to: java.util.Map<java.lang.String, java.util.Set<java.lang.Integer>>'
}
// GROOVY-10120
@@ -736,7 +737,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
ListMultimap<String, Integer> mmap = ArrayListMultimap.create()
Map<String, Set<Integer>> map = mmap.asMap() // ArrayListMultimap#asMap() is bridge and lacks generics
''',
- 'Cannot assign java.util.Map<java.lang.String, java.util.Collection<java.lang.Integer>> to: java.util.Map<java.lang.String, java.util.Set<java.lang.Integer>>'
+ 'Cannot assign java.util.Map<java.lang.String, java.util.Collection<java.lang.Integer>> to: java.util.Map<java.lang.String, java.util.Set<java.lang.Integer>>'
}
// GROOVY-10222
@@ -804,7 +805,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
$type z = bar(foo(), 1)
""",
- 'Cannot assign value of type (java.io.Serializable & java.lang.Comparable',') to variable of type java.lang.' + type
+ 'Cannot assign value of type (java.io.Serializable & java.lang.Comparable', ') to variable of type java.lang.' + type
}
}
@@ -1234,12 +1235,12 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
A<String> ax = new C<>(new D())
''',
- 'Incompatible generic argument types. Cannot assign C<D> to: A<java.lang.String>'
+ 'Incompatible generic argument types. Cannot assign C<D> to: A<java.lang.String>'
shouldFailWithMessages '''
Set<List<String>> strings = new HashSet<>([new ArrayList<Number>()])
''',
- 'Incompatible generic argument types. Cannot assign java.util.HashSet<java.util.ArrayList<java.lang.Number>> to: java.util.Set<java.util.List<java.lang.String>>'
+ 'Incompatible generic argument types. Cannot assign java.util.HashSet<java.util.ArrayList<java.lang.Number>> to: java.util.Set<java.util.List<java.lang.String>>'
}
// GROOVY-9972
@@ -1331,7 +1332,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
cd.p.f.toLowerCase()
}
''',
- 'Incompatible generic argument types. Cannot assign C<? extends java.lang.Object> to: C<D>'
+ 'Incompatible generic argument types. Cannot assign C<? extends java.lang.Object> to: C<D>'
}
// GROOVY-9963
@@ -1385,7 +1386,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
m(0, new C<>(1))
''',
- 'Cannot call', 'm(int, C<D>[]) with arguments [int, C<java.lang.Integer>]'
+ 'Cannot call', 'm(int, C<D>[]) with arguments [int, C<java.lang.Integer>]'
}
// GROOVY-9970
@@ -1453,10 +1454,10 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
C.m(new A<>(new Object()))
C.m(true ? new A<>(new B()) : new A<>(new Object()))
''',
- 'Cannot assign A<java.lang.Object> to: A<B>',
- 'Cannot assign A<? extends java.lang.Object> to: A<B>',
- 'Cannot call C#m(A<B>) with arguments [A<java.lang.Object>]',
- 'Cannot call C#m(A<B>) with arguments [A<? extends java.lang.Object>]'
+ 'Cannot assign A<java.lang.Object> to: A<B>',
+ 'Cannot assign A<? extends java.lang.Object> to: A<B>',
+ 'Cannot call C#m(A<B>) with arguments [A<java.lang.Object>]',
+ 'Cannot call C#m(A<B>) with arguments [A<? extends java.lang.Object>]'
}
// GROOVY-10114
@@ -2059,7 +2060,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
List<String> list = new LinkedList<String>([1,2,3])
''',
- 'Cannot call java.util.LinkedList#<init>(java.util.Collection<? extends java.lang.String>) with arguments [java.util.ArrayList<java.lang.Integer>]'
+ 'Cannot call java.util.LinkedList#<init>(java.util.Collection<? extends java.lang.String>) with arguments [java.util.ArrayList<java.lang.Integer>]'
}
@Test
@@ -2074,7 +2075,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
List<Integer> list = new groovy.transform.stc.GenericsSTCTest.MyList()
''',
- 'Incompatible generic argument types'
+ 'Incompatible generic argument types'
}
@Test
@@ -2093,7 +2094,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
List<? super Number> list = ['string']
''',
- 'Cannot assign java.util.ArrayList<java.lang.String> to: java.util.List<? super java.lang.Number>'
+ 'Cannot assign java.util.ArrayList<java.lang.String> to: java.util.List<? super java.lang.Number>'
}
// GROOVY-10371
@@ -2108,7 +2109,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
B<? extends Number> x = new B<Double>()
A<Number> y = x
''',
- 'Incompatible generic argument types. Cannot assign B<java.lang.Double> to: A<java.lang.Number>'
+ 'Incompatible generic argument types. Cannot assign B<java.lang.Double> to: A<java.lang.Number>'
}
// GROOVY-9914, GROOVY-10036
@@ -2191,7 +2192,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
N n = m()
}
''',
- 'Cannot assign value of type java.lang.CharSequence to variable of type N'
+ 'Cannot assign value of type java.lang.CharSequence to variable of type N'
}
// GROOVY-10754
@@ -2371,7 +2372,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
new Foo()
''',
- 'Cannot find matching method FooWithGenerics#say(java.lang.Object)'
+ 'Cannot find matching method FooWithGenerics#say(java.lang.Object)'
}
// GROOVY-5237
@@ -2502,7 +2503,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
A<String, Integer> a = new B()
a.test()
''',
- '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>]'
+ '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>]'
}
@Test
@@ -2555,7 +2556,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
def map = new HashMap<String, Integer>()
map.put('hello', new Object())
''',
- 'Cannot call java.util.HashMap#put(java.lang.String, java.lang.Integer) with arguments [java.lang.String, java.lang.Object]'
+ 'Cannot call java.util.HashMap#put(java.lang.String, java.lang.Integer) with arguments [java.lang.String, java.lang.Object]'
}
@Test
@@ -2564,7 +2565,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
def map = new HashMap<Integer, Integer>()
map[123] = new Object()
''',
- 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.lang.Integer, java.lang.Integer>, int, java.lang.Object]'
+ 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.lang.Integer, java.lang.Integer>, int, java.lang.Object]'
}
// GROOVY-8788
@@ -2574,7 +2575,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
def map = new HashMap<String, Integer>()
map['x'] = new Object()
''',
- 'Cannot assign value of type java.lang.Object to variable of type java.lang.Integer'
+ 'Cannot assign value of type java.lang.Object to variable of type java.lang.Integer'
}
// GROOVY-9069
@@ -2590,8 +2591,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
''',
- 'Cannot call java.util.Map#put(java.lang.String, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) with arguments [java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>]',
- 'Cannot assign java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>> to: java.util.Map<java.lang.String, java.util.List<java.lang.String>>'
+ 'Cannot call java.util.Map#put(java.lang.String, java.util.Map<java.lang.String, java.util.List<java.lang.String>>) with arguments [java.lang.String, java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>>]',
+ 'Cannot assign java.util.LinkedHashMap<java.lang.String, java.util.List<ConfigAttribute>> to: java.util.Map<java.lang.String, java.util.List<java.lang.String>>'
assertScript '''
void test(Map<String, Map<String, List<String>>> maps) {
@@ -2686,8 +2687,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
new Test()
''',
- 'Cannot find matching method java.lang.Object#getAt(int)',
- 'Cannot find matching method java.lang.Object#toInteger()'
+ 'Cannot find matching method java.lang.Object#getAt(int)',
+ 'Cannot find matching method java.lang.Object#toInteger()'
}
@Test
@@ -2731,7 +2732,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
new ClassB()
''',
- 'Cannot call <X> groovy.transform.stc.GenericsSTCTest$ClassA#bar(java.lang.Class<java.lang.Long>) with arguments [java.lang.Class<? extends java.lang.Object>]'
+ 'Cannot call <X> groovy.transform.stc.GenericsSTCTest$ClassA#bar(java.lang.Class<java.lang.Long>) with arguments [java.lang.Class<? extends java.lang.Object>]'
}
// GROOVY-8961, GROOVY-9915
@@ -2759,7 +2760,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
x = Collections.<Integer>emptyList()
""",
- 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.Integer> to: java.util.List<java.lang.String>'
+ 'Incompatible generic argument types. Cannot assign java.util.List<java.lang.Integer> to: java.util.List<java.lang.String>'
}
}
@@ -2986,7 +2987,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
new D<Number>().p = 'x'
''',
- 'Cannot assign value of type java.lang.String to variable of type java.lang.Number'
+ 'Cannot assign value of type java.lang.String to variable of type java.lang.Number'
}
// GROOVY-10225
@@ -3094,7 +3095,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Collection<Integer> numbers = (Collection<Integer>) [1,2,3]
boolean r = list.addAll(numbers)
''',
- 'Cannot call java.util.ArrayList#addAll(java.util.Collection<? extends java.lang.String>) with arguments [java.util.Collection<java.lang.Integer>]'
+ 'Cannot call java.util.ArrayList#addAll(java.util.Collection<? extends java.lang.String>) with arguments [java.util.Collection<java.lang.Integer>]'
}
// GROOVY-5528
@@ -3128,7 +3129,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
})
List<String> list = ["foo", "$bar"]
''',
- 'You are trying to use a GString in place of a String'
+ 'You are trying to use a GString in place of a String'
shouldFailWithMessages base + '''
@ASTTest(phase=INSTRUCTION_SELECTION, value={
@@ -3137,13 +3138,13 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
})
List<String> list = ["$bar"]
''',
- 'You are trying to use a GString in place of a String'
+ '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'
+ 'You are trying to use a GString in place of a String'
}
// GROOVY-5559, GROOVY-10010
@@ -3154,19 +3155,19 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages base + '''
Map<String,String> map = [x:"foo", y:"$bar"]
''',
- 'You are trying to use a GString in place of a String'
+ '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'
+ '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'
+ 'You are trying to use a GString in place of a String'
}
// GROOVY-5559: related behaviour
@@ -3313,7 +3314,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shouldFailWithMessages '''
Map<String,Date> map = new HashMap<>() {}
''',
- 'Cannot use diamond <> with anonymous inner classes'
+ 'Cannot use diamond <> with anonymous inner classes'
}
// GROOVY-5614
@@ -3348,6 +3349,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
r.bindings2.put('b', 'B')
'''
}
+
@Test
void testInferDiamondForAssignment() {
assertScript '''
@@ -3366,6 +3368,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Map<String, String> map = new HashMap<>()
'''
}
+
@Test
void testInferDiamondForAssignmentWithDates() {
assertScript '''
@@ -3385,6 +3388,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Map<Date, Date> map = new HashMap<>()
'''
}
+
@Test
void testInferDiamondForAssignmentWithDatesAndIllegalKeyUsingPut() {
shouldFailWithMessages '''
@@ -3404,8 +3408,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Map<Date, Date> map = new HashMap<>()
map.put(123, new Date())
''',
- 'Cannot call java.util.HashMap#put(java.util.Date, java.util.Date) with arguments [int, java.util.Date]'
+ 'Cannot call java.util.HashMap#put(java.util.Date, java.util.Date) with arguments [int, java.util.Date]'
}
+
@Test
void testInferDiamondForAssignmentWithDatesAndIllegalKeyUsingSquareBracket() {
shouldFailWithMessages '''
@@ -3425,8 +3430,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Map<Date, Date> map = new HashMap<>()
map[123] = new Date()
''',
- 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.util.Date, java.util.Date>, int, java.util.Date]'
+ 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.util.Date, java.util.Date>, int, java.util.Date]'
}
+
@Test
void testInferDiamondForAssignmentWithDatesAndIllegalValueUsingPut() {
shouldFailWithMessages '''
@@ -3446,8 +3452,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Map<Date, Date> map = new HashMap<>()
map.put(new Date(), 'foo')
''',
- 'Cannot call java.util.HashMap#put(java.util.Date, java.util.Date) with arguments [java.util.Date, java.lang.String]'
+ 'Cannot call java.util.HashMap#put(java.util.Date, java.util.Date) with arguments [java.util.Date, java.lang.String]'
}
+
@Test
void testInferDiamondForAssignmentWithDatesAndIllegalValueUsingSquareBracket() {
shouldFailWithMessages '''
@@ -3467,7 +3474,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Map<Date, Date> map = new HashMap<>()
map[new Date()] = 'foo'
''',
- 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.util.Date, java.util.Date>, java.util.Date, java.lang.String]'
+ 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#putAt(java.util.Map<K, V>, K, V) with arguments [java.util.HashMap<java.util.Date, java.util.Date>, java.util.Date, java.lang.String]'
}
@Test
@@ -3826,7 +3833,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
def <T extends Number> T test(T one, T two) { }
test(1,"II")
''',
- '#test(int, java.lang.String). Please check if the declared type is correct and if the method exists.'
+ '#test(int, java.lang.String). Please check if the declared type is correct and if the method exists.'
}
// GROOVY-7720
@@ -3840,7 +3847,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
''',
- 'Cannot assign value of type U to variable of type T'
+ 'Cannot assign value of type U to variable of type T'
shouldFailWithMessages '''
class Converter<T, U> {
@@ -3849,7 +3856,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
''',
- 'Cannot return value of type T for method returning U'
+ 'Cannot return value of type T for method returning U'
shouldFailWithMessages '''
class C<X, Y> {
@@ -3858,7 +3865,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
new X[]{new C<Object, String>()}
}
''',
- 'Cannot convert from C<java.lang.Object, java.lang.String> to X'
+ 'Cannot convert from C<java.lang.Object, java.lang.String> to X'
}
// GROOVY-9902: incomplete generics should not stop type checking
@@ -3897,9 +3904,9 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
test(new Holder<Object>())
''',
- 'Cannot call TypedProperty#eq(java.lang.String) with arguments [groovy.lang.GString]',
- 'Cannot call TypedProperty#eq(java.lang.String) with arguments [int]',
- 'Cannot call TypedProperty#eq(java.lang.Number) with arguments [java.lang.String]'
+ 'Cannot call TypedProperty#eq(java.lang.String) with arguments [groovy.lang.GString]',
+ 'Cannot call TypedProperty#eq(java.lang.String) with arguments [int]',
+ 'Cannot call TypedProperty#eq(java.lang.Number) with arguments [java.lang.String]'
}
// GROOVY-5748
@@ -4162,8 +4169,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Map<String,?> getTwo() { [:] }
def one_plus_two = one + two // <K,V> Map<K,V> plus(Map<K,V> left, Map<K,V> right)
''',
- 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#plus(java.util.Map<K, V>, java.util.Map<K, V>)' +
- ' with arguments [java.util.LinkedHashMap<java.lang.String, ?>, java.util.Map<java.lang.String, ? extends java.lang.Object>]'
+ 'Cannot call <K,V> org.codehaus.groovy.runtime.DefaultGroovyMethods#plus(java.util.Map<K, V>, java.util.Map<K, V>)' +
+ ' with arguments [java.util.LinkedHashMap<java.lang.String, ?>, java.util.Map<java.lang.String, ? extends java.lang.Object>]'
}
@Test
@@ -4174,7 +4181,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
Foo.bar([new Object()])
''',
- 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.ArrayList<java.lang.Object>]'
+ 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.ArrayList<java.lang.Object>]'
}
@Test
@@ -4187,7 +4194,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Foo.bar(list)
}
''',
- 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
+ 'Cannot call <T extends java.util.List<? extends java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
}
// GROOVY-8084
@@ -4201,8 +4208,8 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
Foo.bar(new ArrayList<Integer>())
''',
- 'Cannot call java.util.List#add(capture-of ? extends java.io.Serializable) with arguments [java.lang.String]',
- 'Cannot find matching method java.lang.Class#bar(java.util.ArrayList<java.lang.Integer>) or static method Foo#bar(java.util.ArrayList<java.lang.Integer>)'
+ 'Cannot call java.util.List#add(capture-of ? extends java.io.Serializable) with arguments [java.lang.String]',
+ 'Cannot find matching method java.lang.Class#bar(java.util.ArrayList<java.lang.Integer>) or static method Foo#bar(java.util.ArrayList<java.lang.Integer>)'
}
// GROOVY-9074
@@ -4226,7 +4233,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
shapes.add(0, new Rectangle())
}
''',
- 'Cannot call java.util.List#add(int, capture-of ? extends Shape) with arguments [int, Rectangle]'
+ 'Cannot call java.util.List#add(int, capture-of ? extends Shape) with arguments [int, Rectangle]'
}
@Test
@@ -4265,7 +4272,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
Foo.bar(['abc'])
''',
- 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.ArrayList<java.lang.String>]'
+ 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [java.util.ArrayList<java.lang.String>]'
}
@Test
@@ -4278,7 +4285,7 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
Foo.bar(list)
}
''',
- 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
+ 'Cannot call <T extends java.util.List<? super java.lang.CharSequence>> Foo#bar(T) with arguments [U]'
}
// GROOVY-5721
@@ -4616,9 +4623,29 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
}
}
- // GROOVY-11022
+ // GROOVY-11770
@Test
void testNoStackOverflow3() {
+ // Cycle through a wrapper type: LUB(B, D) needs LUB(W<B>, W<D>),
+ // which needs LUB(B, D) again. Caught by the in-flight LUB pair guard.
+ assertScript '''
+ class A<T> {}
+ class W<T> {}
+ class B extends A<W<B>> {}
+ class D extends A<W<D>> {}
+
+ @groovy.transform.TypeChecked
+ def test(boolean f) {
+ def x = f ? new B() : new D()
+ x.class.name
+ }
+ assert test(true) == 'B'
+ '''
+ }
+
+ // GROOVY-11022
+ @Test
+ void testNoStackOverflow4() {
// F-bounded type parameter with self-reference inside a `? super` wildcard
assertScript '''
class C<K, V> {