Bug fix: jump out of a loop inside if condition
This commit is contained in:
+12
-6
@@ -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];
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -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()
|
||||||
|
}
|
||||||
+3
@@ -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")
|
||||||
|
|||||||
Reference in New Issue
Block a user