Add support local return from lambda with implicit unit type.
This commit is contained in:
+66
-39
@@ -303,54 +303,65 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
|
||||
: (expectedReturnType != null ? expectedReturnType : NO_EXPECTED_TYPE);
|
||||
ExpressionTypingContext newContext = context.replaceScope(functionInnerScope).replaceExpectedType(expectedType);
|
||||
context.trace.record(EXPECTED_RETURN_TYPE, functionLiteral, expectedType);
|
||||
JetType typeOfBodyExpression = // needed for error reporting
|
||||
components.expressionTypingServices.getBlockReturnedType(bodyExpression, COERCION_TO_UNIT, newContext).getType();
|
||||
|
||||
JetType typeOfBodyExpression = components.expressionTypingServices.getBlockReturnedType(bodyExpression, COERCION_TO_UNIT, newContext).getType();
|
||||
if (declaredReturnType != null) {
|
||||
return declaredReturnType;
|
||||
}
|
||||
else {
|
||||
return computeReturnTypeBasedOnReturnExpressions(functionLiteral, context, typeOfBodyExpression);
|
||||
}
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private JetType computeReturnTypeBasedOnReturnExpressions(
|
||||
@NotNull JetFunctionLiteral functionLiteral,
|
||||
@NotNull ExpressionTypingContext context,
|
||||
@Nullable JetType typeOfBodyExpression
|
||||
) {
|
||||
List<JetType> returnedExpressionTypes = Lists.newArrayList();
|
||||
|
||||
boolean hasEmptyReturn = false;
|
||||
Collection<JetReturnExpression> returnExpressions = collectReturns(functionLiteral, context.trace);
|
||||
for (JetReturnExpression returnExpression : returnExpressions) {
|
||||
JetExpression returnedExpression = returnExpression.getReturnedExpression();
|
||||
if (returnedExpression == null) {
|
||||
hasEmptyReturn = true;
|
||||
}
|
||||
else {
|
||||
// the type should have been computed by getBlockReturnedType() above, but can be null, if returnExpression contains some error
|
||||
ContainerUtil.addIfNotNull(returnedExpressionTypes, context.trace.get(EXPRESSION_TYPE, returnedExpression));
|
||||
}
|
||||
}
|
||||
|
||||
if (hasEmptyReturn) {
|
||||
for (JetReturnExpression returnExpression : returnExpressions) {
|
||||
JetExpression returnedExpression = returnExpression.getReturnedExpression();
|
||||
if (returnedExpression != null) {
|
||||
JetType type = context.trace.get(EXPRESSION_TYPE, returnedExpression);
|
||||
if (type == null || !KotlinBuiltIns.isUnit(type)) {
|
||||
context.trace.report(RETURN_TYPE_MISMATCH.on(returnedExpression, components.builtIns.getUnitType()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return components.builtIns.getUnitType();
|
||||
}
|
||||
|
||||
List<JetType> returnedExpressionTypes = Lists.newArrayList(getTypesOfLocallyReturnedExpressions(
|
||||
functionLiteral, context.trace, collectReturns(bodyExpression)));
|
||||
ContainerUtil.addIfNotNull(returnedExpressionTypes, typeOfBodyExpression);
|
||||
|
||||
if (declaredReturnType != null) return declaredReturnType;
|
||||
if (returnedExpressionTypes.isEmpty()) return null;
|
||||
return CommonSupertypes.commonSupertype(returnedExpressionTypes);
|
||||
}
|
||||
|
||||
private List<JetType> getTypesOfLocallyReturnedExpressions(
|
||||
final JetFunctionLiteral functionLiteral,
|
||||
final BindingTrace trace,
|
||||
Collection<JetReturnExpression> returnExpressions
|
||||
private static Collection<JetReturnExpression> collectReturns(
|
||||
@NotNull final JetFunctionLiteral functionLiteral,
|
||||
@NotNull final BindingTrace trace
|
||||
) {
|
||||
return ContainerUtil.mapNotNull(returnExpressions, new Function<JetReturnExpression, JetType>() {
|
||||
@Override
|
||||
public JetType fun(JetReturnExpression returnExpression) {
|
||||
JetSimpleNameExpression label = returnExpression.getTargetLabel();
|
||||
if (label == null) {
|
||||
// No label => non-local return
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiElement labelTarget = trace.get(BindingContext.LABEL_TARGET, label);
|
||||
if (labelTarget != functionLiteral) {
|
||||
// Either a local return of inner lambda/function or a non-local return
|
||||
return null;
|
||||
}
|
||||
|
||||
JetExpression returnedExpression = returnExpression.getReturnedExpression();
|
||||
if (returnedExpression == null) {
|
||||
return components.builtIns.getUnitType();
|
||||
}
|
||||
JetType returnedType = trace.get(EXPRESSION_TYPE, returnedExpression);
|
||||
assert returnedType != null : "No type for returned expression: " + returnedExpression + ",\n" +
|
||||
"the type should have been computed by getBlockReturnedType() above\n" +
|
||||
JetPsiUtil.getElementTextWithContext(returnedExpression);
|
||||
return returnedType;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public static Collection<JetReturnExpression> collectReturns(@NotNull JetExpression expression) {
|
||||
Collection<JetReturnExpression> result = Lists.newArrayList();
|
||||
expression.accept(
|
||||
JetBlockExpression bodyExpression = functionLiteral.getBodyExpression();
|
||||
assert bodyExpression != null;
|
||||
bodyExpression.accept(
|
||||
new JetTreeVisitor<Collection<JetReturnExpression>>() {
|
||||
@Override
|
||||
public Void visitReturnExpression(
|
||||
@@ -362,6 +373,22 @@ public class ClosureExpressionsTypingVisitor extends ExpressionTypingVisitor {
|
||||
},
|
||||
result
|
||||
);
|
||||
return result;
|
||||
return ContainerUtil.mapNotNull(result, new Function<JetReturnExpression, JetReturnExpression>() {
|
||||
@Override
|
||||
public JetReturnExpression fun(@NotNull JetReturnExpression returnExpression) {
|
||||
JetSimpleNameExpression label = returnExpression.getTargetLabel();
|
||||
if (label == null) {
|
||||
// No label => non-local return
|
||||
return null;
|
||||
}
|
||||
|
||||
PsiElement labelTarget = trace.get(BindingContext.LABEL_TARGET, label);
|
||||
if (labelTarget != functionLiteral) {
|
||||
// Either a local return of inner lambda/function or a non-local return
|
||||
return null;
|
||||
}
|
||||
return returnExpression;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
+6
-1
@@ -47,6 +47,7 @@ import static org.jetbrains.kotlin.psi.PsiPackage.JetPsiFactory;
|
||||
import static org.jetbrains.kotlin.resolve.BindingContext.*;
|
||||
import static org.jetbrains.kotlin.resolve.calls.context.ContextDependency.INDEPENDENT;
|
||||
import static org.jetbrains.kotlin.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||
import static org.jetbrains.kotlin.types.TypeUtils.isDontCarePlaceholder;
|
||||
import static org.jetbrains.kotlin.types.TypeUtils.noExpectedType;
|
||||
import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.createCallForSpecialConstruction;
|
||||
import static org.jetbrains.kotlin.types.expressions.ControlStructureTypingUtils.createDataFlowInfoForArgumentsForIfCall;
|
||||
@@ -492,7 +493,11 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
|
||||
.replaceContextDependency(INDEPENDENT));
|
||||
}
|
||||
else {
|
||||
if (expectedType != null && !noExpectedType(expectedType) && !KotlinBuiltIns.isUnit(expectedType)) {
|
||||
if (expectedType != null &&
|
||||
!noExpectedType(expectedType) &&
|
||||
!KotlinBuiltIns.isUnit(expectedType) &&
|
||||
!isDontCarePlaceholder(expectedType)) // for lambda with implicit return type Unit
|
||||
{
|
||||
context.trace.report(RETURN_TYPE_MISMATCH.on(expression, expectedType));
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
fun test(a: Int) {
|
||||
run @f{
|
||||
if (a > 0) <!RETURN_TYPE_MISMATCH!>return@f<!>
|
||||
else return@f 1
|
||||
if (a > 0) return@f
|
||||
else return@f <!RETURN_TYPE_MISMATCH!>1<!>
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
val flag = true
|
||||
|
||||
val a = @b {
|
||||
if (flag) return@b <!RETURN_TYPE_MISMATCH!>4<!>
|
||||
return@b
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal val a: () -> kotlin.Unit
|
||||
internal val flag: kotlin.Boolean = true
|
||||
@@ -1,6 +1,6 @@
|
||||
fun test(a: Int) {
|
||||
val x = run @f{
|
||||
if (a > 0) <!RETURN_TYPE_MISMATCH!>return@f<!>
|
||||
if (a > 0) return@f
|
||||
else return@f Unit
|
||||
}
|
||||
x: Unit
|
||||
|
||||
+20
@@ -0,0 +1,20 @@
|
||||
val flag = true
|
||||
fun run<T>(f: () -> T): T { return f() }
|
||||
|
||||
// type of a was checked by txt
|
||||
val a = run { // () -> Unit
|
||||
return@run
|
||||
}
|
||||
|
||||
// Unit
|
||||
val b = run {
|
||||
if (flag) return@run
|
||||
<!UNUSED_EXPRESSION!>5<!>
|
||||
}
|
||||
|
||||
// Unit
|
||||
val c = run {
|
||||
if (flag) return@run
|
||||
|
||||
return@run <!RETURN_TYPE_MISMATCH!>4<!>
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
package
|
||||
|
||||
internal val a: kotlin.Unit
|
||||
internal val b: kotlin.Unit
|
||||
internal val c: kotlin.Unit
|
||||
internal val flag: kotlin.Boolean = true
|
||||
internal fun </*0*/ T> run(/*0*/ f: () -> T): T
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
val flag = true
|
||||
|
||||
// type of lambda was checked by txt
|
||||
val a = @b { // () -> Unit
|
||||
if (flag) return@b
|
||||
else <!UNUSED_EXPRESSION!>54<!>
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal val a: () -> kotlin.Unit
|
||||
internal val flag: kotlin.Boolean = true
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
val flag = true
|
||||
|
||||
// type of lambda was checked by txt
|
||||
val a = @l { // () -> Any
|
||||
if (flag) return@l 4
|
||||
return@l Unit
|
||||
}
|
||||
|
||||
val b = @l { // () -> Any
|
||||
if (flag) return@l Unit
|
||||
5
|
||||
}
|
||||
|
||||
val c = @l { // () -> Unit
|
||||
if (flag) return@l Unit
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
package
|
||||
|
||||
internal val a: () -> kotlin.Any
|
||||
internal val b: () -> kotlin.Any
|
||||
internal val c: () -> kotlin.Unit
|
||||
internal val flag: kotlin.Boolean = true
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
val a = @l {
|
||||
return@l <!UNRESOLVED_REFERENCE!>r<!>
|
||||
}
|
||||
|
||||
val b = @l {
|
||||
if ("" == "OK") return@l
|
||||
|
||||
return@l <!UNRESOLVED_REFERENCE, RETURN_TYPE_MISMATCH!>r<!>
|
||||
}
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
package
|
||||
|
||||
internal val a: () -> kotlin.Nothing
|
||||
internal val b: () -> kotlin.Unit
|
||||
@@ -4697,18 +4697,42 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LocalReturnSecondUnit.kt")
|
||||
public void testLocalReturnSecondUnit() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnSecondUnit.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LocalReturnUnit.kt")
|
||||
public void testLocalReturnUnit() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnit.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LocalReturnUnitAndDontCareType.kt")
|
||||
public void testLocalReturnUnitAndDontCareType() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitAndDontCareType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LocalReturnUnitWithBodyExpression.kt")
|
||||
public void testLocalReturnUnitWithBodyExpression() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnUnitWithBodyExpression.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LocalReturnWithExpectedType.kt")
|
||||
public void testLocalReturnWithExpectedType() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExpectedType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LocalReturnWithExplicitUnit.kt")
|
||||
public void testLocalReturnWithExplicitUnit() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnWithExplicitUnit.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("LocalReturnsWithExplicitReturnType.kt")
|
||||
public void testLocalReturnsWithExplicitReturnType() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/LocalReturnsWithExplicitReturnType.kt");
|
||||
@@ -4738,6 +4762,12 @@ public class JetDiagnosticsTestGenerated extends AbstractJetDiagnosticsTest {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/SmartCastWithExplicitType.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("unresolvedReferenceInReturnBlock.kt")
|
||||
public void testUnresolvedReferenceInReturnBlock() throws Exception {
|
||||
String fileName = JetTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/functionLiterals/return/unresolvedReferenceInReturnBlock.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user