Fix data flow info after inner loops with continue-outer

ControlStructureTypingVisitor.containsBreak() is refined and now also takes
into account continue to outer loops
This commit is contained in:
Alexander Udalov
2012-11-16 15:42:14 +04:00
parent d1de446ae0
commit 6c7b32dc03
3 changed files with 62 additions and 9 deletions
@@ -16,6 +16,7 @@
package org.jetbrains.jet.lang.types.expressions; package org.jetbrains.jet.lang.types.expressions;
import com.google.common.collect.Lists;
import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pair;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.util.PsiTreeUtil; import com.intellij.psi.util.PsiTreeUtil;
@@ -171,32 +172,47 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
context.expressionTypingServices.getBlockReturnedTypeWithWritableScope(scopeToExtend, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(conditionInfo), context.trace); context.expressionTypingServices.getBlockReturnedTypeWithWritableScope(scopeToExtend, Collections.singletonList(body), CoercionStrategy.NO_COERCION, context.replaceDataFlowInfo(conditionInfo), context.trace);
} }
if (!containsBreak(expression, context)) { if (!containsJumpOutOfLoop(expression, context)) {
dataFlowInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, context).and(dataFlowInfo); dataFlowInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, context).and(dataFlowInfo);
} }
return DataFlowUtils.checkType(KotlinBuiltIns.getInstance().getUnitType(), expression, contextWithExpectedType, dataFlowInfo); return DataFlowUtils.checkType(KotlinBuiltIns.getInstance().getUnitType(), expression, contextWithExpectedType, dataFlowInfo);
} }
private boolean containsBreak(final JetLoopExpression loopExpression, final ExpressionTypingContext context) { private boolean containsJumpOutOfLoop(final JetLoopExpression loopExpression, 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 JetTreeVisitor<JetLoopExpression>() { loopExpression.accept(new JetTreeVisitor<List<JetLoopExpression>>() {
@Override @Override
public Void visitBreakExpression(JetBreakExpression breakExpression, JetLoopExpression outerLoop) { public Void visitBreakExpression(JetBreakExpression breakExpression, List<JetLoopExpression> outerLoops) {
JetSimpleNameExpression targetLabel = breakExpression.getTargetLabel(); JetSimpleNameExpression 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 && outerLoop == loopExpression)) { if (element == loopExpression || (targetLabel == null && outerLoops.get(outerLoops.size() - 1) == loopExpression)) {
result[0] = true; result[0] = true;
} }
return null; return null;
} }
@Override @Override
public Void visitLoopExpression(JetLoopExpression loopExpression, JetLoopExpression outerLoop) { public Void visitContinueExpression(JetContinueExpression expression, List<JetLoopExpression> outerLoops) {
return super.visitLoopExpression(loopExpression, loopExpression); // continue@someOuterLoop is also considered as break
JetSimpleNameExpression targetLabel = expression.getTargetLabel();
if (targetLabel != null) {
PsiElement element = context.trace.get(LABEL_TARGET, targetLabel);
if (element instanceof JetLoopExpression && !outerLoops.contains(element)) {
result[0] = true;
}
}
return null;
} }
}, loopExpression);
@Override
public Void visitLoopExpression(JetLoopExpression loopExpression, List<JetLoopExpression> outerLoops) {
List<JetLoopExpression> newOuterLoops = Lists.newArrayList(outerLoops);
newOuterLoops.add(loopExpression);
return super.visitLoopExpression(loopExpression, newOuterLoops);
}
}, Lists.newArrayList(loopExpression));
return result[0]; return result[0];
} }
@@ -238,7 +254,7 @@ public class ControlStructureTypingVisitor extends ExpressionTypingVisitor {
JetExpression condition = expression.getCondition(); JetExpression condition = expression.getCondition();
DataFlowInfo conditionDataFlowInfo = checkCondition(conditionScope, condition, context); DataFlowInfo conditionDataFlowInfo = checkCondition(conditionScope, condition, context);
DataFlowInfo dataFlowInfo; DataFlowInfo dataFlowInfo;
if (!containsBreak(expression, context)) { if (!containsJumpOutOfLoop(expression, context)) {
dataFlowInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, context).and(conditionDataFlowInfo); dataFlowInfo = DataFlowUtils.extractDataFlowInfoFromCondition(condition, false, context).and(conditionDataFlowInfo);
} }
else { else {
@@ -0,0 +1,32 @@
fun whileLoop(x: Int?) {
@outer while (x != 0) {
while (x != 1) {
if (x == 2) continue @outer
}
<!TYPE_MISMATCH!>x<!> : Int
}
x : Int
}
fun doWhileLoop(x: Int?) {
@outer while (x != 0) {
do {
if (x == 2) continue @outer
} while (x == null)
<!TYPE_MISMATCH!>x<!> : Int
}
x : Int
}
fun whileLoopContinueInnerOuter(x: Int?) {
@outer while (x != 0) {
@inner while (x != 1) {
while (x != 2) {
if (x == 3) continue @inner
}
<!TYPE_MISMATCH!>x<!> : Int
}
x : Int
}
x : Int
}
@@ -1221,6 +1221,11 @@ public class JetDiagnosticsTestGenerated extends AbstractDiagnosticsTestWithEage
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Condition.kt"); doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/Condition.kt");
} }
@TestMetadata("ContinueOuterLoop.kt")
public void testContinueOuterLoop() throws Exception {
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/ContinueOuterLoop.kt");
}
@TestMetadata("DeepIf.kt") @TestMetadata("DeepIf.kt")
public void testDeepIf() throws Exception { public void testDeepIf() throws Exception {
doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt"); doTest("compiler/testData/diagnostics/tests/dataFlowInfoTraversal/DeepIf.kt");