'Else' is not required in 'when' if Unit is expected
This commit is contained in:
@@ -837,8 +837,7 @@ public class JetControlFlowProcessor {
|
||||
}
|
||||
}
|
||||
builder.bindLabel(doneLabel);
|
||||
boolean isWhenExhaust = WhenChecker.isWhenExhaustive(expression, trace);
|
||||
if (!hasElseOrIrrefutableBranch && !isWhenExhaust) {
|
||||
if (!hasElseOrIrrefutableBranch && WhenChecker.mustHaveElse(expression, trace)) {
|
||||
trace.report(NO_ELSE_IN_WHEN.on(expression));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,6 +26,7 @@ import org.jetbrains.jet.lang.psi.*;
|
||||
import org.jetbrains.jet.lang.resolve.BindingContext;
|
||||
import org.jetbrains.jet.lang.resolve.BindingTrace;
|
||||
import org.jetbrains.jet.lang.types.JetType;
|
||||
import org.jetbrains.jet.lang.types.lang.KotlinBuiltIns;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@@ -35,7 +36,16 @@ public final class WhenChecker {
|
||||
private WhenChecker() {
|
||||
}
|
||||
|
||||
public static boolean isWhenExhaustive(@NotNull JetWhenExpression expression, @NotNull BindingTrace trace) {
|
||||
public static boolean mustHaveElse(@NotNull JetWhenExpression expression, @NotNull BindingTrace trace) {
|
||||
JetType expectedType = trace.get(BindingContext.EXPECTED_EXPRESSION_TYPE, expression);
|
||||
boolean isUnit = expectedType != null && KotlinBuiltIns.getInstance().isUnit(expectedType);
|
||||
// Some "statements" are actually expressions returned from lambdas, their expected types are non-null
|
||||
boolean isStatement = Boolean.TRUE.equals(trace.get(BindingContext.STATEMENT, expression)) && expectedType == null;
|
||||
|
||||
return !isUnit && !isStatement && !isWhenExhaustive(expression, trace);
|
||||
}
|
||||
|
||||
private static boolean isWhenExhaustive(@NotNull JetWhenExpression expression, @NotNull BindingTrace trace) {
|
||||
JetExpression subjectExpression = expression.getSubjectExpression();
|
||||
if (subjectExpression == null) return false;
|
||||
JetType type = trace.get(BindingContext.EXPRESSION_TYPE, subjectExpression);
|
||||
|
||||
@@ -42,6 +42,7 @@ import static org.jetbrains.jet.lang.diagnostics.Errors.*;
|
||||
import static org.jetbrains.jet.lang.resolve.BindingContext.AUTOCAST;
|
||||
import static org.jetbrains.jet.lang.resolve.calls.context.ContextDependency.INDEPENDENT;
|
||||
import static org.jetbrains.jet.lang.types.TypeUtils.NO_EXPECTED_TYPE;
|
||||
import static org.jetbrains.jet.lang.types.TypeUtils.UNIT_EXPECTED_TYPE;
|
||||
import static org.jetbrains.jet.lang.types.TypeUtils.noExpectedType;
|
||||
|
||||
public class DataFlowUtils {
|
||||
@@ -162,9 +163,7 @@ public class DataFlowUtils {
|
||||
) {
|
||||
// non-block 'if' branches are wrapped in a block, but here genuine expressions (not wrappers) should be checked
|
||||
JetExpression expression = JetPsiUtil.unwrapFromBlock(possiblyWrappedInBlockExpression);
|
||||
if (!noExpectedType(expectedType)) {
|
||||
trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, expectedType);
|
||||
}
|
||||
recordExpectedType(trace, expression, expectedType);
|
||||
|
||||
if (expressionType == null || noExpectedType(expectedType) ||
|
||||
JetTypeChecker.INSTANCE.isSubtypeOf(expressionType, expectedType)) {
|
||||
@@ -196,6 +195,13 @@ public class DataFlowUtils {
|
||||
return expressionType;
|
||||
}
|
||||
|
||||
public static void recordExpectedType(@NotNull BindingTrace trace, @NotNull JetExpression expression, @NotNull JetType expectedType) {
|
||||
if (expectedType != NO_EXPECTED_TYPE) {
|
||||
JetType normalizeExpectedType = expectedType == UNIT_EXPECTED_TYPE ? KotlinBuiltIns.getInstance().getUnitType() : expectedType;
|
||||
trace.record(BindingContext.EXPECTED_EXPRESSION_TYPE, expression, normalizeExpectedType);
|
||||
}
|
||||
}
|
||||
|
||||
@NotNull
|
||||
public static JetTypeInfo checkStatementType(@NotNull JetExpression expression, @NotNull ResolutionContext context, @NotNull DataFlowInfo dataFlowInfo) {
|
||||
return JetTypeInfo.create(checkStatementType(expression, context), dataFlowInfo);
|
||||
|
||||
+4
-1
@@ -66,6 +66,8 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
}
|
||||
|
||||
public JetTypeInfo visitWhenExpression(JetWhenExpression expression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
|
||||
DataFlowUtils.recordExpectedType(contextWithExpectedType.trace, expression, contextWithExpectedType.expectedType);
|
||||
|
||||
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE).replaceContextDependency(INDEPENDENT);
|
||||
// TODO :change scope according to the bound value in the when header
|
||||
JetExpression subjectExpression = expression.getSubjectExpression();
|
||||
@@ -277,7 +279,8 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
|
||||
if (typeReferenceAfterIs == null) {
|
||||
return noChange(context);
|
||||
}
|
||||
JetType type = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReferenceAfterIs, context.trace, true);
|
||||
JetType type = context.expressionTypingServices.getTypeResolver().resolveType(context.scope, typeReferenceAfterIs, context.trace,
|
||||
true);
|
||||
if (!subjectType.isNullable() && type.isNullable()) {
|
||||
JetTypeElement element = typeReferenceAfterIs.getTypeElement();
|
||||
assert element instanceof JetNullableType : "element must be instance of " + JetNullableType.class.getName();
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fun box(): String {
|
||||
var r = "Fail 0"
|
||||
val x = 1
|
||||
val y: Unit = when (x) {
|
||||
1 -> { r = "OK" }
|
||||
2 -> { r = "Fail 1" }
|
||||
}
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
var r = "Fail 0"
|
||||
|
||||
fun foo(x: Int) {
|
||||
return when (x) {
|
||||
1 -> { r = "OK" }
|
||||
}
|
||||
}
|
||||
fun box(): String {
|
||||
foo(1)
|
||||
return r
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun box(): String {
|
||||
val x = 1
|
||||
when (x) {
|
||||
1 -> return "OK"
|
||||
}
|
||||
return "Fail 1"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
fun foo(x: Int) {
|
||||
val y: Unit = when (x) {
|
||||
2 -> {}
|
||||
3 -> {}
|
||||
}
|
||||
return y
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(x: Int): Any {
|
||||
val v = <!NO_ELSE_IN_WHEN!>when<!> (x) {
|
||||
2 -> 0
|
||||
}
|
||||
return v
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fun foo(x: Int) {
|
||||
r {
|
||||
when (x) {
|
||||
2 -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun r(f: () -> Unit) {
|
||||
f()
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fun foo(x: Int) {
|
||||
r {
|
||||
<!NO_ELSE_IN_WHEN!>when<!> (x) {
|
||||
2 -> 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun r(f: () -> Int) {
|
||||
f()
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
fun foo(x: Int): Any {
|
||||
return <!NO_ELSE_IN_WHEN!>when<!> (x) {
|
||||
2 -> 0
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(x: Int) {
|
||||
return when (x) {
|
||||
2 -> {}
|
||||
3 -> {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
fun foo(x: Int) {
|
||||
when (x) {
|
||||
2 -> {}
|
||||
3 -> {}
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -24,7 +24,7 @@ fun test() {
|
||||
val x = 1;
|
||||
val s = "";
|
||||
|
||||
<!NO_ELSE_IN_WHEN!>when<!> (x) {
|
||||
when (x) {
|
||||
<!INCOMPATIBLE_TYPES!>s<!> -> 1
|
||||
<!INCOMPATIBLE_TYPES!>""<!> -> 1
|
||||
x -> 1
|
||||
@@ -33,7 +33,7 @@ import org.jetbrains.jet.checkers.AbstractDiagnosticsTestWithEagerResolve;
|
||||
@InnerTestClasses({JetDiagnosticsTestGenerated.Tests.class, JetDiagnosticsTestGenerated.Script.class})
|
||||
public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEagerResolve {
|
||||
@TestMetadata("compiler/testData/diagnostics/tests")
|
||||
@InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.CallableReference.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.DelegatedProperty.class, Tests.Deparenthesize.class, Tests.Enum.class, Tests.Extensions.class, Tests.FunctionLiterals.class, Tests.Generics.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.Inner.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Library.class, Tests.NullabilityAndAutoCasts.class, Tests.NullableTypes.class, Tests.Numbers.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Recovery.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Resolve.class, Tests.Scopes.class, Tests.SenselessComparison.class, Tests.Shadowing.class, Tests.SmartCasts.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.ThisAndSuper.class, Tests.Varargs.class})
|
||||
@InnerTestClasses({Tests.Annotations.class, Tests.BackingField.class, Tests.CallableReference.class, Tests.Cast.class, Tests.CheckArguments.class, Tests.ControlFlowAnalysis.class, Tests.ControlStructures.class, Tests.DataClasses.class, Tests.DataFlow.class, Tests.DataFlowInfoTraversal.class, Tests.DeclarationChecks.class, Tests.DelegatedProperty.class, Tests.Deparenthesize.class, Tests.Enum.class, Tests.Extensions.class, Tests.FunctionLiterals.class, Tests.Generics.class, Tests.IncompleteCode.class, Tests.Inference.class, Tests.Infos.class, Tests.Inner.class, Tests.J_k.class, Tests.Jdk_annotations.class, Tests.Library.class, Tests.NullabilityAndAutoCasts.class, Tests.NullableTypes.class, Tests.Numbers.class, Tests.Objects.class, Tests.OperatorsOverloading.class, Tests.Overload.class, Tests.Override.class, Tests.Recovery.class, Tests.Redeclarations.class, Tests.Regressions.class, Tests.Resolve.class, Tests.Scopes.class, Tests.SenselessComparison.class, Tests.Shadowing.class, Tests.SmartCasts.class, Tests.Substitutions.class, Tests.Subtyping.class, Tests.ThisAndSuper.class, Tests.Varargs.class, Tests.When.class})
|
||||
public static class Tests extends AbstractDiagnosticsTestWithEagerResolve {
|
||||
@TestMetadata("Abstract.kt")
|
||||
public void testAbstract() throws Exception {
|
||||
@@ -524,11 +524,6 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
doTest("compiler/testData/diagnostics/tests/Variance.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("When.kt")
|
||||
public void testWhen() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/When.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/annotations")
|
||||
public static class Annotations extends AbstractDiagnosticsTestWithEagerResolve {
|
||||
public void testAllFilesPresentInAnnotations() throws Exception {
|
||||
@@ -5587,6 +5582,54 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
|
||||
}
|
||||
|
||||
@TestMetadata("compiler/testData/diagnostics/tests/when")
|
||||
public static class When extends AbstractDiagnosticsTestWithEagerResolve {
|
||||
public void testAllFilesPresentInWhen() throws Exception {
|
||||
JetTestUtils.assertAllTestsPresentByMetadata(this.getClass(), "org.jetbrains.jet.generators.tests.GenerateTests", new File("compiler/testData/diagnostics/tests/when"), Pattern.compile("^(.+)\\.kt$"), true);
|
||||
}
|
||||
|
||||
@TestMetadata("NoElseExpectedUnit.kt")
|
||||
public void testNoElseExpectedUnit() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/when/NoElseExpectedUnit.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NoElseNoExpectedType.kt")
|
||||
public void testNoElseNoExpectedType() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/when/NoElseNoExpectedType.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NoElseReturnedCoercionToUnit.kt")
|
||||
public void testNoElseReturnedCoercionToUnit() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/when/NoElseReturnedCoercionToUnit.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NoElseReturnedFromLambdaExpectedInt.kt")
|
||||
public void testNoElseReturnedFromLambdaExpectedInt() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/when/NoElseReturnedFromLambdaExpectedInt.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NoElseReturnedNonUnit.kt")
|
||||
public void testNoElseReturnedNonUnit() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/when/NoElseReturnedNonUnit.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NoElseReturnedUnit.kt")
|
||||
public void testNoElseReturnedUnit() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/when/NoElseReturnedUnit.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("NoElseWhenStatement.kt")
|
||||
public void testNoElseWhenStatement() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/when/NoElseWhenStatement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("When.kt")
|
||||
public void testWhen() throws Exception {
|
||||
doTest("compiler/testData/diagnostics/tests/when/When.kt");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static Test innerSuite() {
|
||||
TestSuite suite = new TestSuite("Tests");
|
||||
suite.addTestSuite(Tests.class);
|
||||
@@ -5633,6 +5676,7 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
|
||||
suite.addTestSuite(Subtyping.class);
|
||||
suite.addTestSuite(ThisAndSuper.class);
|
||||
suite.addTestSuite(Varargs.class);
|
||||
suite.addTestSuite(When.class);
|
||||
return suite;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4282,6 +4282,21 @@ public class BlackBoxCodegenTestGenerated extends AbstractBlackBoxCodegenTest {
|
||||
doTest("compiler/testData/codegen/box/when/matchNotNullAgainstNullable.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noElseAssigned.kt")
|
||||
public void testNoElseAssigned() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/when/noElseAssigned.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noElseInRetunedExpression.kt")
|
||||
public void testNoElseInRetunedExpression() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/when/noElseInRetunedExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("noElseInStatement.kt")
|
||||
public void testNoElseInStatement() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/when/noElseInStatement.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nullableWhen.kt")
|
||||
public void testNullableWhen() throws Exception {
|
||||
doTest("compiler/testData/codegen/box/when/nullableWhen.kt");
|
||||
|
||||
Reference in New Issue
Block a user