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 3c87eb0ca1 GROOVY-11816: SC: set variable scope on spread-dot 
generated for loop
3c87eb0ca1 is described below

commit 3c87eb0ca13d2bb308b5435120fee3dbbbab95ff
Author: Eric Milles <[email protected]>
AuthorDate: Sun Dec 14 17:31:37 2025 -0600

    GROOVY-11816: SC: set variable scope on spread-dot generated for loop
---
 .../classgen/asm/sc/StaticInvocationWriter.java    | 17 +++--
 src/test/groovy/groovy/SpreadDotTest.groovy        | 84 +++++++++++-----------
 .../stc/ArraysAndCollectionsSTCTest.groovy         | 11 +++
 3 files changed, 64 insertions(+), 48 deletions(-)

diff --git 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
index f1486ffe7e..816df9717a 100644
--- 
a/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
+++ 
b/src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java
@@ -27,6 +27,7 @@ import org.codehaus.groovy.ast.GroovyCodeVisitor;
 import org.codehaus.groovy.ast.InnerClassNode;
 import org.codehaus.groovy.ast.MethodNode;
 import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.VariableScope;
 import org.codehaus.groovy.ast.decompiled.DecompiledClassNode;
 import org.codehaus.groovy.ast.expr.ArgumentListExpression;
 import org.codehaus.groovy.ast.expr.ArrayExpression;
@@ -520,6 +521,11 @@ public class StaticInvocationWriter extends 
InvocationWriter {
             ClassNode elementType = 
StaticTypeCheckingVisitor.inferLoopElementType(controller.getTypeChooser().resolveType(receiver,
 controller.getClassNode()));
             Parameter element = new Parameter(elementType, "for$it$" + 
labelCounter.incrementAndGet());
 
+            VariableScope varScope = new 
VariableScope(controller.getCompileStack().getScope());
+            
varScope.setInStaticContext(varScope.getParent().isInStaticContext());
+            element 
.setInStaticContext(varScope.getParent().isInStaticContext());
+            varScope.putDeclaredVariable(element);
+
             Expression nextValue;
             if (origin instanceof MethodCallExpression) {
                 MethodCallExpression oldMCE = (MethodCallExpression) origin;
@@ -550,13 +556,10 @@ public class StaticInvocationWriter extends 
InvocationWriter {
             addNextValue.setImplicitThis(false);
             
addNextValue.setMethodTarget(StaticCompilationVisitor.ARRAYLIST_ADD_METHOD);
 
-            // for (element in receiver) 
result.add(element?.method(arguments));
-            var stmt = new ForStatement(
-                    element,
-                    tmpReceiver,
-                    stmt(produceResultList ? addNextValue : nextValue)
-            );
-            stmt.visit(controller.getAcg());
+            // for (element in receiver) result.add(element?.method(arguments))
+            var loop = new ForStatement(element, tmpReceiver, 
stmt(produceResultList ? addNextValue : nextValue));
+            loop.setVariableScope(varScope); // GROOVY-11816
+            loop.visit(controller.getAcg());
             if (produceResultList) {
                 result.remove(controller);
             }
diff --git a/src/test/groovy/groovy/SpreadDotTest.groovy 
b/src/test/groovy/groovy/SpreadDotTest.groovy
index 164cb34b24..ac9a3d78da 100644
--- a/src/test/groovy/groovy/SpreadDotTest.groovy
+++ b/src/test/groovy/groovy/SpreadDotTest.groovy
@@ -18,68 +18,75 @@
  */
 package groovy
 
-import org.junit.Test
+import org.junit.jupiter.api.Test
 
-import static org.junit.Assert.assertEquals
+import static org.junit.jupiter.api.Assertions.assertEquals
 
 /**
- * Tests for the spread dot operator "*.".
- *
- * For an example,
- *          list*.property
- * means
- *          list.collect { it?.property }
+ * Tests for the spread dot operator. For example, {@code list*.name} works
+ * like: <pre>
+ * list.collect { it?.name }
+ * </pre>
  */
 final class SpreadDotTest {
 
     @Test
-    void testSpreadDot() {
-        def m1 = ["a": 1, "b": 2]
-        def m2 = ["a": 11, "b": 22]
-        def m3 = ["a": 111, "b": 222]
+    void testSpreadDot1() {
+        def map = [A: 'one', B: 'two', C: 'three']
+
+        assert map*.key == ['A', 'B', 'C']
+        assert map*.value*.size() == [3, 3, 5]
+        assert map.collect { entry -> entry.value.size() } == [3, 3, 5]
+    }
+
+    @Test
+    void testSpreadDot2() {
+        def m1 = [a:   1, b:   2]
+        def m2 = [a:  11, b:  22]
+        def m3 = [a: 111, b: 222]
+
         def x = [m1, m2, m3]
-        assert x*.a == [1, 11, 111]
-        assert x*."a" == [1, 11, 111]
         assert x == [m1, m2, m3]
+        assert x*.a == [1, 11, 111]
+        assert x*.'a' == [1, 11, 111]
 
-        def m4 = null
-        x << m4
-        assert x*.a == [1, 11, 111, null]
-        assert x*."a" == [1, 11, 111, null]
+        x << null
         assert x == [m1, m2, m3, null]
+        assert x*.a == [1, 11, 111, null]
+        assert x*.'a' == [1, 11, 111, null]
 
         Date checkDate = new Date()
         def d = new SpreadDotDemo()
         x << d
-        assert x*."a"[4] >= checkDate
+        assert x*.'a'[4] >= checkDate
         assert x == [m1, m2, m3, null, d]
 
         def y = new SpreadDotDemo2()
-        assert y."a" == 'Attribute Get a'
         assert y.a == 'Attribute Get a'
+        assert y.'a' == 'Attribute Get a'
 
         x << y
-        assert x*."a"[5] == 'Attribute Get a'
         assert x == [m1, m2, m3, null, d, y]
+        assert x*.'a'[5] == 'Attribute Get a'
     }
 
     @Test
-    void testSpreadDot2() {
+    void testSpreadDot3() {
         def a = new SpreadDotDemo()
         def b = new SpreadDotDemo2()
         def x = [a, b]
 
-        assert x*.fnB("1") == [a.fnB("1"), b.fnB("1")]
+        assert x*.fnB('1') == [a.fnB('1'), b.fnB('1')]
         assert [a, b]*.fnB() == [a.fnB(), b.fnB()]
     }
 
     @Test
-    void testSpreadDotArrays() {
+    void testSpreadDotArrays1() {
         def a = new SpreadDotDemo()
         def b = new SpreadDotDemo2()
         Object[] x = [a, b]
 
-        assert x*.fnB("1") == [a.fnB("1"), b.fnB("1")]
+        assert x*.fnB('1') == [a.fnB('1'), b.fnB('1')]
         assert [a, b]*.fnB() == [a.fnB(), b.fnB()]
 
         int[] nums = [3, 4, 5]
@@ -98,9 +105,9 @@ final class SpreadDotTest {
 
         books*.metaClass*.foo = { "Hello, 
${delegate.class.simpleName}".toString() }
 
-        assertEquals("Hello, Book1", new Book1().foo())
-        assertEquals("Hello, Book2", new Book2().foo())
-        assertEquals("Hello, Book3", new Book3().foo())
+        assertEquals('Hello, Book1', new Book1().foo())
+        assertEquals('Hello, Book2', new Book2().foo())
+        assertEquals('Hello, Book3', new Book3().foo())
     }
 
     @Test
@@ -116,14 +123,6 @@ final class SpreadDotTest {
         assertEquals(['Large'], new Shirt()*.size())
     }
 
-    @Test
-    void testSpreadDotMap() {
-        def map = [A: "one", B: "two", C: "three"]
-        assert map.collect { child -> child.value.size() } == [3, 3, 5]
-        assert map*.value*.size() == [3, 3, 5]
-        assert map*.getKey() == ['A', 'B', 'C']
-    }
-
     @Test
     void testSpreadDotAttribute() {
         def s = new Singlet()
@@ -160,7 +159,7 @@ final class SpreadDotTest {
         }
 
         String fnB() {
-            return "bb"
+            return 'bb'
         }
 
         String fnB(String m) {
@@ -178,7 +177,7 @@ final class SpreadDotTest {
         }
 
         String fnB() {
-            return "cc"
+            return 'cc'
         }
 
         String fnB(String m) {
@@ -186,11 +185,14 @@ final class SpreadDotTest {
         }
     }
 
-    static class Book1 {}
+    static class Book1 {
+    }
 
-    static class Book2 {}
+    static class Book2 {
+    }
 
-    static class Book3 {}
+    static class Book3 {
+    }
 
     static class Shirt {
         def size() { 'Large' }
diff --git 
a/src/test/groovy/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy 
b/src/test/groovy/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
index 3bc268d00c..4ece87fab2 100644
--- a/src/test/groovy/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy
@@ -339,6 +339,17 @@ class ArraysAndCollectionsSTCTest extends 
StaticTypeCheckingTestCase {
             def listClass = list.class
             assert listClass == ArrayList
         '''
+
+        // GROOVY-11816
+        assertScript '''
+            class C {
+                List<Class> classes
+                void test() {
+                    def names = classes*.simpleName
+                }
+            }
+            new C().test()
+        '''
     }
 
     void testListStarMethod() {

Reply via email to