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;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
@@ -701,10 +702,17 @@ public class ControlFlowInformationProvider {
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));
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() {
final Map<Instruction, Edges<InitControlFlowInfo>> initializers = pseudocodeVariablesData.getVariableInitializers();
PseudocodeTraverserKt.traverse(
@@ -741,6 +774,18 @@ public class ControlFlowInformationProvider {
for (KtElement element : instruction.getOwner().getValueElements(value)) {
if (!(element instanceof KtWhenExpression)) continue;
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;
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) {
components.dataFlowAnalyzer.recordExpectedType(contextWithExpectedType.trace, ifExpression, contextWithExpectedType.expectedType);
ExpressionTypingContext context = contextWithExpectedType.replaceExpectedType(NO_EXPECTED_TYPE);
KtExpression condition = ifExpression.getCondition();
DataFlowInfo conditionDataFlowInfo = checkCondition(context.scope, condition, context);
@@ -112,12 +114,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
? result.replaceJumpOutPossible(true).replaceJumpFlowInfo(conditionDataFlowInfo)
: result;
}
return TypeInfoFactoryKt.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast(
components.builtIns.getUnitType(), ifExpression,
contextWithExpectedType, isStatement
),
thenInfo.or(elseInfo)
);
return TypeInfoFactoryKt.createTypeInfo(components.builtIns.getUnitType(), thenInfo.or(elseInfo));
}
if (thenBranch == null) {
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
return TypeInfoFactoryKt
.createTypeInfo(components.dataFlowAnalyzer.checkImplicitCast(resultType, ifExpression, contextWithExpectedType, isStatement),
resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo);
return TypeInfoFactoryKt.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo);
}
@NotNull
@@ -199,15 +194,10 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
} else {
dataFlowInfo = typeInfo.getDataFlowInfo().or(otherInfo);
}
return components.dataFlowAnalyzer.checkImplicitCast(
components.dataFlowAnalyzer.checkType(
typeInfo.replaceType(components.builtIns.getUnitType()),
ifExpression,
context
),
return components.dataFlowAnalyzer.checkType(
typeInfo.replaceType(components.builtIns.getUnitType()),
ifExpression,
context,
isStatement
context
).replaceDataFlowInfo(dataFlowInfo);
}
@@ -271,29 +271,6 @@ public class DataFlowAnalyzer {
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
public KotlinTypeInfo illegalStatementType(@NotNull KtExpression expression, @NotNull ExpressionTypingContext context, @NotNull ExpressionTypingInternals facade) {
facade.checkStatementType(
@@ -162,12 +162,9 @@ public class PatternMatchingTypingVisitor extends ExpressionTypingVisitor {
if (isExhaustive && expression.getElseExpression() == null && KotlinBuiltIns.isNothing(resultType)) {
context.trace.record(BindingContext.IMPLICIT_EXHAUSTIVE_WHEN, expression);
}
resultType = components.dataFlowAnalyzer.checkType(resultType, expression, contextWithExpectedType);
}
return TypeInfoFactoryKt.createTypeInfo(expressionTypes.isEmpty() ? null : components.dataFlowAnalyzer.checkType(
components.dataFlowAnalyzer.checkImplicitCast(
resultType, expression,
contextWithExpectedType, isStatement),
expression, contextWithExpectedType),
return TypeInfoFactoryKt.createTypeInfo(resultType,
commonDataFlowInfo,
loopBreakContinuePossible,
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 {
@@ -18,13 +18,13 @@ val w = <!EXPRESSION_EXPECTED!>while (true) {}<!>
fun foo() {
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>z<!> = 2
val r = { // type fun(): Int is inferred
if (true) {
val r = { // type fun(): Any is inferred
<!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {
2
}
else {
z = 34
}
}<!>
}
val <!UNUSED_VARIABLE!>f<!>: ()-> Int = <!TYPE_MISMATCH!>r<!>
val <!UNUSED_VARIABLE!>g<!>: ()-> Any = r
@@ -73,11 +73,11 @@ fun testCoercionToUnit() {
var <!ASSIGNED_BUT_NEVER_ACCESSED_VARIABLE!>x<!> = 43
val checkType = {
if (true) {
<!IMPLICIT_CAST_TO_UNIT_OR_ANY!>if (true) {
x = 4
} else {
45
}
}<!>
}
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);
}
@TestMetadata("ifInResultOfLambda.kt")
public void testIfInResultOfLambda() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifInResultOfLambda.kt");
doTest(fileName);
}
@TestMetadata("ifWhenWithoutElse.kt")
public void testIfWhenWithoutElse() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/ifWhenWithoutElse.kt");
@@ -3402,6 +3408,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
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")
public void testWhen_kt234_kt973() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/controlStructures/when.kt234.kt973.kt");