Bug fix: jump out of a loop inside if condition

This commit is contained in:
Mikhail Glukhikh
2016-01-20 15:27:01 +03:00
parent b3c5760eb5
commit 859059d714
4 changed files with 34 additions and 6 deletions
@@ -96,6 +96,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
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);
boolean loopBreakContinuePossibleInCondition = condition != null && containsJumpOutOfLoop(condition, context);
KtExpression elseBranch = ifExpression.getElse(); KtExpression elseBranch = ifExpression.getElse();
KtExpression thenBranch = ifExpression.getThen(); KtExpression thenBranch = ifExpression.getThen();
@@ -150,7 +151,8 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
elseDataFlowInfo = elseDataFlowInfo.assign(resultValue, elseValue); elseDataFlowInfo = elseDataFlowInfo.assign(resultValue, elseValue);
} }
boolean loopBreakContinuePossible = thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible(); boolean loopBreakContinuePossible = loopBreakContinuePossibleInCondition ||
thenTypeInfo.getJumpOutPossible() || elseTypeInfo.getJumpOutPossible();
boolean jumpInThen = thenType != null && KotlinBuiltIns.isNothing(thenType); boolean jumpInThen = thenType != null && KotlinBuiltIns.isNothing(thenType);
boolean jumpInElse = elseType != null && KotlinBuiltIns.isNothing(elseType); boolean jumpInElse = elseType != null && KotlinBuiltIns.isNothing(elseType);
@@ -170,7 +172,8 @@ 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.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible, conditionDataFlowInfo); return TypeInfoFactoryKt.createTypeInfo(resultType, resultDataFlowInfo, loopBreakContinuePossible,
loopBreakContinuePossibleInCondition ? context.dataFlowInfo : conditionDataFlowInfo);
} }
@NotNull @NotNull
@@ -253,16 +256,17 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
.replaceDataFlowInfo(dataFlowInfo); .replaceDataFlowInfo(dataFlowInfo);
} }
private boolean containsJumpOutOfLoop(final KtLoopExpression loopExpression, final ExpressionTypingContext context) { private boolean containsJumpOutOfLoop(@NotNull final KtExpression expression, final ExpressionTypingContext context) {
final boolean[] result = new boolean[1]; final boolean[] result = new boolean[1];
result[0] = false; result[0] = false;
//todo breaks in inline function literals //todo breaks in inline function literals
loopExpression.accept(new KtTreeVisitor<List<KtLoopExpression>>() { expression.accept(new KtTreeVisitor<List<KtLoopExpression>>() {
@Override @Override
public Void visitBreakExpression(@NotNull KtBreakExpression breakExpression, List<KtLoopExpression> outerLoops) { public Void visitBreakExpression(@NotNull KtBreakExpression breakExpression, List<KtLoopExpression> outerLoops) {
KtSimpleNameExpression targetLabel = breakExpression.getTargetLabel(); KtSimpleNameExpression targetLabel = breakExpression.getTargetLabel();
PsiElement element = targetLabel != null ? context.trace.get(LABEL_TARGET, targetLabel) : null; PsiElement element = targetLabel != null ? context.trace.get(LABEL_TARGET, targetLabel) : null;
if (element == loopExpression || (targetLabel == null && outerLoops.get(outerLoops.size() - 1) == loopExpression)) { if (outerLoops.isEmpty() || element == expression ||
(targetLabel == null && outerLoops.get(outerLoops.size() - 1) == expression)) {
result[0] = true; result[0] = true;
} }
return null; return null;
@@ -287,7 +291,9 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
newOuterLoops.add(loopExpression); newOuterLoops.add(loopExpression);
return super.visitLoopExpression(loopExpression, newOuterLoops); return super.visitLoopExpression(loopExpression, newOuterLoops);
} }
}, Lists.newArrayList(loopExpression)); }, expression instanceof KtLoopExpression
? Lists.newArrayList((KtLoopExpression) expression)
: Lists.<KtLoopExpression>newArrayList());
return result[0]; return result[0];
} }
@@ -0,0 +1,13 @@
fun checkJump(x: Int?, y: Int?) {
while (true) {
if (x ?: break == 0) {
y!!
} else {
y!!
}
// Ok
<!DEBUG_INFO_SMARTCAST!>y<!>.hashCode()
}
// Smart cast here is erroneous: y is nullable
y<!UNSAFE_CALL!>.<!>hashCode()
}
@@ -0,0 +1,3 @@
package
public fun checkJump(/*0*/ x: kotlin.Int?, /*1*/ y: kotlin.Int?): kotlin.Unit
@@ -16522,6 +16522,12 @@ public class DiagnosticsTestGenerated extends AbstractDiagnosticsTest {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.kt"); String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/whileTrueReturn.kt");
doTest(fileName); doTest(fileName);
} }
@TestMetadata("WhileTrueWithBreakInIfCondition.kt")
public void testWhileTrueWithBreakInIfCondition() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("compiler/testData/diagnostics/tests/smartCasts/loops/WhileTrueWithBreakInIfCondition.kt");
doTest(fileName);
}
} }
@TestMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals") @TestMetadata("compiler/testData/diagnostics/tests/smartCasts/objectLiterals")