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

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


The following commit(s) were added to refs/heads/GROOVY_5_0_X by this push:
     new 7e37003e27 GROOVY-11805: parameterized type initialization array guard
7e37003e27 is described below

commit 7e37003e2733245650cd4f62ba8ad8b8e1e18888
Author: Eric Milles <[email protected]>
AuthorDate: Fri Nov 21 10:39:14 2025 -0600

    GROOVY-11805: parameterized type initialization array guard
---
 .../org/codehaus/groovy/vmplugin/v8/Java8.java     |  19 +-
 .../groovy/gls/generics/GenericsUsageTest.groovy   | 301 ++++++++++++---------
 2 files changed, 179 insertions(+), 141 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java 
b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
index 9a6e8bb0e6..18315879f5 100644
--- a/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
+++ b/src/main/java/org/codehaus/groovy/vmplugin/v8/Java8.java
@@ -214,18 +214,19 @@ public class Java8 implements VMPlugin {
     }
 
     private ClassNode configureParameterizedType(final ParameterizedType 
parameterizedType) {
-        ClassNode base = configureType(parameterizedType.getRawType());
-        GenericsType[] gts = 
configureTypeArguments(parameterizedType.getActualTypeArguments());
+        ClassNode type = configureType(parameterizedType.getRawType());
+        GenericsType[] tas = 
configureTypeArguments(parameterizedType.getActualTypeArguments());
         // fix erasure : ResolveVisitor#resolveWildcardBounding
-        final int n; if (gts != null && (n = gts.length) > 0) {
-            for (int i = 0; i < n; i += 1) { GenericsType gt = gts[i];
-                if (!gt.isWildcard() || gt.getUpperBounds() != null) continue;
-                ClassNode[] ubs = 
base.redirect().getGenericsTypes()[i].getUpperBounds();
-                if (ubs != null && !ClassHelper.OBJECT_TYPE.equals(ubs[0])) 
gt.getType().setRedirect(ubs[0]);
+        final int n; if (tas != null && (n = tas.length) > 0) {
+            GenericsType[] tps = type.redirect().getGenericsTypes();
+            for (int i = 0; i < n; i += 1) { GenericsType ta = tas[i];
+                if (!ta.isWildcard() || ta.getUpperBounds() != null) continue;
+                ClassNode[] ubs = (tps != null && tps.length > i ? 
tps[i].getUpperBounds() : null);
+                if (ubs != null && !ClassHelper.isObjectType(ubs[0])) 
ta.getType().setRedirect(ubs[0]);
             }
         }
-        base.setGenericsTypes(gts);
-        return base;
+        type.setGenericsTypes(tas);
+        return type;
     }
 
     private GenericsType[] configureTypeArguments(final Type[] ta) {
diff --git a/src/test/groovy/gls/generics/GenericsUsageTest.groovy 
b/src/test/groovy/gls/generics/GenericsUsageTest.groovy
index c743ee41a2..2792c2bf61 100644
--- a/src/test/groovy/gls/generics/GenericsUsageTest.groovy
+++ b/src/test/groovy/gls/generics/GenericsUsageTest.groovy
@@ -18,29 +18,35 @@
  */
 package gls.generics
 
-import gls.CompilableTestSupport
+import org.codehaus.groovy.control.CompilationFailedException
 import org.codehaus.groovy.control.MultipleCompilationErrorsException
+import org.junit.jupiter.api.Test
 
-final class GenericsUsageTest extends CompilableTestSupport {
+import static groovy.test.GroovyAssert.assertScript
+import static groovy.test.GroovyAssert.shouldFail
 
+final class GenericsUsageTest {
+
+    @Test
     void testInvalidParameterUsage() {
-        shouldNotCompile """
+        shouldFail CompilationFailedException, '''
             abstract class B<T> implements Map<T>{}
-        """
-        shouldNotCompile """
+        '''
+        shouldFail CompilationFailedException, '''
             class A<T,V> extends ArrayList<T,V>{}
-        """
-        shouldNotCompile """
+        '''
+        shouldFail CompilationFailedException, '''
             class A<T extends Number> {}
             class B<T> extends A<T>{}
-        """
-        shouldNotCompile """
+        '''
+        shouldFail CompilationFailedException, '''
             class B<T> extends ArrayList<?>{}
-        """
+        '''
     }
 
+    @Test
     void testCovariantReturn() {
-        shouldNotCompile '''
+        shouldFail CompilationFailedException, '''
             class A<T> {
                 T foo(T t) { 1 }
             }
@@ -49,24 +55,30 @@ final class GenericsUsageTest extends CompilableTestSupport 
{
                 String foo(Long l) { '2' }
             }
         '''
+    }
 
-        // GROOVY-6977
+    // GROOVY-6977
+    @Test
+    void testCovariantReturn2() {
         assertScript '''
             class A {
-              public <R> List<R> foo() {
-                List<R> list = new ArrayList<R>() {
+                public <R> List<R> foo() {
+                    List<R> list = new ArrayList<R>() {
+                        // ...
+                    }
                     // ...
+                    list
                 }
-                // ...
-                list
-              }
             }
 
             def longList = new A().<Long>foo()
             assert longList != null
             assert longList.empty
         '''
+    }
 
+    @Test
+    void testCovariantReturn3() {
         assertScript '''
             class A<T> {
                 T foo(T t) {1}
@@ -87,8 +99,9 @@ final class GenericsUsageTest extends CompilableTestSupport {
         '''
     }
 
+    @Test
     void testCovariantReturnWithInterface() {
-        assertScript """
+        assertScript '''
             import java.util.concurrent.*
 
             class CallableTask implements Callable<String> {
@@ -97,57 +110,63 @@ final class GenericsUsageTest extends 
CompilableTestSupport {
 
             def task = new CallableTask()
             assert task.call() == "x"
-        """
+        '''
     }
 
+    @Test
     void testCovariantReturnWithEmptyAbstractClassesInBetween() {
-        assertScript """
-        import java.util.concurrent.*;
+        assertScript '''
+            import java.util.concurrent.*
 
-        abstract class AbstractCallableTask<T> implements Callable<T> { }
-        abstract class SubclassCallableTask<T> extends AbstractCallableTask<T> 
{ }
-        class CallableTask extends SubclassCallableTask<String> {
-            String call() { return "x"; }
-        }
-        assert "x" == new CallableTask().call();
-      """
+            abstract class AbstractCallableTask<T> implements Callable<T> { }
+            abstract class SubclassCallableTask<T> extends 
AbstractCallableTask<T> { }
+            class CallableTask extends SubclassCallableTask<String> {
+                String call() { return "x" }
+            }
+
+            assert "x" == new CallableTask().call()
+        '''
     }
 
+    @Test
     void testGenericsDiamondShortcutSimple() {
-        assertScript """
-            List<List<String>> list1 = new ArrayList<>()
-            assert list1.size() == 0
-        """
+        assertScript '''
+            List<List<String>> list = new ArrayList<>()
+            assert list.size() == 0
+        '''
     }
 
+    @Test
     void testGenericsDiamondShortcutComplex() {
-        assertScript """
-            List<List<List<List<List<String>>>>> list2 = new ArrayList<>()
-            assert list2.size() == 0
-        """
+        assertScript '''
+            List<List<List<List<List<String>>>>> list = new ArrayList<>()
+            assert list.size() == 0
+        '''
     }
 
+    @Test
     void testGenericsDiamondShortcutMethodCall() {
-        assertScript """
-            def method(List<List<String>> list3) {
-              list3.size()
+        assertScript '''
+            def method(List<List<String>> list) {
+                list.size()
             }
 
             assert method(new ArrayList<>()) == 0
-        """
+        '''
     }
 
+    @Test
     void testGenericsDiamondShortcutIllegalPosition() {
-        shouldFailCompilationWithAnyMessage '''
-            List<> list4 = []
-        ''', ['unexpected token: <', 'Unexpected input: \'List<>\'']
+        shouldFailCompilationWithMessage 'List<> list;', 'Unexpected input: 
\'List<>\''
     }
 
+    // GROOVY-2725
+    @Test
     void testGenericsInAsType() {
         // this is to ensure no regression to GROOVY-2725 will happen
         // "as ThreadLocal<Integer>\n" did not compile because the nls
         // was swallowed and could not be used to end the expression
-        assertScript """
+        assertScript '''
             import java.util.concurrent.atomic.AtomicInteger
 
             class ThreadId {
@@ -165,49 +184,51 @@ final class GenericsUsageTest extends 
CompilableTestSupport {
             // we do not actually want to execute something, just
             // ensure this compiles, so we do a dummy command here
             assert ThreadId != null
-        """
+        '''
     }
 
+    @Test
     void testCompilationWithMissingClosingBracketsInGenerics() {
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             def list1 = new ArrayList<Integer()
-        """, "Unexpected input: '('"
+        ''', "Unexpected input: '('"
 
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             List<Integer list2 = new ArrayList<Integer>()
-        """, "Unexpected input: 'List<Integer'"
+        ''', "Unexpected input: 'List<Integer'"
 
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             def c = []
             for (Iterator<String i = c.iterator(); i.hasNext(); ) { }
-        """, "Unexpected input: 'Iterator<String i'"
+        ''', "Unexpected input: 'Iterator<String i'"
 
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             def m(Class<Integer someParam) {}
-        """, "Unexpected input: 'Class<Integer someParam'"
+        ''', "Unexpected input: 'Class<Integer someParam'"
 
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             abstract class ArrayList1<E extends AbstractList<E> implements 
List<E> {}
-        """, "Unexpected input: 'implements'"
+        ''', "Unexpected input: 'implements'"
 
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             abstract class ArrayList2<E> extends AbstractList<E implements 
List<E> {}
-        """, "Unexpected input: '<'"
+        ''', "Unexpected input: '<'"
 
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             abstract class ArrayList3<E> extends AbstractList<E> implements 
List<E {}
-        """, "Unexpected input: '<'"
+        ''', "Unexpected input: '<'"
 
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             def List<List<Integer> history = new ArrayList<List<Integer>>()
-        """, "Unexpected input: 'def List<List<Integer> history'"
+        ''', "Unexpected input: 'def List<List<Integer> history'"
 
-        shouldFailCompilationWithMessage """
+        shouldFailCompilationWithMessage '''
             def List<List<Integer>> history = new ArrayList<List<Integer>()
-        """, "Unexpected input: '('"
+        ''', "Unexpected input: '('"
     }
 
     // GROOVY-3975
+    @Test
     void testGenericsForClosureParameters() {
         def cl = { List<String> s -> }
 
@@ -219,6 +240,7 @@ final class GenericsUsageTest extends CompilableTestSupport 
{
     }
 
     // GROOVY-4974
+    @Test
     void testBoundedGenericsWithInheritance() {
         assertScript '''
             class TestGenerics {
@@ -242,30 +264,57 @@ final class GenericsUsageTest extends 
CompilableTestSupport {
         '''
     }
 
+    // GROOVY-11805
+    @Test
+    void testSelfReferentialTypeVariableWildcard() {
+        assertScript '''
+            interface I<T extends I<?>> {
+                void proc()
+            }
+            class C implements I<C> {
+                @Override
+                void proc() {
+                }
+            }
+            I<?> func(Closure cl) {
+                (new C()).tap(cl)
+            }
+
+            func {
+                proc()
+            }
+
+            def classLoader = new GroovyClassLoader(this.class.classLoader)
+            def shell = new GroovyShell(classLoader)
+            shell.parse("import I; new C()").run()
+        '''
+    }
+
     // GROOVY-3731, GROOVY-7865, GROOVY-10033
+    @Test
     void testFriendlyErrorMessageForGenericsErrors() {
         // superclass and interfaces
-        shouldFailCompilationWithMessages '''
+        shouldFailCompilationWithMessage '''
             class C extends ArrayList<> { }
-        ''', ["Unexpected input: '<'"]
-        shouldFailCompilationWithMessages '''
+        ''', "Unexpected input: '<'"
+        shouldFailCompilationWithMessage '''
             class C extends ArrayList<? extends Number> { }
-        ''', ['A supertype may not specify a wildcard type']
+        ''', 'A supertype may not specify a wildcard type'
         shouldFailCompilationWithMessages '''
             class C extends ArrayList<String, String> { }
-        ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+        ''', '(supplied with 2 type parameters)', 'which takes 1 parameter'
         shouldFailCompilationWithMessages '''
             class C extends HashMap<String> { }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
-        shouldFailCompilationWithMessages '''
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
+        shouldFailCompilationWithMessage '''
             class C implements Map<> { }
-        ''', ["Unexpected input: '<'"]
+        ''', "Unexpected input: '<'"
         shouldFailCompilationWithMessages '''
             class MyMap implements Map<String> { }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             class C implements List<String, String> { }
-        ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+        ''', '(supplied with 2 type parameters)', 'which takes 1 parameter'
 
         // constructor call
         assertScript '''
@@ -276,103 +325,104 @@ final class GenericsUsageTest extends 
CompilableTestSupport {
         ''', 'Cannot use diamond <> with anonymous inner classes'
         shouldFailCompilationWithMessages '''
             new LinkedList<Integer, String>()
-        ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+        ''', '(supplied with 2 type parameters)', 'which takes 1 parameter'
         shouldFailCompilationWithMessages '''
             new LinkedList<String, String>()
-        ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+        ''', '(supplied with 2 type parameters)', 'which takes 1 parameter'
         shouldFailCompilationWithMessages '''
             new LinkedList<String, String>() { }
-        ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+        ''', '(supplied with 2 type parameters)', 'which takes 1 parameter'
         shouldFailCompilationWithMessages '''
             new Date<Calendar>()
-        ''', ['(supplied with 1 type parameter)', 'which takes no parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes no parameters'
 
         // constructor declaration
         shouldFailCompilationWithMessages '''
             class C { C(Map<String> m) { } }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             class C { C(Closure<String,Number> c) { } }
-        ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+        ''', '(supplied with 2 type parameters)', 'which takes 1 parameter'
 
         // method declaration
         shouldFailCompilationWithMessages '''
             def method(Map<String> map) { }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             Map<String> method() { }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             def method(Map<String, Map<String>> map) { }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             def method(Map<String, Map<String>> map) { }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
 
         // field declaration
         shouldFailCompilationWithMessages '''
             class C { Map<String> map }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             class C { Map<String, Map<String>> map }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
 
         // variable declaration
         shouldFailCompilationWithMessages '''
             def method() { Map<String> map }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             def method() { Map<String, Map<String>> map }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             def (Map<String,String> one, Map<String> two) = [ [:], [:] ]
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             Map<String>[][] array = new Map[0][0]
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             Map<String,String>[][] array = new Map<String>[0][]
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             class C { { Map<String> m = null } }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             class C { static { Map<String> m = null } }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             @groovy.transform.ASTTest(value={
                 Map<String> m = null
             })
             class C { }
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
 
         // casting and coercion
         shouldFailCompilationWithMessages '''
             def map = (Map<String>) null
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             def map = null as Map<String>
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
     }
 
     // GROOVY-5441
+    @Test
     void testCompilationErrorForMismatchedGenericsWithQualifiedTypes() {
         shouldFailCompilationWithMessages '''
             groovy.lang.Tuple2<Object> tuple
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             java.util.List<Object,Object> list
-        ''', ['(supplied with 2 type parameters)', 'which takes 1 parameter']
+        ''', '(supplied with 2 type parameters)', 'which takes 1 parameter'
         shouldFailCompilationWithMessages '''
             java.util.Map<Object,Object,Object> map
-        ''', ['(supplied with 3 type parameters)', 'which takes 2 parameters']
+        ''', '(supplied with 3 type parameters)', 'which takes 2 parameters'
         shouldFailCompilationWithMessages '''
             def (java.util.Map<Object> x, java.util.List<Object,Object> y) = 
[null,null]
-        ''', ['(supplied with 1 type parameter)', 'which takes 2 parameters',
-              '(supplied with 2 type parameters)', 'which takes 1 parameter']
+        ''', '(supplied with 1 type parameter)', 'which takes 2 parameters', 
'(supplied with 2 type parameters)', 'which takes 1 parameter'
     }
 
     // GROOVY-8990
+    @Test
     void testCompilationErrorForMismatchedGenericsWithMultipleBounds() {
         shouldFailCompilationWithMessages '''
             class C1<T> {}
@@ -399,50 +449,37 @@ final class GenericsUsageTest extends 
CompilableTestSupport {
             interface I2<T extends Number> {}
             class C12 implements I2<String> {} // String not a Number
             class C13 implements I2<C10> {} // C10 is a Number
-        ''', [
-                'The type String is not a valid substitute for the bounded 
parameter <T extends I1>',
-                'The type C2 is not a valid substitute for the bounded 
parameter <T extends java.lang.Number & I1>',
-                'The type Integer is not a valid substitute for the bounded 
parameter <T extends java.lang.Number & I1>',
-                'The type String is not a valid substitute for the bounded 
parameter <T extends java.lang.Number>'
-        ]
+        ''',
+        'The type String is not a valid substitute for the bounded parameter 
<T extends I1>',
+        'The type C2 is not a valid substitute for the bounded parameter <T 
extends java.lang.Number & I1>',
+        'The type Integer is not a valid substitute for the bounded parameter 
<T extends java.lang.Number & I1>',
+        'The type String is not a valid substitute for the bounded parameter 
<T extends java.lang.Number>'
     }
 
     
//--------------------------------------------------------------------------
 
-    private void shouldFailCompilationWithDefaultMessage(scriptText) {
-        shouldFailCompilationWithMessage scriptText, "Missing closing bracket 
'>' for generics types"
-    }
-
     private void shouldFailCompilationWithMessage(scriptText, String 
errorMessage) {
-        shouldFailCompilationWithMessages(scriptText, [errorMessage])
+        def mcee = shouldFail(MultipleCompilationErrorsException, scriptText)
+        def text = mcee.toString()
+        assert text.contains(errorMessage)
     }
 
-    private void shouldFailCompilationWithMessages(scriptText, List<String> 
errorMessages) {
-        try {
-            assertScript scriptText
-            fail("The script compilation should have failed as it contains 
generics errors, e.g. mis-matching generic brackets")
-        } catch (MultipleCompilationErrorsException mcee) {
-            def text = mcee.toString()
-            errorMessages.each {
-                assert text.contains(it)
-            }
+    private void shouldFailCompilationWithMessages(scriptText, String... 
errorMessages) {
+        def mcee = shouldFail(MultipleCompilationErrorsException, scriptText)
+        def text = mcee.toString()
+        for (errorMessage in errorMessages) {
+            assert text.contains(errorMessage)
         }
     }
 
     private void shouldFailCompilationWithAnyMessage(scriptText, List<String> 
errorMessages) {
-        try {
-            assertScript scriptText
-            fail("The script compilation should have failed as it contains 
generics errors, e.g. mis-matching generic brackets")
-        } catch (MultipleCompilationErrorsException mcee) {
-            def text = mcee.toString()
-
-            for (errorMessage in errorMessages) {
-                if (text.contains(errorMessage)) {
-                    return
-                }
+        def mcee = shouldFail(MultipleCompilationErrorsException, scriptText)
+        def text = mcee.toString()
+        for (errorMessage in errorMessages) {
+            if (text.contains(errorMessage)) {
+                return
             }
-
-            assert false, text + " can not match any expected error message: " 
+ errorMessages
         }
+        assert false, "$text can not match any expected error message: 
$errorMessages"
     }
 }

Reply via email to