Skip to content

Commit

Permalink
GROOVY-10660
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-milles committed Jun 22, 2022
1 parent daae628 commit c49c190
Show file tree
Hide file tree
Showing 4 changed files with 91 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -5820,6 +5820,31 @@ public void testTypeChecked10651() {
runConformTest(sources);
}

@Test
public void testTypeChecked10660() {
//@formatter:off
String[] sources = {
"Main.groovy",
"import java.util.function.*\n" +
"@groovy.transform.TypeChecked\n" +
"def <T> BiConsumer<String, List<T>> m(BiConsumer<String, ? super T> proc) {\n" +
" return { text, list ->\n" +
" for (item in list) proc.accept(text, item)\n" +
" }\n" +
"}\n" +
"m { text, item -> }\n",
};
//@formatter:on

runConformTest(sources);

if (isParrotParser()) {
sources[1] = sources[1].replace("{ text, list ->", "(text, list) -> {");

runConformTest(sources);
}
}

@Test
public void testTypeChecked10662() {
//@formatter:off
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2650,6 +2650,19 @@ public void visitExpressionStatement(ExpressionStatement statement) {

@Override
public void visitReturnStatement(ReturnStatement statement) {
// GRECLIPSE add -- GROOVY-10660
if (typeCheckingContext.getEnclosingClosure() == null) {
MethodNode method = typeCheckingContext.getEnclosingMethod();
if (method != null && !method.isVoidMethod() && !method.isDynamicReturnType()) {
ClassNode returnType = method.getReturnType(); Expression returnValue = statement.getExpression();
if (isFunctionalInterface(returnType)) {
processFunctionalInterfaceAssignment(returnType, returnValue);
} else if (isClosureWithType(returnType) && returnValue instanceof ClosureExpression) {
storeInferredReturnType(returnValue, getCombinedBoundType(returnType.getGenericsTypes()[0]));
}
}
}
// GRECLIPSE end
typeCheckingContext.pushEnclosingReturnStatement(statement);
try {
super.visitReturnStatement(statement);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2468,6 +2468,19 @@ public void visitExpressionStatement(final ExpressionStatement statement) {

@Override
public void visitReturnStatement(final ReturnStatement statement) {
// GRECLIPSE add -- GROOVY-10660
if (typeCheckingContext.getEnclosingClosure() == null) {
MethodNode method = typeCheckingContext.getEnclosingMethod();
if (method != null && !method.isVoidMethod() && !method.isDynamicReturnType()) {
ClassNode returnType = method.getReturnType(); Expression returnValue = statement.getExpression();
if (isFunctionalInterface(returnType)) {
processFunctionalInterfaceAssignment(returnType, returnValue);
} else if (isClosureWithType(returnType) && returnValue instanceof ClosureExpression) {
storeInferredReturnType(returnValue, getCombinedBoundType(returnType.getGenericsTypes()[0]));
}
}
}
// GRECLIPSE end
super.visitReturnStatement(statement);
returnListener.returnStatementAdded(statement);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -365,16 +365,13 @@ public class StaticTypeCheckingVisitor extends ClassCodeVisitorSupport {

public static final Statement GENERATED_EMPTY_STATEMENT = EmptyStatement.INSTANCE;

protected final ReturnAdder.ReturnStatementListener returnListener = new ReturnAdder.ReturnStatementListener() {
@Override
public void returnStatementAdded(final ReturnStatement returnStatement) {
if (isNullConstant(returnStatement.getExpression())) return;
ClassNode returnType = checkReturnType(returnStatement);
if (typeCheckingContext.getEnclosingClosure() != null) {
addClosureReturnType(returnType);
} else if (typeCheckingContext.getEnclosingMethod() == null) {
throw new GroovyBugError("Unexpected return statement at " + returnStatement.getLineNumber() + ":" + returnStatement.getColumnNumber() + " " + returnStatement.getText());
}
protected final ReturnAdder.ReturnStatementListener returnListener = returnStatement -> {
if (returnStatement.isReturningNullOrVoid()) return;
ClassNode returnType = checkReturnType(returnStatement);
if (this.typeCheckingContext.getEnclosingClosure() != null) {
addClosureReturnType(returnType);
} else if (this.typeCheckingContext.getEnclosingMethod() == null) {
throw new GroovyBugError("Unexpected return statement at " + returnStatement.getLineNumber() + ":" + returnStatement.getColumnNumber() + " " + returnStatement.getText());
}
};

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

if (isFunctionalInterface(lType)) {
processFunctionalInterfaceAssignment(lType, rightExpression);
} else if (isClosureWithType(lType) && rightExpression instanceof ClosureExpression) {
storeInferredReturnType(rightExpression, getCombinedBoundType(lType.getGenericsTypes()[0]));
}
applyTargetType(lType, rightExpression);
}
rightExpression.visit(this);
}
Expand Down Expand Up @@ -965,15 +958,22 @@ private void validateResourceInARM(final BinaryExpression expression, final Clas
}
}

private void processFunctionalInterfaceAssignment(final ClassNode lhsType, final Expression rhsExpression) {
if (rhsExpression instanceof ClosureExpression) {
inferParameterAndReturnTypesOfClosureOnRHS(lhsType, (ClosureExpression) rhsExpression);
} else if (rhsExpression instanceof MethodReferenceExpression) {
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(lhsType);
private void applyTargetType(final ClassNode target, final Expression source) {
if (isFunctionalInterface(target)) {
if (source instanceof ClosureExpression) {
inferParameterAndReturnTypesOfClosureOnRHS(target, (ClosureExpression) source);
} else if (source instanceof MethodReferenceExpression) {
LambdaExpression lambdaExpression = constructLambdaExpressionForMethodReference(target);

inferParameterAndReturnTypesOfClosureOnRHS(lhsType, lambdaExpression);
rhsExpression.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
rhsExpression.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
inferParameterAndReturnTypesOfClosureOnRHS(target, lambdaExpression);
source.putNodeMetaData(CONSTRUCTED_LAMBDA_EXPRESSION, lambdaExpression);
source.putNodeMetaData(CLOSURE_ARGUMENTS, Arrays.stream(lambdaExpression.getParameters()).map(Parameter::getType).toArray(ClassNode[]::new));
}
} else if (isClosureWithType(target)) {
if (source instanceof ClosureExpression) {
GenericsType returnType = target.getGenericsTypes()[0];
storeInferredReturnType(source, getCombinedBoundType(returnType));
}
}
}

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

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

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

@Override
public void visitReturnStatement(final ReturnStatement statement) {
if (typeCheckingContext.getEnclosingClosure() == null) {
MethodNode method = typeCheckingContext.getEnclosingMethod();
if (method != null && !method.isVoidMethod() && !method.isDynamicReturnType()) {
applyTargetType(method.getReturnType(), statement.getExpression()); // GROOVY-10660
}
}
super.visitReturnStatement(statement);
returnListener.returnStatementAdded(statement);
}
Expand Down Expand Up @@ -2631,6 +2633,10 @@ protected void startMethodInference(final MethodNode node, final ErrorCollector
@Override
protected void visitConstructorOrMethod(final MethodNode node, final boolean isConstructor) {
typeCheckingContext.pushEnclosingMethod(node);
final ClassNode returnType = node.getReturnType(); // GROOVY-10660: implicit return case
if (!isConstructor && (isClosureWithType(returnType) || isFunctionalInterface(returnType))) {
new ReturnAdder(returnStmt -> applyTargetType(returnType, returnStmt.getExpression())).visitMethod(node);
}
readClosureParameterAnnotation(node); // GROOVY-6603
super.visitConstructorOrMethod(node, isConstructor);
if (node.hasDefaultValue()) {
Expand Down Expand Up @@ -4168,18 +4174,14 @@ public void visitArrayExpression(final ArrayExpression expression) {

@Override
public void visitCastExpression(final CastExpression expression) {
ClassNode type = expression.getType();
ClassNode target = expression.getType();
Expression source = expression.getExpression();
if (isFunctionalInterface(type)) { // GROOVY-9997
processFunctionalInterfaceAssignment(type, source);
} else if (isClosureWithType(type) && source instanceof ClosureExpression) {
storeInferredReturnType(source, getCombinedBoundType(type.getGenericsTypes()[0]));
}
applyTargetType(target, source); // GROOVY-9997

source.visit(this);

if (!expression.isCoerce() && !checkCast(type, source)) {
addStaticTypeError("Inconvertible types: cannot cast " + prettyPrintType(getType(source)) + " to " + prettyPrintType(type), expression);
if (!expression.isCoerce() && !checkCast(target, source)) {
addStaticTypeError("Inconvertible types: cannot cast " + prettyPrintType(getType(source)) + " to " + prettyPrintType(target), expression);
}
}

Expand Down Expand Up @@ -5999,19 +6001,16 @@ private class ParameterVariableExpression extends VariableExpression {
ParameterVariableExpression(final Parameter parameter) {
super(parameter);
this.parameter = parameter;
/* GRECLIPSE edit -- GROOVY-10651
this.parameter.getNodeMetaData(INFERRED_TYPE, x -> parameter.getOriginType());
*/

ClassNode inferredType = getNodeMetaData(INFERRED_TYPE);
if (inferredType == null) {
inferredType = typeCheckingContext.controlStructureVariables.get(parameter); // for/catch/closure
if (inferredType == null) {
TypeCheckingContext.EnclosingClosure enclosingClosure = typeCheckingContext.getEnclosingClosure();
if (enclosingClosure != null) inferredType = getTypeFromClosureArguments(parameter, enclosingClosure);
}
setNodeMetaData(INFERRED_TYPE, inferredType != null ? inferredType : parameter.getType()); // to parameter
setNodeMetaData(INFERRED_TYPE, inferredType != null ? inferredType : parameter.getType()); // GROOVY-10651
}
// GRECLIPSE end
}

@Override
Expand Down

0 comments on commit c49c190

Please sign in to comment.