Skip to content

Commit c46a124

Browse files
committed
GROOVY-11816: SC: set variable scope on spread-dot generated for loop
1 parent cc8bc3e commit c46a124

3 files changed

Lines changed: 64 additions & 48 deletions

File tree

src/main/java/org/codehaus/groovy/classgen/asm/sc/StaticInvocationWriter.java

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import org.codehaus.groovy.ast.InnerClassNode;
2828
import org.codehaus.groovy.ast.MethodNode;
2929
import org.codehaus.groovy.ast.Parameter;
30+
import org.codehaus.groovy.ast.VariableScope;
3031
import org.codehaus.groovy.ast.decompiled.DecompiledClassNode;
3132
import org.codehaus.groovy.ast.expr.ArgumentListExpression;
3233
import org.codehaus.groovy.ast.expr.ArrayExpression;
@@ -520,6 +521,11 @@ public void makeCall(final Expression origin, final Expression receiver, final E
520521
ClassNode elementType = StaticTypeCheckingVisitor.inferLoopElementType(controller.getTypeChooser().resolveType(receiver, controller.getClassNode()));
521522
Parameter element = new Parameter(elementType, "for$it$" + labelCounter.incrementAndGet());
522523

524+
VariableScope varScope = new VariableScope(controller.getCompileStack().getScope());
525+
varScope.setInStaticContext(varScope.getParent().isInStaticContext());
526+
element .setInStaticContext(varScope.getParent().isInStaticContext());
527+
varScope.putDeclaredVariable(element);
528+
523529
Expression nextValue;
524530
if (origin instanceof MethodCallExpression) {
525531
MethodCallExpression oldMCE = (MethodCallExpression) origin;
@@ -550,13 +556,10 @@ public void makeCall(final Expression origin, final Expression receiver, final E
550556
addNextValue.setImplicitThis(false);
551557
addNextValue.setMethodTarget(StaticCompilationVisitor.ARRAYLIST_ADD_METHOD);
552558

553-
// for (element in receiver) result.add(element?.method(arguments));
554-
var stmt = new ForStatement(
555-
element,
556-
tmpReceiver,
557-
stmt(produceResultList ? addNextValue : nextValue)
558-
);
559-
stmt.visit(controller.getAcg());
559+
// for (element in receiver) result.add(element?.method(arguments))
560+
var loop = new ForStatement(element, tmpReceiver, stmt(produceResultList ? addNextValue : nextValue));
561+
loop.setVariableScope(varScope); // GROOVY-11816
562+
loop.visit(controller.getAcg());
560563
if (produceResultList) {
561564
result.remove(controller);
562565
}

src/test/groovy/groovy/SpreadDotTest.groovy

Lines changed: 43 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -18,68 +18,75 @@
1818
*/
1919
package groovy
2020

21-
import org.junit.Test
21+
import org.junit.jupiter.api.Test
2222

23-
import static org.junit.Assert.assertEquals
23+
import static org.junit.jupiter.api.Assertions.assertEquals
2424

2525
/**
26-
* Tests for the spread dot operator "*.".
27-
*
28-
* For an example,
29-
* list*.property
30-
* means
31-
* list.collect { it?.property }
26+
* Tests for the spread dot operator. For example, {@code list*.name} works
27+
* like: <pre>
28+
* list.collect { it?.name }
29+
* </pre>
3230
*/
3331
final class SpreadDotTest {
3432

3533
@Test
36-
void testSpreadDot() {
37-
def m1 = ["a": 1, "b": 2]
38-
def m2 = ["a": 11, "b": 22]
39-
def m3 = ["a": 111, "b": 222]
34+
void testSpreadDot1() {
35+
def map = [A: 'one', B: 'two', C: 'three']
36+
37+
assert map*.key == ['A', 'B', 'C']
38+
assert map*.value*.size() == [3, 3, 5]
39+
assert map.collect { entry -> entry.value.size() } == [3, 3, 5]
40+
}
41+
42+
@Test
43+
void testSpreadDot2() {
44+
def m1 = [a: 1, b: 2]
45+
def m2 = [a: 11, b: 22]
46+
def m3 = [a: 111, b: 222]
47+
4048
def x = [m1, m2, m3]
41-
assert x*.a == [1, 11, 111]
42-
assert x*."a" == [1, 11, 111]
4349
assert x == [m1, m2, m3]
50+
assert x*.a == [1, 11, 111]
51+
assert x*.'a' == [1, 11, 111]
4452

45-
def m4 = null
46-
x << m4
47-
assert x*.a == [1, 11, 111, null]
48-
assert x*."a" == [1, 11, 111, null]
53+
x << null
4954
assert x == [m1, m2, m3, null]
55+
assert x*.a == [1, 11, 111, null]
56+
assert x*.'a' == [1, 11, 111, null]
5057

5158
Date checkDate = new Date()
5259
def d = new SpreadDotDemo()
5360
x << d
54-
assert x*."a"[4] >= checkDate
61+
assert x*.'a'[4] >= checkDate
5562
assert x == [m1, m2, m3, null, d]
5663

5764
def y = new SpreadDotDemo2()
58-
assert y."a" == 'Attribute Get a'
5965
assert y.a == 'Attribute Get a'
66+
assert y.'a' == 'Attribute Get a'
6067

6168
x << y
62-
assert x*."a"[5] == 'Attribute Get a'
6369
assert x == [m1, m2, m3, null, d, y]
70+
assert x*.'a'[5] == 'Attribute Get a'
6471
}
6572

6673
@Test
67-
void testSpreadDot2() {
74+
void testSpreadDot3() {
6875
def a = new SpreadDotDemo()
6976
def b = new SpreadDotDemo2()
7077
def x = [a, b]
7178

72-
assert x*.fnB("1") == [a.fnB("1"), b.fnB("1")]
79+
assert x*.fnB('1') == [a.fnB('1'), b.fnB('1')]
7380
assert [a, b]*.fnB() == [a.fnB(), b.fnB()]
7481
}
7582

7683
@Test
77-
void testSpreadDotArrays() {
84+
void testSpreadDotArrays1() {
7885
def a = new SpreadDotDemo()
7986
def b = new SpreadDotDemo2()
8087
Object[] x = [a, b]
8188

82-
assert x*.fnB("1") == [a.fnB("1"), b.fnB("1")]
89+
assert x*.fnB('1') == [a.fnB('1'), b.fnB('1')]
8390
assert [a, b]*.fnB() == [a.fnB(), b.fnB()]
8491

8592
int[] nums = [3, 4, 5]
@@ -98,9 +105,9 @@ final class SpreadDotTest {
98105

99106
books*.metaClass*.foo = { "Hello, ${delegate.class.simpleName}".toString() }
100107

101-
assertEquals("Hello, Book1", new Book1().foo())
102-
assertEquals("Hello, Book2", new Book2().foo())
103-
assertEquals("Hello, Book3", new Book3().foo())
108+
assertEquals('Hello, Book1', new Book1().foo())
109+
assertEquals('Hello, Book2', new Book2().foo())
110+
assertEquals('Hello, Book3', new Book3().foo())
104111
}
105112

106113
@Test
@@ -116,14 +123,6 @@ final class SpreadDotTest {
116123
assertEquals(['Large'], new Shirt()*.size())
117124
}
118125

119-
@Test
120-
void testSpreadDotMap() {
121-
def map = [A: "one", B: "two", C: "three"]
122-
assert map.collect { child -> child.value.size() } == [3, 3, 5]
123-
assert map*.value*.size() == [3, 3, 5]
124-
assert map*.getKey() == ['A', 'B', 'C']
125-
}
126-
127126
@Test
128127
void testSpreadDotAttribute() {
129128
def s = new Singlet()
@@ -160,7 +159,7 @@ final class SpreadDotTest {
160159
}
161160

162161
String fnB() {
163-
return "bb"
162+
return 'bb'
164163
}
165164

166165
String fnB(String m) {
@@ -178,19 +177,22 @@ final class SpreadDotTest {
178177
}
179178

180179
String fnB() {
181-
return "cc"
180+
return 'cc'
182181
}
183182

184183
String fnB(String m) {
185184
return "CC$m"
186185
}
187186
}
188187

189-
static class Book1 {}
188+
static class Book1 {
189+
}
190190

191-
static class Book2 {}
191+
static class Book2 {
192+
}
192193

193-
static class Book3 {}
194+
static class Book3 {
195+
}
194196

195197
static class Shirt {
196198
def size() { 'Large' }

src/test/groovy/groovy/transform/stc/ArraysAndCollectionsSTCTest.groovy

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -339,6 +339,17 @@ class ArraysAndCollectionsSTCTest extends StaticTypeCheckingTestCase {
339339
def listClass = list.class
340340
assert listClass == ArrayList
341341
'''
342+
343+
// GROOVY-11816
344+
assertScript '''
345+
class C {
346+
List<Class> classes
347+
void test() {
348+
def names = classes*.simpleName
349+
}
350+
}
351+
new C().test()
352+
'''
342353
}
343354

344355
void testListStarMethod() {

0 commit comments

Comments
 (0)