This is an automated email from the ASF dual-hosted git repository. emilles pushed a commit to branch GROOVY-11685 in repository https://gitbox.apache.org/repos/asf/groovy.git
commit 67af81026f39e8c27c98b87901bb54e5b2a4fec8 Author: Eric Milles <[email protected]> AuthorDate: Sat May 31 11:37:17 2025 -0500 GROOVY-11685: STC: `null` or `void` return within closure expression --- .../groovy/transform/stc/StaticTypeCheckingVisitor.java | 5 +++-- .../groovy/groovy/transform/stc/ClosuresSTCTest.groovy | 17 +++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) 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 fa7df3da1e..2605a17e2e 100644 --- a/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/src/main/java/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -2397,7 +2397,7 @@ out: if ((samParameterTypes.length == 1 && isOrImplements(samParameterTypes[0 protected ClassNode checkReturnType(final ReturnStatement statement) { Expression expression = statement.getExpression(); - ClassNode type = getType(expression); + var type = statement.isReturningNullOrVoid() ? VOID_TYPE : getType(expression); // GROOVY-11685 TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure(); if (enclosingClosure != null) { @@ -2419,7 +2419,8 @@ out: if ((samParameterTypes.length == 1 && isOrImplements(samParameterTypes[0 } else if (statement.isReturningNullOrVoid() ? !isPrimitiveType(inferredReturnType) // GROOVY-7713, GROOVY-8202 : GenericsUtils.buildWildcardType(wrapTypeIfNecessary(inferredReturnType)).isCompatibleWith(wrapTypeIfNecessary(type))) { type = inferredReturnType; // GROOVY-8310, GROOVY-10082, GROOVY-10091, GROOVY-10128, GROOVY-10306: allow simple covariance - } else if (!isPrimitiveVoid(type) && !extension.handleIncompatibleReturnType(statement, type)) { // GROOVY-10277: incompatible + } else if ((statement.isReturningNullOrVoid() || !isPrimitiveVoid(type)) && !extension.handleIncompatibleReturnType(statement, type)) { + // GROOVY-10277, GROOVY-11490: incompatible return statement (null for primitive or inconvertible type) String value = (statement.isReturningNullOrVoid() ? "null" : "value of type " + prettyPrintType(type)); String which = (enclosingClosure.getClosureExpression() instanceof LambdaExpression ? "lambda" : "closure"); addStaticTypeError("Cannot return " + value + " for " + which + " expecting " + prettyPrintType(inferredReturnType), expression); diff --git a/src/test/groovy/groovy/transform/stc/ClosuresSTCTest.groovy b/src/test/groovy/groovy/transform/stc/ClosuresSTCTest.groovy index 3bec5b2998..856cc3c4bd 100644 --- a/src/test/groovy/groovy/transform/stc/ClosuresSTCTest.groovy +++ b/src/test/groovy/groovy/transform/stc/ClosuresSTCTest.groovy @@ -429,6 +429,23 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase { ''' } + // GROOVY-11685 + void testCollect3() { + assertScript ''' + List<Number> test(List<String> strings) { + List<Number> numbers = [] + if (true) { + numbers = strings.collect { string -> + if (!string.isNumber()) return + (Number) Integer.parseInt(string) + } + } + numbers + } + assert test(['1','2','3','x']) == [1,2,3,null] + ''' + } + void testWithIntReturnType() { assertScript ''' class Test {
