Skip to content

Commit

Permalink
GROOVY-11490: STC: check null return of lambda
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Oct 18, 2024
1 parent ab5a811 commit a818836
Show file tree
Hide file tree
Showing 4 changed files with 20 additions and 20 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -376,8 +376,7 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {

public static final Statement GENERATED_EMPTY_STATEMENT = EmptyStatement.INSTANCE;

protected final ReturnAdder.ReturnStatementListener returnListener = returnStatement -> {
if (returnStatement.isReturningNullOrVoid()) return;
protected final ReturnAdder.ReturnStatementListener returnListener = (returnStatement) -> {
ClassNode returnType = checkReturnType(returnStatement);
if (this.typeCheckingContext.getEnclosingClosure() != null) {
addClosureReturnType(returnType);
Expand Down Expand Up @@ -2392,10 +2391,13 @@ protected ClassNode checkReturnType(final ReturnStatement statement) {
&& !GenericsUtils.hasUnresolvedGenerics(inferredReturnType)) {
if (isStringType(inferredReturnType) && isGStringOrGStringStringLUB(type)) {
type = STRING_TYPE; // GROOVY-9971: convert GString to String at point of return
} else if (GenericsUtils.buildWildcardType(wrapTypeIfNecessary(inferredReturnType)).isCompatibleWith(wrapTypeIfNecessary(type))) {
} 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 return value
addStaticTypeError("Cannot return value of type " + prettyPrintType(type) + " for " + (enclosingClosure.getClosureExpression() instanceof LambdaExpression ? "lambda" : "closure") + " expecting " + prettyPrintType(inferredReturnType), expression);
} else if (!isPrimitiveVoid(type) && !extension.handleIncompatibleReturnType(statement, type)) { // GROOVY-10277: incompatible
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);
}
}
return type;
Expand All @@ -2404,7 +2406,7 @@ protected ClassNode checkReturnType(final ReturnStatement statement) {
MethodNode enclosingMethod = typeCheckingContext.getEnclosingMethod();
if (enclosingMethod != null && !enclosingMethod.isVoidMethod() && !enclosingMethod.isDynamicReturnType()) {
ClassNode returnType = enclosingMethod.getReturnType();
if (!isPrimitiveVoid(getUnwrapper(type)) && !checkCompatibleAssignmentTypes(returnType, type, null, false)) {
if (!isPrimitiveVoid(getUnwrapper(type)) && !checkCompatibleAssignmentTypes(returnType, type, expression, false)) {
if (!extension.handleIncompatibleReturnType(statement, type)) {
addStaticTypeError("Cannot return value of type " + prettyPrintType(type) + " for method returning " + prettyPrintType(returnType), expression);
}
Expand Down
4 changes: 2 additions & 2 deletions src/test/groovy/transform/stc/ClosuresSTCTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
'Cannot return value of type java.util.ArrayList<java.lang.Object> for closure expecting java.lang.Boolean'
}

// GROOVY-8202
// GROOVY-7713, GROOVY-8202
void testClosureReturnTypeInference9() {
assertScript '''
void proc() {
Expand All @@ -382,7 +382,7 @@ class ClosuresSTCTest extends StaticTypeCheckingTestCase {
c.call()
}
String test2(flag) {
Closure<String> c = { -> // Cannot assign Closure<Object> to Closure<String>
Closure<String> c = { ->
if (flag) {
'baz'
} else {
Expand Down
12 changes: 0 additions & 12 deletions src/test/groovy/transform/stc/GenericsSTCTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -5402,18 +5402,6 @@ class GenericsSTCTest extends StaticTypeCheckingTestCase {
'''
}

// GROOVY-7713
void testClosureReturnNull() {
assertScript '''
Closure<String> cl = {
if (hashCode() > 0) {
return null
}
'foo'
}
'''
}

// GROOVY-10528
void testRawTypeGuard() {
assertScript '''
Expand Down
10 changes: 10 additions & 0 deletions src/test/groovy/transform/stc/LambdaTest.groovy
Original file line number Diff line number Diff line change
Expand Up @@ -198,6 +198,16 @@ final class LambdaTest {
'''
}

// GROOVY-11490
@Test
void testUnaryOperator2() {
def err = shouldFail shell, '''
IntUnaryOperator f = (i) -> null
int i = f.applyAsInt(42)
'''
assert err =~ /Cannot return null for lambda expecting int/
}

@Test
void testBiConsumer() {
assertScript shell, '''
Expand Down

0 comments on commit a818836

Please sign in to comment.