diff --git a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java index 63dafef84b9..d79a1fd7aac 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/cfg/JetFlowInformationProvider.java @@ -126,6 +126,8 @@ public class JetFlowInformationProvider { markUnusedExpressions(); + markIfWithoutElse(); + markWhenWithoutElse(); } @@ -692,6 +694,28 @@ public class JetFlowInformationProvider { ); } + public void markIfWithoutElse() { + PseudocodeTraverserKt.traverse( + pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1() { + @Override + public void execute(@NotNull Instruction instruction) { + PseudoValue value = instruction instanceof InstructionWithValue + ? ((InstructionWithValue) instruction).getOutputValue() + : null; + for (KtElement element : instruction.getOwner().getValueElements(value)) { + if (!(element instanceof KtIfExpression)) continue; + KtIfExpression ifExpression = (KtIfExpression) element; + if (ifExpression.getThen() != null && ifExpression.getElse() != null) continue; + + if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) { + trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression)); + } + } + } + } + ); + } + public void markWhenWithoutElse() { PseudocodeTraverserKt.traverse( pseudocode, FORWARD, new JetFlowInformationProvider.FunctionVoid1() { diff --git a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java index 75b760c51f6..ff487d46102 100644 --- a/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java +++ b/compiler/frontend/src/org/jetbrains/kotlin/types/expressions/DataFlowAnalyzer.java @@ -274,18 +274,12 @@ public class DataFlowAnalyzer { @Nullable public KotlinType checkImplicitCast(@Nullable KotlinType expressionType, @NotNull KtExpression expression, @NotNull ResolutionContext context, boolean isStatement) { boolean isIfExpression = expression instanceof KtIfExpression; - if (expressionType != null && (context.expectedType == NO_EXPECTED_TYPE || isIfExpression) - && context.contextDependency == INDEPENDENT && !isStatement - && (KotlinBuiltIns.isUnit(expressionType) || KotlinBuiltIns.isAnyOrNullableAny(expressionType)) - && !DynamicTypesKt.isDynamic(expressionType)) { - if (isIfExpression && KotlinBuiltIns.isUnit(expressionType)) { - KtIfExpression ifExpression = (KtIfExpression) expression; - if (ifExpression.getThen() == null || ifExpression.getElse() == null) { - context.trace.report(INVALID_IF_AS_EXPRESSION.on((KtIfExpression) expression)); - return expressionType; - } - } - else if (isIfExpression && context.expectedType != NO_EXPECTED_TYPE) { + if (expressionType != null + && (context.expectedType == NO_EXPECTED_TYPE || isIfExpression) + && context.contextDependency == INDEPENDENT && !isStatement + && (KotlinBuiltIns.isUnit(expressionType) || KotlinBuiltIns.isAnyOrNullableAny(expressionType)) + && !DynamicTypesKt.isDynamic(expressionType)) { + if (isIfExpression && KotlinBuiltIns.isUnit(expressionType) || isIfExpression && context.expectedType != NO_EXPECTED_TYPE) { return expressionType; } else { diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt b/compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt new file mode 100644 index 00000000000..c3afe38c124 --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt @@ -0,0 +1,72 @@ +fun idAny(x: Any) = x +fun id(x: T) = x +fun idUnit(x: Unit) = x + +class MList { + // MutableCollection.add returns Boolean, but nobody cares + fun add(): Boolean = true +} +val mlist = MList() + +fun work() {} + +val xx1 = if (true) 42 +val xx2: Unit = if (true) 42 +val xx3 = idAny(if (true) 42) +val xx4 = id(if (true) 42) +val xx5 = idUnit(if (true) 42) +val xx6 = null ?: if (true) 42 +val xx7 = "" + if (true) 42 + +val wxx1 = when { true -> 42 } +val wxx2: Unit = when { true -> 42 } +val wxx3 = idAny(when { true -> 42 }) +val wxx4 = id(when { true -> 42 }) +val wxx5 = idUnit(when { true -> 42 }) // TODO illegal expression +val wxx6 = null ?: when { true -> 42 } +val wxx7 = "" + when { true -> 42 } + +val fn1 = { if (true) 42 } +val fn2 = { if (true) mlist.add() } +val fn3 = { if (true) work() } +val fn4 = { when { true -> 42 } } +val fn5 = { when { true -> mlist.add() } } +val fn6 = { when { true -> work() } } + +val ufn1: () -> Unit = { if (true) 42 } +val ufn2: () -> Unit = { if (true) mlist.add() } +val ufn3: () -> Unit = { if (true) work() } +val ufn4: () -> Unit = { when { true -> 42 } } +val ufn5: () -> Unit = { when { true -> mlist.add() } } +val ufn6: () -> Unit = { when { true -> work() } } + +fun foo1(x: String?) { + "" + if (true) 42 + w@while (true) { + x ?: if (true) break + x ?: when { true -> break@w } + } +} + +fun foo2() { + if (true) { + mlist.add() + } + else if (true) { + mlist.add() + } + else if (true) { + mlist.add() + } + + when { + true -> mlist.add() + else -> when { + true -> mlist.add() + else -> when { + true -> mlist.add() + } + } + } +} + diff --git a/compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.txt b/compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.txt new file mode 100644 index 00000000000..89e7128aaef --- /dev/null +++ b/compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.txt @@ -0,0 +1,43 @@ +package + +public val fn1: () -> kotlin.Unit +public val fn2: () -> kotlin.Unit +public val fn3: () -> kotlin.Unit +public val fn4: () -> kotlin.Int +public val fn5: () -> kotlin.Boolean +public val fn6: () -> kotlin.Unit +public val mlist: MList +public val ufn1: () -> kotlin.Unit +public val ufn2: () -> kotlin.Unit +public val ufn3: () -> kotlin.Unit +public val ufn4: () -> kotlin.Unit +public val ufn5: () -> kotlin.Unit +public val ufn6: () -> kotlin.Unit +public val wxx1: kotlin.Int +public val wxx2: kotlin.Unit +public val wxx3: kotlin.Any +public val wxx4: kotlin.Int +public val wxx5: kotlin.Unit +public val wxx6: kotlin.Int +public val wxx7: kotlin.String +public val xx1: kotlin.Unit +public val xx2: kotlin.Unit +public val xx3: kotlin.Any +public val xx4: kotlin.Unit +public val xx5: kotlin.Unit +public val xx6: kotlin.Unit +public val xx7: kotlin.String +public fun foo1(/*0*/ x: kotlin.String?): kotlin.Unit +public fun foo2(): kotlin.Unit +public fun id(/*0*/ x: T): T +public fun idAny(/*0*/ x: kotlin.Any): kotlin.Any +public fun idUnit(/*0*/ x: kotlin.Unit): kotlin.Unit +public fun work(): kotlin.Unit + +public final class MList { + public constructor MList() + public final fun add(): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun equals(/*0*/ other: kotlin.Any?): kotlin.Boolean + public open override /*1*/ /*fake_override*/ fun hashCode(): kotlin.Int + public open override /*1*/ /*fake_override*/ fun toString(): kotlin.String +} diff --git a/compiler/testData/diagnostics/tests/controlStructures/kt4310.kt b/compiler/testData/diagnostics/tests/controlStructures/kt4310.kt index a66e432c47c..92239f7037d 100644 --- a/compiler/testData/diagnostics/tests/controlStructures/kt4310.kt +++ b/compiler/testData/diagnostics/tests/controlStructures/kt4310.kt @@ -4,7 +4,7 @@ fun test(a: Boolean, b: Boolean): Int { return if(a) { 1 } else { - if (b) { + if (b) { 3 } } diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElse.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElse.kt index 5492702c75d..7d02a8b3433 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElse.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElse.kt @@ -2,7 +2,7 @@ val flag = true // type of a was checked by txt val a/*: () -> Any*/ = l@ { - if (flag) return@l 4 + if (flag) return@l 4 } val b/*: () -> Int */ = l@ { diff --git a/compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt b/compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt index 278daa46611..ef1c77c23ba 100644 --- a/compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt +++ b/compiler/testData/diagnostics/tests/functionLiterals/return/IfWithoutElseWithExplicitType.kt @@ -1,7 +1,7 @@ val flag = true val a: () -> Int = l@ { - if (flag) return@l 4 + if (flag) return@l 4 } val b: () -> Unit = l@ { @@ -9,7 +9,7 @@ val b: () -> Unit = l@ { } val c: () -> Any = l@ { - if (flag) return@l 4 + if (flag) return@l 4 } val d: () -> Int = l@ { diff --git a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java index 29df31a2663..ea17b9bd5be 100644 --- a/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java +++ b/compiler/tests/org/jetbrains/kotlin/checkers/DiagnosticsTestGenerated.java @@ -3282,6 +3282,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest { doTest(fileName); } + @TestMetadata("ifWhenWithoutElse.kt") + public void testIfWhenWithoutElse() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt"); + doTest(fileName); + } + @TestMetadata("improperElseInExpression.kt") public void testImproperElseInExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/improperElseInExpression.kt");