This is an automated email from the ASF dual-hosted git repository.
sunlan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/groovy.git
The following commit(s) were added to refs/heads/master by this push:
new f0dca5c318 GROOVY-11623: STC: support return of list expression for
array method
f0dca5c318 is described below
commit f0dca5c318840f2d4e778b9faa87e80b810b7888
Author: Eric Milles <[email protected]>
AuthorDate: Wed Apr 16 15:15:43 2025 -0500
GROOVY-11623: STC: support return of list expression for array method
---
.../transform/stc/StaticTypeCheckingSupport.java | 29 ++++---
.../transform/stc/StaticTypeCheckingVisitor.java | 2 +
.../groovy/transform/stc/ReturnsSTCTest.groovy | 93 ++++++++++++++++++----
.../transform/stc/TypeInferenceSTCTest.groovy | 36 +++++++++
4 files changed, 132 insertions(+), 28 deletions(-)
diff --git
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
index 4867825693..e075f7eb0a 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingSupport.java
@@ -652,23 +652,26 @@ public abstract class StaticTypeCheckingSupport {
}
if (left.isArray()) {
+ ClassNode leftItemType = left.getComponentType();
if (right.isArray()) {
- ClassNode leftComponent = left.getComponentType();
- ClassNode rightComponent = right.getComponentType();
- return (isPrimitiveType(leftComponent) &&
!isPrimitiveBoolean(leftComponent))
- ? isPrimitiveType(rightComponent) // GROOVY-11371:
primitive array only
- : checkCompatibleAssignmentTypes(leftComponent,
rightComponent, rightExpression, false);
- }
- if (GeneralUtils.isOrImplements(right, Collection_TYPE) &&
!(rightExpression instanceof ListExpression)) {
- GenericsType elementType =
GenericsUtils.parameterizeType(right, Collection_TYPE).getGenericsTypes()[0];
- return OBJECT_TYPE.equals(left.getComponentType()) // Object[]
can accept any collection element type(s)
- || (elementType.getLowerBound() == null &&
isCovariant(extractType(elementType), left.getComponentType()));
+ ClassNode rightItemType = right.getComponentType();
+ return (isPrimitiveType(leftItemType) &&
!isPrimitiveBoolean(leftItemType))
+ ? isPrimitiveType(rightItemType) // GROOVY-11371:
primitive array only
+ : checkCompatibleAssignmentTypes(leftItemType,
rightItemType, rightExpression, false);
+ }
+ if (rightExpression instanceof ListExpression) {
+ return true; // addPrecisionErrors checks values
+ }
+ if (GeneralUtils.isOrImplements(right, Collection_TYPE)) {
+ var elementType = GenericsUtils.parameterizeType(right,
Collection_TYPE).getGenericsTypes()[0];
+ return isObjectType(leftItemType) // Object[] can accept any
collection element type(s)
+ || (elementType.getLowerBound() == null &&
isCovariant(extractType(elementType), leftItemType));
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ GROOVY-8984: "?
super T" is only compatible with an Object[] target
}
if (GeneralUtils.isOrImplements(right, BaseStream_TYPE)) {
- GenericsType elementType =
GenericsUtils.parameterizeType(right, BaseStream_TYPE).getGenericsTypes()[0];
- return isObjectType(left.getComponentType()) // Object[] can
accept any stream API element type(s)
- || (elementType.getLowerBound() == null &&
isCovariant(extractType(elementType), getWrapper(left.getComponentType())));
+ var elementType = GenericsUtils.parameterizeType(right,
BaseStream_TYPE).getGenericsTypes()[0];
+ return isObjectType(leftItemType) // Object[] can accept any
stream API element type(s)
+ || (elementType.getLowerBound() == null &&
isCovariant(extractType(elementType), getWrapper(leftItemType)));
}
}
diff --git
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
index fd1c090078..2d72a17e0f 100644
---
a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
+++
b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java
@@ -2428,6 +2428,8 @@ out: if ((samParameterTypes.length == 1 &&
isOrImplements(samParameterTypes[0
BinaryExpression dummy = assignX(varX("{target}", returnType),
expression, statement);
ClassNode resultType = getResultType(returnType, ASSIGN, type,
dummy); // GROOVY-10295
checkTypeGenerics(returnType, resultType, expression);
+ } else { // GROOVY-11623: check array items or number bounds
+ addPrecisionErrors(returnType.redirect(), returnType, type,
expression);
}
}
return null;
diff --git a/src/test/groovy/groovy/transform/stc/ReturnsSTCTest.groovy
b/src/test/groovy/groovy/transform/stc/ReturnsSTCTest.groovy
index 1172243665..ce8b66ff25 100644
--- a/src/test/groovy/groovy/transform/stc/ReturnsSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/ReturnsSTCTest.groovy
@@ -29,7 +29,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
}
int x = method()
- ''', 'Cannot assign value of type void to variable of type int'
+ ''',
+ 'Cannot assign value of type void to variable of type int'
}
void testIncompatibleExplicitReturn() {
@@ -39,7 +40,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
}
int x = method()
- ''', 'Cannot assign value of type java.lang.String to variable of type
int'
+ ''',
+ 'Cannot assign value of type java.lang.String to variable of type int'
}
void testIncompatibleExplicitReturn2() {
@@ -47,7 +49,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
int method() {
return 'String'
}
- ''', 'Cannot return value of type java.lang.String for method
returning int'
+ ''',
+ 'Cannot return value of type java.lang.String for method returning int'
}
void testIncompatibleImplicitReturn2() {
@@ -55,7 +58,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
int method() {
'String'
}
- ''', 'Cannot return value of type java.lang.String for method
returning int'
+ ''',
+ 'Cannot return value of type java.lang.String for method returning int'
}
void testIncompatibleImplicitReturn() {
@@ -65,7 +69,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
}
int x = method()
- ''', 'Cannot assign value of type java.lang.String to variable of type
int'
+ ''',
+ 'Cannot assign value of type java.lang.String to variable of type int'
}
void testImplicitReturnFailureWithIfElse() {
@@ -77,7 +82,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
2
}
}
- ''', 'Cannot return value of type java.lang.String for method
returning int'
+ ''',
+ 'Cannot return value of type java.lang.String for method returning int'
}
void testImplicitReturnFailureWithIfElse2() {
@@ -89,7 +95,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
'String'
}
}
- ''', 'Cannot return value of type java.lang.String for method
returning int'
+ ''',
+ 'Cannot return value of type java.lang.String for method returning int'
}
void testImplicitReturnFailureWithIfElse3() {
@@ -145,7 +152,8 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
double greeting(String name) {
new Object()
}
- ''', 'Cannot return value of type java.lang.Object for method
returning double'
+ ''',
+ 'Cannot return value of type java.lang.Object for method returning
double'
}
void testRecursiveTypeInferrence() {
@@ -169,14 +177,13 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
void testReturnTypeInferrenceInSingleClass() {
assertScript '''
- class Foo {
- int square(int i) { i*i }
-
- int foo(int i) {
- square(i)
+ class Foo {
+ int square(int i) { i*i }
+ int foo(int i) {
+ square(i)
+ }
}
- }
- new Foo().foo(2)
+ new Foo().foo(2)
'''
}
@@ -251,6 +258,62 @@ class ReturnsSTCTest extends StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-11623
+ void testImplicitReturnToArray() {
+ assertScript '''
+ int[] foo() {
+ []
+ }
+ int[] bar() {
+ [0]
+ }
+ assert foo().length == 0
+ assert bar().length == 1
+ '''
+
+ shouldFailWithMessages '''
+ int[] baz() {
+ [null]
+ }
+ ''',
+ 'Cannot assign value of type java.lang.Object into array of type int[]'
+
+ assertScript '''
+ Number[] foo() {
+ []
+ }
+ Number[] bar() {
+ [0]
+ }
+ Number[] baz() {
+ [null]
+ }
+ assert foo().length == 0
+ assert bar().length == 1
+ assert baz().length == 1
+ '''
+
+ shouldFailWithMessages '''
+ Integer[] baz() {
+ [new Object()]
+ }
+ ''',
+ 'Cannot assign value of type java.lang.Object into array of type
java.lang.Integer[]'
+
+ assertScript '''
+ Object[][] foo() {
+ []
+ }
+ Object[][] bar() {
+ [[12345]]
+ }
+ assert foo().length == 0
+ assert bar().length == 1
+ assert bar()[0].length == 1
+ assert bar()[0][0] == 12345
+ '''
+ }
+
// GROOVY-5835
void testReturnInClosureShouldNotBeConsideredAsReturnOfEnclosingMethod() {
assertScript '''
diff --git a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
index 508be6d96f..bb12727f5c 100644
--- a/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
+++ b/src/test/groovy/groovy/transform/stc/TypeInferenceSTCTest.groovy
@@ -1500,6 +1500,42 @@ class TypeInferenceSTCTest extends
StaticTypeCheckingTestCase {
'''
}
+ // GROOVY-11623
+ void testAnnotationDefaults() {
+ assertScript '''
+ import java.lang.annotation.*
+
+ @Retention(RetentionPolicy.RUNTIME)
+ @Target(ElementType.TYPE)
+ @interface A {
+ int iType() default 1
+ long lType() default 1
+ short sType() default 1
+ float fType() default 1f
+ double dType() default 1d
+ boolean bType() default true
+ char cType() default 'c'
+ String chars() default 's'
+ Class tType() default Object
+ Class[] xType() default [] // TODO: {} -- GROOVY-11492
+ }
+
+ @A class C {
+ }
+
+ assert C.getAnnotation(A).iType() == 1
+ assert C.getAnnotation(A).lType() == 1L
+ assert C.getAnnotation(A).sType() == (short) 1
+ assert C.getAnnotation(A).fType() == 1.0f
+ assert C.getAnnotation(A).dType() == 1.0d
+ assert C.getAnnotation(A).bType() == true
+ assert C.getAnnotation(A).cType() == (char) 'c'
+ assert C.getAnnotation(A).chars() == "s"
+ assert C.getAnnotation(A).tType() == Object.class
+ assert C.getAnnotation(A).xType() == new Class[0]
+ '''
+ }
+
// GROOVY-
void testGetAnnotationFails() {
assertScript '''