KT-10646, KT-10647:

Move IMPLICIT_CAST_TO_UNIT_OR_ANY to ControlFlowInformationProvider
(where checks for 'if' and 'when' used as expressions are performed).
This commit is contained in:
Dmitry Petrov
2016-01-13 16:43:41 +03:00
parent f91c01919b
commit f54de08073
11 changed files with 103 additions and 54 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.kotlin.cfg; package org.jetbrains.kotlin.cfg;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.google.common.collect.Maps; import com.google.common.collect.Maps;
import com.google.common.collect.Sets; import com.google.common.collect.Sets;
@@ -701,10 +702,17 @@ public class ControlFlowInformationProvider {
for (KtElement element : instruction.getOwner().getValueElements(value)) { for (KtElement element : instruction.getOwner().getValueElements(value)) {
if (!(element instanceof KtIfExpression)) continue; if (!(element instanceof KtIfExpression)) continue;
KtIfExpression ifExpression = (KtIfExpression) element; KtIfExpression ifExpression = (KtIfExpression) element;
if (ifExpression.getThen() != null && ifExpression.getElse() != null) continue;
if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) { if (BindingContextUtilsKt.isUsedAsExpression(ifExpression, trace.getBindingContext())) {
trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression)); KtExpression thenExpression = ifExpression.getThen();
KtExpression elseExpression = ifExpression.getElse();
if (thenExpression == null || elseExpression == null) {
trace.report(INVALID_IF_AS_EXPRESSION.on(ifExpression));
}
else {
checkImplicitCastOnConditionalExpression(ifExpression, ImmutableList.of(thenExpression, elseExpression));
}
} }
} }
} }
@@ -712,6 +720,31 @@ public class ControlFlowInformationProvider {
); );
} }
private void checkImplicitCastOnConditionalExpression(
@NotNull KtExpression expression,
@NotNull Collection<KtExpression> branchExpressions
) {
KotlinType expectedExpressionType = trace.get(EXPECTED_EXPRESSION_TYPE, expression);
if (expectedExpressionType != null) return;
KotlinType expressionType = trace.getType(expression);
if (expressionType == null) {
return;
}
if (KotlinBuiltIns.isAnyOrNullableAny(expressionType)) {
for (KtExpression branchExpression : branchExpressions) {
KotlinType branchType = trace.getType(branchExpression);
if (branchType == null || KotlinBuiltIns.isAnyOrNullableAny(branchType)) {
return;
}
}
trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
}
else if (KotlinBuiltIns.isUnit(expressionType)) {
trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
}
}
public void markWhenWithoutElse() { public void markWhenWithoutElse() {
final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers(); final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
PseudocodeTraverserKt.traverse( PseudocodeTraverserKt.traverse(
@@ -741,6 +774,18 @@ public class ControlFlowInformationProvider {
for (KtElement element : instruction.getOwner().getValueElements(value)) { for (KtElement element : instruction.getOwner().getValueElements(value)) {
if (!(element instanceof KtWhenExpression)) continue; if (!(element instanceof KtWhenExpression)) continue;
KtWhenExpression whenExpression = (KtWhenExpression) element; KtWhenExpression whenExpression = (KtWhenExpression) element;
if (BindingContextUtilsKt.isUsedAsExpression(whenExpression, trace.getBindingContext())) {
List<KtExpression> branchExpressions = new ArrayList<KtExpression>(whenExpression.getEntries().size() + 1);
for (KtWhenEntry whenEntry : whenExpression.getEntries()) {
branchExpressions.add(whenEntry.getExpression());
}
if (whenExpression.getElseExpression() != null) {
branchExpressions.add(whenExpression.getElseExpression());
}
checkImplicitCastOnConditionalExpression(whenExpression, branchExpressions);
}
if (whenExpression.getElseExpression() != null) continue; if (whenExpression.getElseExpression() != null) continue;
BindingContext context = trace.getBindingContext(); BindingContext context = trace.getBindingContext();
@@ -761,6 +806,8 @@ public class ControlFlowInformationProvider {
} }
} }
} }
} }
} }
); );
@@ -91,6 +91,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
} }
public KotlinTypeInfo visitIfExpression(KtIfExpression ifExpression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) { public KotlinTypeInfo visitIfExpression(KtIfExpression ifExpression, ExpressionTypingContext contextWithExpectedType, boolean isStatement) {
components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, ifExpression, contextWithExpectedType.expectedType);
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE); ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE);
KtExpression condition = ifExpression.getCondition(); KtExpression condition = ifExpression.getCondition();
DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context); DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context);
@@ -112,12 +114,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo) ? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo)
: result; : result;
} }
return TypeInfoFactoryKt.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast( return TypeInfoFactoryKt.createTypeInfo(components.builtIns.getUnitType(), thenInfo.or(elseInfo));
components.builtIns.getUnitType(), ifExpression,
contextWithExpectedType, isStatement
),
thenInfo.or(elseInfo)
);
} }
if (thenBranch == null) { if (thenBranch == null) {
return getTypeInfoWhenOnlyOneBranchIsPresent( return getTypeInfoWhenOnlyOneBranchIsPresent(
@@ -173,9 +170,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
} }
// If break or continue was possible, take condition check info as the jump info // If break or continue was possible, take condition check info as the jump info
return TypeInfoFactoryKt return TypeInfoFactoryKt.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo);
.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast(resultType, ifExpression, contextWithExpectedType, isStatement),
resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo);
} }
@NotNull @NotNull
@@ -199,15 +194,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
} else { } else {
dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo); dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo);
} }
return components.dataFlowAnalyzer.checkImplicitCast( return components.dataFlowAnalyzer.checkType(
components.dataFlowAnalyzer.checkType( typeInfo.replaceType(components.builtIns.getUnitType()),
typeInfo.replaceType(components.builtIns.getUnitType()),
ifExpression,
context
),
ifExpression, ifExpression,
context, context
isStatement
).replaceDataFlowInfo(dataFlowInfo); ).replaceDataFlowInfo(dataFlowInfo);
} }
@@ -271,29 +271,6 @@ public class DataFlowAnalyzer {
return builtIns.getUnitType(); return builtIns.getUnitType();
} }
@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) || isIfExpression && context.expectedType != NO_EXPECTED_TYPE) {
return expressionType;
}
else {
context.trace.report(IMPLICIT_CAST_TO_UNIT_OR_ANY.on(expression, expressionType));
}
}
return expressionType;
}
@NotNull
public KotlinTypeInfo checkImplicitCast(@NotNull KotlinTypeInfo typeInfo, @NotNull KtExpression expression, @NotNull ResolutionContext context, boolean isStatement) {
return typeInfo.replaceType(checkImplicitCast(typeInfo.getType(), expression, context, isStatement));
}
@NotNull @NotNull
public KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) { public KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
facade.checkStatementType( facade.checkStatementType(
@@ -162,12 +162,9 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
if (isExhaustive && expression.getElseExpression() == null && KotlinBuiltIns.isNothing(resultType)) { if (isExhaustive && expression.getElseExpression() == null && KotlinBuiltIns.isNothing(resultType)) {
context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression); context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression);
} }
resultType = components.dataFlowAnalyzer.checkType(resultType, expression, contextWithExpectedType);
} }
return TypeInfoFactoryKt.createTypeInfo(expressionTypes.isEmpty() ? null : components.dataFlowAnalyzer.checkType( return TypeInfoFactoryKt.createTypeInfo(resultType,
components.dataFlowAnalyzer.checkImplicitCast(
resultType, expression,
contextWithExpectedType, isStatement),
expression, contextWithExpectedType),
commonDataFlowInfo, commonDataFlowInfo,
loopBreakContinuePossible, loopBreakContinuePossible,
contextWithExpectedType.dataFlowInfo); contextWithExpectedType.dataFlowInfo);
@@ -0,0 +1,7 @@
val test1 = { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) 1 else ""<!> }
val test2 = { { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) 1 else ""<!> } }
val test3: (Boolean) -> Any = { if (it) 1 else "" }
val test4: (Boolean) -> Any? = { if (it) 1 else "" }
@@ -0,0 +1,6 @@
package
public val test1: () -> kotlin.Any
public val test2: () -> () -> kotlin.Any
public val test3: (kotlin.Boolean) -> kotlin.Any
public val test4: (kotlin.Boolean) -> kotlin.Any?
@@ -17,12 +17,12 @@ fun example() {
}(); }();
{ {
if (true) {} else false <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {} else false<!>
}(); }();
{ {
if (true) true else {} <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) true else {}<!>
}() }()
fun t(): Boolean { fun t(): Boolean {
@@ -18,13 +18,13 @@ val w = <!EXPRESSION_EXPECTED!>while (true) {}<!>
fun foo() { fun foo() {
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>z<!> = 2 var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>z<!> = 2
val r = { // type fun(): Int is inferred val r = { // type fun(): Any is inferred
if (true) { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {
2 2
} }
else { else {
z = 34 z = 34
} }<!>
} }
val <!UNUSED_VARIABLE!>f<!>: ()-> Int = <!TYPE_MISMATCH!>r<!> val <!UNUSED_VARIABLE!>f<!>: ()-> Int = <!TYPE_MISMATCH!>r<!>
val <!UNUSED_VARIABLE!>g<!>: ()-> Any = r val <!UNUSED_VARIABLE!>g<!>: ()-> Any = r
@@ -73,11 +73,11 @@ fun testCoercionToUnit() {
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>x<!> = 43 var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>x<!> = 43
val checkType = { val checkType = {
if (true) { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {
x = 4 x = 4
} else { } else {
45 45
} }<!>
} }
val <!UNUSED_VARIABLE!>f<!> : () -> String = <!TYPE_MISMATCH!>checkType<!> val <!UNUSED_VARIABLE!>f<!> : () -> String = <!TYPE_MISMATCH!>checkType<!>
} }
@@ -0,0 +1,7 @@
val test1 = { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>when (true) { true -> 1; else -> "" }<!> }
val test2 = { { <!IMPLICIT_CAST_TO_UNIT_OR_ANY!>when (true) { true -> 1; else -> "" }<!> } }
val test3: (Boolean) -> Any = { when (true) { true -> 1; else -> "" } }
val test4: (Boolean) -> Any? = { when (true) { true -> 1; else -> "" } }
@@ -0,0 +1,6 @@
package
public val test1: () -> kotlin.Any
public val test2: () -> () -> kotlin.Any
public val test3: (kotlin.Boolean) -> kotlin.Any
public val test4: (kotlin.Boolean) -> kotlin.Any?
@@ -3318,6 +3318,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("ifInResultOfLambda.kt")
public void testIfInResultOfLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.kt");
doTest(fileName);
}
@TestMetadata("ifWhenWithoutElse.kt") @TestMetadata("ifWhenWithoutElse.kt")
public void testIfWhenWithoutElse() throws Exception { public void testIfWhenWithoutElse() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt");
@@ -3402,6 +3408,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("whenInResultOfLambda.kt")
public void testWhenInResultOfLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/whenInResultOfLambda.kt");
doTest(fileName);
}
@TestMetadata("when.kt234.kt973.kt") @TestMetadata("when.kt234.kt973.kt")
public void testWhen_kt234_kt973() throws Exception { public void testWhen_kt234_kt973() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.kt");