Skip to content

Commit c49c190

Browse files
committed
GROOVY-10660
1 parent daae628 commit c49c190

File tree

4 files changed

+91
-41
lines changed

4 files changed

+91
-41
lines changed

base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java

+25
Original file line numberDiff line numberDiff line change
@@ -5820,6 +5820,31 @@ public void testTypeChecked10651() {
58205820
runConformTest(sources);
58215821
}
58225822

5823+
@Test
5824+
public void testTypeChecked10660() {
5825+
//@formatter:off
5826+
String[] sources = {
5827+
"Main.groovy",
5828+
"import java.util.function.*\n" +
5829+
"@groovy.transform.TypeChecked\n" +
5830+
"def <T> BiConsumer<String, List<T>> m(BiConsumer<String, ? super T> proc) {\n" +
5831+
" return { text, list ->\n" +
5832+
" for (item in list) proc.accept(text, item)\n" +
5833+
" }\n" +
5834+
"}\n" +
5835+
"m { text, item -> }\n",
5836+
};
5837+
//@formatter:on
5838+
5839+
runConformTest(sources);
5840+
5841+
if (isParrotParser()) {
5842+
sources[1] = sources[1].replace("{ text, list ->", "(text, list) -> {");
5843+
5844+
runConformTest(sources);
5845+
}
5846+
}
5847+
58235848
@Test
58245849
public void testTypeChecked10662() {
58255850
//@formatter:off

base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+13
Original file line numberDiff line numberDiff line change
@@ -2650,6 +2650,19 @@ public void visitExpressionStatement(ExpressionStatement statement) {
26502650

26512651
@Override
26522652
public void visitReturnStatement(ReturnStatement statement) {
2653+
// GRECLIPSE add -- GROOVY-10660
2654+
if (typeCheckingContext.getEnclosingClosure() == null) {
2655+
MethodNode method = typeCheckingContext.getEnclosingMethod();
2656+
if (method != null && !method.isVoidMethod() && !method.isDynamicReturnType()) {
2657+
ClassNode returnType = method.getReturnType(); Expression returnValue = statement.getExpression();
2658+
if (isFunctionalInterface(returnType)) {
2659+
processFunctionalInterfaceAssignment(returnType, returnValue);
2660+
} else if (isClosureWithType(returnType) && returnValue instanceof ClosureExpression) {
2661+
storeInferredReturnType(returnValue, getCombinedBoundType(returnType.getGenericsTypes()[0]));
2662+
}
2663+
}
2664+
}
2665+
// GRECLIPSE end
26532666
typeCheckingContext.pushEnclosingReturnStatement(statement);
26542667
try {
26552668
super.visitReturnStatement(statement);

base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+13
Original file line numberDiff line numberDiff line change
@@ -2468,6 +2468,19 @@ public void visitExpressionStatement(final ExpressionStatement statement) {
24682468

24692469
@Override
24702470
public void visitReturnStatement(final ReturnStatement statement) {
2471+
// GRECLIPSE add -- GROOVY-10660
2472+
if (typeCheckingContext.getEnclosingClosure() == null) {
2473+
MethodNode method = typeCheckingContext.getEnclosingMethod();
2474+
if (method != null && !method.isVoidMethod() && !method.isDynamicReturnType()) {
2475+
ClassNode returnType = method.getReturnType(); Expression returnValue = statement.getExpression();
2476+
if (isFunctionalInterface(returnType)) {
2477+
processFunctionalInterfaceAssignment(returnType, returnValue);
2478+
} else if (isClosureWithType(returnType) && returnValue instanceof ClosureExpression) {
2479+
storeInferredReturnType(returnValue, getCombinedBoundType(returnType.getGenericsTypes()[0]));
2480+
}
2481+
}
2482+
}
2483+
// GRECLIPSE end
24712484
super.visitReturnStatement(statement);
24722485
returnListener.returnStatementAdded(statement);
24732486
}

base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java

+40-41
Original file line numberDiff line numberDiff line change
@@ -365,16 +365,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {
365365

366366
public static final Statement GENERATED_EMPTY_STATEMENT = EmptyStatement.INSTANCE;
367367

368-
protected final ReturnAdder.ReturnStatementListener returnListener = new ReturnAdder.ReturnStatementListener() {
369-
@Override
370-
public void returnStatementAdded(final ReturnStatement returnStatement) {
371-
if (isNullConstant(returnStatement.getExpression())) return;
372-
ClassNode returnType = checkReturnType(returnStatement);
373-
if (typeCheckingContext.getEnclosingClosure() != null) {
374-
addClosureReturnType(returnType);
375-
} else if (typeCheckingContext.getEnclosingMethod() == null) {
376-
throw new GroovyBugError("Unexpected return statement at " + returnStatement.getLineNumber() + ":" + returnStatement.getColumnNumber() + " " + returnStatement.getText());
377-
}
368+
protected final ReturnAdder.ReturnStatementListener returnListener = returnStatement -> {
369+
if (returnStatement.isReturningNullOrVoid()) return;
370+
ClassNode returnType = checkReturnType(returnStatement);
371+
if (this.typeCheckingContext.getEnclosingClosure() != null) {
372+
addClosureReturnType(returnType);
373+
} else if (this.typeCheckingContext.getEnclosingMethod() == null) {
374+
throw new GroovyBugError("Unexpected return statement at " + returnStatement.getLineNumber() + ":" + returnStatement.getColumnNumber() + " " + returnStatement.getText());
378375
}
379376
};
380377

@@ -792,11 +789,7 @@ public void visitBinaryExpression(final BinaryExpression expression) {
792789
} else {
793790
lType = getOriginalDeclarationType(leftExpression);
794791

795-
if (isFunctionalInterface(lType)) {
796-
processFunctionalInterfaceAssignment(lType, rightExpression);
797-
} else if (isClosureWithType(lType) && rightExpression instanceof ClosureExpression) {
798-
storeInferredReturnType(rightExpression, getCombinedBoundType(lType.getGenericsTypes()[0]));
799-
}
792+
applyTargetType(lType, rightExpression);
800793
}
801794
rightExpression.visit(this);
802795
}
@@ -965,15 +958,22 @@ private void validateResourceInARM(final BinaryExpression expression, final Clas
965958
}
966959
}
967960

968-
private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
969-
if (rhsExpression instanceof ClosureExpression) {
970-
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) rhsExpression);
971-
} else if (rhsExpression instanceof MethodReferenceExpression) {
972-
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lhsType);
961+
private void applyTargetType(final ClassNode target, final Expression source) {
962+
if (isFunctionalInterface(target)) {
963+
if (source instanceof ClosureExpression) {
964+
inferParameterAndReturnTypesOfClosureOnRHS(target, (ClosureExpression) source);
965+
} else if (source instanceof MethodReferenceExpression) {
966+
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(target);
973967

974-
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, lambdaExpression);
975-
rhsExpression.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
976-
rhsExpression.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
968+
inferParameterAndReturnTypesOfClosureOnRHS(target, lambdaExpression);
969+
source.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
970+
source.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
971+
}
972+
} else if (isClosureWithType(target)) {
973+
if (source instanceof ClosureExpression) {
974+
GenericsType returnType = target.getGenericsTypes()[0];
975+
storeInferredReturnType(source, getCombinedBoundType(returnType));
976+
}
977977
}
978978
}
979979

@@ -1970,11 +1970,7 @@ public void visitField(final FieldNode node) {
19701970
private void visitInitialExpression(final Expression value, final Expression target, final ASTNode position) {
19711971
if (value != null) {
19721972
ClassNode lType = target.getType();
1973-
if (isFunctionalInterface(lType)) { // GROOVY-9977
1974-
processFunctionalInterfaceAssignment(lType, value);
1975-
} else if (isClosureWithType(lType) && value instanceof ClosureExpression) {
1976-
storeInferredReturnType(value, getCombinedBoundType(lType.getGenericsTypes()[0]));
1977-
}
1973+
applyTargetType(lType, value); // GROOVY-9977
19781974

19791975
typeCheckingContext.pushEnclosingBinaryExpression(assignX(target, value, position));
19801976

@@ -2244,6 +2240,12 @@ public void visitExpressionStatement(final ExpressionStatement statement) {
22442240

22452241
@Override
22462242
public void visitReturnStatement(final ReturnStatement statement) {
2243+
if (typeCheckingContext.getEnclosingClosure() == null) {
2244+
MethodNode method = typeCheckingContext.getEnclosingMethod();
2245+
if (method != null && !method.isVoidMethod() && !method.isDynamicReturnType()) {
2246+
applyTargetType(method.getReturnType(), statement.getExpression()); // GROOVY-10660
2247+
}
2248+
}
22472249
super.visitReturnStatement(statement);
22482250
returnListener.returnStatementAdded(statement);
22492251
}
@@ -2631,6 +2633,10 @@ protected void startMethodInference(final MethodNode node, final ErrorCollector
26312633
@Override
26322634
protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
26332635
typeCheckingContext.pushEnclosingMethod(node);
2636+
final ClassNode returnType = node.getReturnType(); // GROOVY-10660: implicit return case
2637+
if (!isConstructor && (isClosureWithType(returnType) || isFunctionalInterface(returnType))) {
2638+
new ReturnAdder(returnStmt -> applyTargetType(returnType, returnStmt.getExpression())).visitMethod(node);
2639+
}
26342640
readClosureParameterAnnotation(node); // GROOVY-6603
26352641
super.visitConstructorOrMethod(node, isConstructor);
26362642
if (node.hasDefaultValue()) {
@@ -4168,18 +4174,14 @@ public void visitArrayExpression(final ArrayExpression expression) {
41684174

41694175
@Override
41704176
public void visitCastExpression(final CastExpression expression) {
4171-
ClassNode type = expression.getType();
4177+
ClassNode target = expression.getType();
41724178
Expression source = expression.getExpression();
4173-
if (isFunctionalInterface(type)) { // GROOVY-9997
4174-
processFunctionalInterfaceAssignment(type, source);
4175-
} else if (isClosureWithType(type) && source instanceof ClosureExpression) {
4176-
storeInferredReturnType(source, getCombinedBoundType(type.getGenericsTypes()[0]));
4177-
}
4179+
applyTargetType(target, source); // GROOVY-9997
41784180

41794181
source.visit(this);
41804182

4181-
if (!expression.isCoerce() && !checkCast(type, source)) {
4182-
addStaticTypeError("Inconvertible types: cannot cast " + prettyPrintType(getType(source)) + " to " + prettyPrintType(type), expression);
4183+
if (!expression.isCoerce() && !checkCast(target, source)) {
4184+
addStaticTypeError("Inconvertible types: cannot cast " + prettyPrintType(getType(source)) + " to " + prettyPrintType(target), expression);
41834185
}
41844186
}
41854187

@@ -5999,19 +6001,16 @@ private class ParameterVariableExpression extends VariableExpression {
59996001
ParameterVariableExpression(final Parameter parameter) {
60006002
super(parameter);
60016003
this.parameter = parameter;
6002-
/* GRECLIPSE edit -- GROOVY-10651
6003-
this.parameter.getNodeMetaData(INFERRED_TYPE, x -> parameter.getOriginType());
6004-
*/
6004+
60056005
ClassNode inferredType = getNodeMetaData(INFERRED_TYPE);
60066006
if (inferredType == null) {
60076007
inferredType = typeCheckingContext.controlStructureVariables.get(parameter); // for/catch/closure
60086008
if (inferredType == null) {
60096009
TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure();
60106010
if (enclosingClosure != null) inferredType = getTypeFromClosureArguments(parameter, enclosingClosure);
60116011
}
6012-
setNodeMetaData(INFERRED_TYPE, inferredType != null ? inferredType : parameter.getType()); // to parameter
6012+
setNodeMetaData(INFERRED_TYPE, inferredType != null ? inferredType : parameter.getType()); // GROOVY-10651
60136013
}
6014-
// GRECLIPSE end
60156014
}
60166015

60176016
@Override

0 commit comments

Comments
 (0)