From c49c190e3cbd6346b0165e4052de594748d72162 Mon Sep 17 00:00:00 2001 From: Eric Milles Date: Wed, 22 Jun 2022 09:25:05 -0500 Subject: [PATCH] GROOVY-10660 --- .../core/tests/xform/TypeCheckedTests.java | 25 ++++++ .../stc/StaticTypeCheckingVisitor.java | 13 +++ .../stc/StaticTypeCheckingVisitor.java | 13 +++ .../stc/StaticTypeCheckingVisitor.java | 81 +++++++++---------- 4 files changed, 91 insertions(+), 41 deletions(-) diff --git a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java index 01222420da..1728faeaec 100644 --- a/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java +++ b/base-test/org.eclipse.jdt.groovy.core.tests.compiler/src/org/eclipse/jdt/groovy/core/tests/xform/TypeCheckedTests.java @@ -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 BiConsumer> m(BiConsumer 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 diff --git a/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index 52dc56b2e4..4f441bbd90 100644 --- a/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/base/org.codehaus.groovy25/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -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); diff --git a/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index c8eefb492a..8dde999ed5 100644 --- a/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/base/org.codehaus.groovy30/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -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); } diff --git a/base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java b/base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java index e70d57426f..47a5a8cc1a 100644 --- a/base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java +++ b/base/org.codehaus.groovy40/src/org/codehaus/groovy/transform/stc/StaticTypeCheckingVisitor.java @@ -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()); } }; @@ -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); } @@ -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)); + } } } @@ -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)); @@ -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); } @@ -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()) { @@ -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); } } @@ -5999,9 +6001,7 @@ 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 @@ -6009,9 +6009,8 @@ private class ParameterVariableExpression extends VariableExpression { 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