Skip to content

Commit f0dca5c

Browse files
eric-millesdaniellansun
authored andcommitted
GROOVY-11623: STC: support return of list expression for array method
1 parent ed6ee9b commit f0dca5c

4 files changed

Lines changed: 132 additions & 28 deletions

File tree

src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -652,23 +652,26 @@ public static boolean checkCompatibleAssignmentTypes(final ClassNode left, final
652652
}
653653

654654
if (left.isArray()) {
655+
ClassNode leftItemType = left.getComponentType();
655656
if (right.isArray()) {
656-
ClassNode leftComponent = left.getComponentType();
657-
ClassNode rightComponent = right.getComponentType();
658-
return (isPrimitiveType(leftComponent) && !isPrimitiveBoolean(leftComponent))
659-
? isPrimitiveType(rightComponent) // GROOVY-11371: primitive array only
660-
: checkCompatibleAssignmentTypes(leftComponent, rightComponent, rightExpression, false);
661-
}
662-
if (GeneralUtils.isOrImplements(right, Collection_TYPE) && !(rightExpression instanceof ListExpression)) {
663-
GenericsType elementType = GenericsUtils.parameterizeType(right, Collection_TYPE).getGenericsTypes()[0];
664-
return OBJECT_TYPE.equals(left.getComponentType()) // Object[] can accept any collection element type(s)
665-
|| (elementType.getLowerBound() == null && isCovariant(extractType(elementType), left.getComponentType()));
657+
ClassNode rightItemType = right.getComponentType();
658+
return (isPrimitiveType(leftItemType) && !isPrimitiveBoolean(leftItemType))
659+
? isPrimitiveType(rightItemType) // GROOVY-11371: primitive array only
660+
: checkCompatibleAssignmentTypes(leftItemType, rightItemType, rightExpression, false);
661+
}
662+
if (rightExpression instanceof ListExpression) {
663+
return true; // addPrecisionErrors checks values
664+
}
665+
if (GeneralUtils.isOrImplements(right, Collection_TYPE)) {
666+
var elementType = GenericsUtils.parameterizeType(right, Collection_TYPE).getGenericsTypes()[0];
667+
return isObjectType(leftItemType) // Object[] can accept any collection element type(s)
668+
|| (elementType.getLowerBound() == null && isCovariant(extractType(elementType), leftItemType));
666669
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GROOVY-8984: "? super T" is only compatible with an Object[] target
667670
}
668671
if (GeneralUtils.isOrImplements(right, BaseStream_TYPE)) {
669-
GenericsType elementType = GenericsUtils.parameterizeType(right, BaseStream_TYPE).getGenericsTypes()[0];
670-
return isObjectType(left.getComponentType()) // Object[] can accept any stream API element type(s)
671-
|| (elementType.getLowerBound() == null && isCovariant(extractType(elementType), getWrapper(left.getComponentType())));
672+
var elementType = GenericsUtils.parameterizeType(right, BaseStream_TYPE).getGenericsTypes()[0];
673+
return isObjectType(leftItemType) // Object[] can accept any stream API element type(s)
674+
|| (elementType.getLowerBound() == null && isCovariant(extractType(elementType), getWrapper(leftItemType)));
672675
}
673676
}
674677

src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2428,6 +2428,8 @@ protected ClassNode checkReturnType(final ReturnStatement statement) {
24282428
BinaryExpression dummy = assignX(varX("{target}", returnType), expression, statement);
24292429
ClassNode resultType = getResultType(returnType, ASSIGN, type, dummy); // GROOVY-10295
24302430
checkTypeGenerics(returnType, resultType, expression);
2431+
} else { // GROOVY-11623: check array items or number bounds
2432+
addPrecisionErrors(returnType.redirect(), returnType, type, expression);
24312433
}
24322434
}
24332435
return null;

src/test/groovy/groovy/transform/stc/ReturnsSTCTest.groovy

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
2929
}
3030
3131
int x = method()
32-
''', 'Cannot assign value of type void to variable of type int'
32+
''',
33+
'Cannot assign value of type void to variable of type int'
3334
}
3435

3536
void testIncompatibleExplicitReturn() {
@@ -39,23 +40,26 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
3940
}
4041
4142
int x = method()
42-
''', 'Cannot assign value of type java.lang.String to variable of type int'
43+
''',
44+
'Cannot assign value of type java.lang.String to variable of type int'
4345
}
4446

4547
void testIncompatibleExplicitReturn2() {
4648
shouldFailWithMessages '''
4749
int method() {
4850
return 'String'
4951
}
50-
''', 'Cannot return value of type java.lang.String for method returning int'
52+
''',
53+
'Cannot return value of type java.lang.String for method returning int'
5154
}
5255

5356
void testIncompatibleImplicitReturn2() {
5457
shouldFailWithMessages '''
5558
int method() {
5659
'String'
5760
}
58-
''', 'Cannot return value of type java.lang.String for method returning int'
61+
''',
62+
'Cannot return value of type java.lang.String for method returning int'
5963
}
6064

6165
void testIncompatibleImplicitReturn() {
@@ -65,7 +69,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
6569
}
6670
6771
int x = method()
68-
''', 'Cannot assign value of type java.lang.String to variable of type int'
72+
''',
73+
'Cannot assign value of type java.lang.String to variable of type int'
6974
}
7075

7176
void testImplicitReturnFailureWithIfElse() {
@@ -77,7 +82,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
7782
2
7883
}
7984
}
80-
''', 'Cannot return value of type java.lang.String for method returning int'
85+
''',
86+
'Cannot return value of type java.lang.String for method returning int'
8187
}
8288

8389
void testImplicitReturnFailureWithIfElse2() {
@@ -89,7 +95,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
8995
'String'
9096
}
9197
}
92-
''', 'Cannot return value of type java.lang.String for method returning int'
98+
''',
99+
'Cannot return value of type java.lang.String for method returning int'
93100
}
94101

95102
void testImplicitReturnFailureWithIfElse3() {
@@ -145,7 +152,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
145152
double greeting(String name) {
146153
new Object()
147154
}
148-
''', 'Cannot return value of type java.lang.Object for method returning double'
155+
''',
156+
'Cannot return value of type java.lang.Object for method returning double'
149157
}
150158

151159
void testRecursiveTypeInferrence() {
@@ -169,14 +177,13 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
169177

170178
void testReturnTypeInferrenceInSingleClass() {
171179
assertScript '''
172-
class Foo {
173-
int square(int i) { i*i }
174-
175-
int foo(int i) {
176-
square(i)
180+
class Foo {
181+
int square(int i) { i*i }
182+
int foo(int i) {
183+
square(i)
184+
}
177185
}
178-
}
179-
new Foo().foo(2)
186+
new Foo().foo(2)
180187
'''
181188
}
182189

@@ -251,6 +258,62 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
251258
'''
252259
}
253260

261+
// GROOVY-11623
262+
void testImplicitReturnToArray() {
263+
assertScript '''
264+
int[] foo() {
265+
[]
266+
}
267+
int[] bar() {
268+
[0]
269+
}
270+
assert foo().length == 0
271+
assert bar().length == 1
272+
'''
273+
274+
shouldFailWithMessages '''
275+
int[] baz() {
276+
[null]
277+
}
278+
''',
279+
'Cannot assign value of type java.lang.Object into array of type int[]'
280+
281+
assertScript '''
282+
Number[] foo() {
283+
[]
284+
}
285+
Number[] bar() {
286+
[0]
287+
}
288+
Number[] baz() {
289+
[null]
290+
}
291+
assert foo().length == 0
292+
assert bar().length == 1
293+
assert baz().length == 1
294+
'''
295+
296+
shouldFailWithMessages '''
297+
Integer[] baz() {
298+
[new Object()]
299+
}
300+
''',
301+
'Cannot assign value of type java.lang.Object into array of type java.lang.Integer[]'
302+
303+
assertScript '''
304+
Object[][] foo() {
305+
[]
306+
}
307+
Object[][] bar() {
308+
[[12345]]
309+
}
310+
assert foo().length == 0
311+
assert bar().length == 1
312+
assert bar()[0].length == 1
313+
assert bar()[0][0] == 12345
314+
'''
315+
}
316+
254317
// GROOVY-5835
255318
void testReturnInClosureShouldNotBeConsideredAsReturnOfEnclosingMethod() {
256319
assertScript '''

src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1500,6 +1500,42 @@ class TypeInferenceSTCTest extends StaticTypeCheckingTestCase {
15001500
'''
15011501
}
15021502

1503+
// GROOVY-11623
1504+
void testAnnotationDefaults() {
1505+
assertScript '''
1506+
import java.lang.annotation.*
1507+
1508+
@Retention(RetentionPolicy.RUNTIME)
1509+
@Target(ElementType.TYPE)
1510+
@interface A {
1511+
int iType() default 1
1512+
long lType() default 1
1513+
short sType() default 1
1514+
float fType() default 1f
1515+
double dType() default 1d
1516+
boolean bType() default true
1517+
char cType() default 'c'
1518+
String chars() default 's'
1519+
Class tType() default Object
1520+
Class[] xType() default [] // TODO: {} -- GROOVY-11492
1521+
}
1522+
1523+
@A class C {
1524+
}
1525+
1526+
assert C.getAnnotation(A).iType() == 1
1527+
assert C.getAnnotation(A).lType() == 1L
1528+
assert C.getAnnotation(A).sType() == (short) 1
1529+
assert C.getAnnotation(A).fType() == 1.0f
1530+
assert C.getAnnotation(A).dType() == 1.0d
1531+
assert C.getAnnotation(A).bType() == true
1532+
assert C.getAnnotation(A).cType() == (char) 'c'
1533+
assert C.getAnnotation(A).chars() == "s"
1534+
assert C.getAnnotation(A).tType() == Object.class
1535+
assert C.getAnnotation(A).xType() == new Class[0]
1536+
'''
1537+
}
1538+
15031539
// GROOVY-
15041540
void testGetAnnotationFails() {
15051541
assertScript '''

0 commit comments

Comments
 (0)