This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY-11623 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 7ba3a2ce74dc36a8f145607c942f7342b2f8a5ab 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 | 92 ++++++++++++++++++---- .../transform/stc/TypeInferenceSTCTest.groovy | 36 +++++++++ 4 files changed, 131 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 94dd15e53d..d4a9615888 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -2427,6 +2427,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..67a789bd0b 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,61 @@ 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() { + [[]] + } + assert foo().length == 0 + assert bar().length == 1 + assert bar()[0].length == 0 + ''' + } + // 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 '''
