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 5e82e1e42c GROOVY-11711: restore outer class generics after inner 
class visit
5e82e1e42c is described below

commit 5e82e1e42c806623bdcc7c6aa5a4391ecc256718
Author: Eric Milles <[email protected]>
AuthorDate: Tue Jul 8 08:40:32 2025 -0500

    GROOVY-11711: restore outer class generics after inner class visit
    
    3_0_X backport
---
 .../codehaus/groovy/control/ResolveVisitor.java    | 26 ++++++++++++----------
 src/test/gls/innerClass/InnerClassTest.groovy      | 21 ++++++++++++++++-
 2 files changed, 34 insertions(+), 13 deletions(-)

diff --git a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java 
b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
index e440d4ee8c..541cde99b8 100644
--- a/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
+++ b/src/main/java/org/codehaus/groovy/control/ResolveVisitor.java
@@ -1408,24 +1408,23 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
 
     @Override
     public void visitClass(final ClassNode node) {
-        ClassNode oldNode = currentClass;
-
-        currentClass = node;
-
+        ClassNode oldNode = currentClass; currentClass = node;
+        Map<GenericsTypeName, GenericsType> outerNames = null;
         if (node instanceof InnerClassNode) {
-            if (Modifier.isStatic(node.getModifiers())) {
-                genericParameterNames = new HashMap<>();
+            outerNames = genericParameterNames;
+            genericParameterNames = new HashMap<>();
+            if (!Modifier.isStatic(node.getModifiers())) {
+                genericParameterNames.putAll(outerNames); // outer names 
visible
             }
-
-            InnerClassNode innerClassNode = (InnerClassNode) node;
-            if (innerClassNode.isAnonymous()) {
-                MethodNode enclosingMethod = 
innerClassNode.getEnclosingMethod();
-                if (null != enclosingMethod) {
+            InnerClassNode innerClass = (InnerClassNode) node;
+            if (innerClass.isAnonymous()) {
+                MethodNode enclosingMethod = innerClass.getEnclosingMethod();
+                if (enclosingMethod != null) {
                     resolveGenericsHeader(enclosingMethod.getGenericsTypes());
                 }
             }
         } else {
-            genericParameterNames = new HashMap<>();
+            genericParameterNames.clear(); // outer class: new generic 
namespace
         }
 
         resolveGenericsHeader(node.getGenericsTypes());
@@ -1493,6 +1492,9 @@ public class ResolveVisitor extends 
ClassCodeExpressionTransformer {
 
         resolveOuterNestedClassFurther(node);
 
+        if (outerNames != null) // GROOVY-11711
+            genericParameterNames = outerNames;
+
         currentClass = oldNode;
     }
 
diff --git a/src/test/gls/innerClass/InnerClassTest.groovy 
b/src/test/gls/innerClass/InnerClassTest.groovy
index 74dbf5c20b..42d2cf60a0 100644
--- a/src/test/gls/innerClass/InnerClassTest.groovy
+++ b/src/test/gls/innerClass/InnerClassTest.groovy
@@ -1133,12 +1133,31 @@ final class InnerClassTest {
         assert err =~ /No enclosing instance passed in constructor call of a 
non-static inner class/
     }
 
+    // GROOVY-11711
+    @Test
+    void testUsageOfOuterType6() {
+        assertScript '''
+            class Foo<T> {
+                static class Bar {
+                }
+                /*non-static*/ class Baz
+                    implements java.util.concurrent.Callable<T> {
+                    T call() {
+                    }
+                }
+            }
+            def foo = new Foo<Short>()
+            def baz = new Foo.Baz(foo)
+            assert baz.call() == null
+        '''
+    }
+
     @Test
     void testClassOutputOrdering() {
         // this does actually not do much, but before this
         // change the inner class was tried to be executed
         // because a class ordering bug. The main method
-        // makes the Foo class executeable, but Foo$Bar is
+        // makes the Foo class executable, but Foo$Bar is
         // not. So if Foo$Bar is returned, asserScript will
         // fail. If Foo is returned, asserScript will not
         // fail.

Reply via email to