KT-10139: any if without else used in expression is an error

regardless of expected type: it can't be an expression, even of type Unit.

 #KT-10139 Fixed
This commit is contained in:
Dmitry Petrov
2015-11-27 16:06:11 +03:00
parent b0dab4c67a
commit 1a3a296827
8 changed files with 155 additions and 16 deletions
@@ -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<Instruction>() {
@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<Instruction>() {
@@ -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 {
@@ -0,0 +1,72 @@
fun idAny(x: Any) = x
fun <T> id(x: T) = x
fun idUnit(x: Unit) = x
class MList {
// MutableCollection<T>.add returns Boolean, but nobody cares
fun add(): Boolean = true
}
val mlist = MList()
fun work() {}
val xx1 = <!INVALID_IF_AS_EXPRESSION!>if (true) 42<!>
val xx2: Unit = <!INVALID_IF_AS_EXPRESSION!>if (true) 42<!>
val xx3 = idAny(<!INVALID_IF_AS_EXPRESSION!>if (true) 42<!>)
val xx4 = id(<!INVALID_IF_AS_EXPRESSION!>if (true) 42<!>)
val xx5 = idUnit(<!INVALID_IF_AS_EXPRESSION!>if (true) 42<!>)
val xx6 = null ?: <!INVALID_IF_AS_EXPRESSION!>if (true) 42<!>
val xx7 = "" + <!INVALID_IF_AS_EXPRESSION!>if (true) 42<!>
val wxx1 = <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val wxx2: Unit = when { true -> <!CONSTANT_EXPECTED_TYPE_MISMATCH!>42<!> }
val wxx3 = idAny(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
val wxx4 = id(<!NO_ELSE_IN_WHEN!>when<!> { true -> 42 })
val wxx5 = idUnit(when { true -> 42 }) // TODO illegal expression
val wxx6 = null ?: <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val wxx7 = "" + <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 }
val fn1 = { if (true) <!UNUSED_EXPRESSION!>42<!> }
val fn2 = { if (true) mlist.add() }
val fn3 = { if (true) work() }
val fn4 = { <!NO_ELSE_IN_WHEN!>when<!> { true -> 42 } }
val fn5 = { <!NO_ELSE_IN_WHEN!>when<!> { true -> mlist.add() } }
val fn6 = { when { true -> work() } }
val ufn1: () -> Unit = { if (true) <!UNUSED_EXPRESSION!>42<!> }
val ufn2: () -> Unit = { if (true) mlist.add() }
val ufn3: () -> Unit = { if (true) work() }
val ufn4: () -> Unit = { when { true -> <!UNUSED_EXPRESSION!>42<!> } }
val ufn5: () -> Unit = { when { true -> mlist.add() } }
val ufn6: () -> Unit = { when { true -> work() } }
fun foo1(x: String?) {
"" + <!INVALID_IF_AS_EXPRESSION!>if (true) 42<!>
w@while (true) {
x ?: if (true) break
x ?: <!NO_ELSE_IN_WHEN!>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()
}
}
}
}
@@ -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 </*0*/ T> 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
}
@@ -4,7 +4,7 @@ fun test(a: Boolean, b: Boolean): Int {
return if(a) {
1
} else {
<!TYPE_MISMATCH!>if (b) {
<!TYPE_MISMATCH, INVALID_IF_AS_EXPRESSION!>if (b) {
3
}<!>
}
@@ -2,7 +2,7 @@ val flag = true
// type of a was checked by txt
val a/*: () -> Any*/ = l@ {
if (flag) return@l 4
<!INVALID_IF_AS_EXPRESSION!>if (flag) return@l 4<!>
}
val b/*: () -> Int */ = l@ {
@@ -1,7 +1,7 @@
val flag = true
val a: () -> Int = l@ {
<!TYPE_MISMATCH!>if (flag) return@l 4<!>
<!TYPE_MISMATCH, INVALID_IF_AS_EXPRESSION!>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
<!INVALID_IF_AS_EXPRESSION!>if (flag) return@l 4<!>
}
val d: () -> Int = l@ {
@@ -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");