mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-07 05:09:37 +07:00
Create pending edge from continue when loop has at least one iteration (PY-29767)
There is no edge between loop statement and next after loop instruction when loop has at least one iteration so `continue` is marked as one more last instruction in the loop
This commit is contained in:
@@ -474,7 +474,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
boolean isStaticallyTrue = false;
|
||||
if (condition != null) {
|
||||
condition.accept(this);
|
||||
isStaticallyTrue = PyEvaluator.evaluateAsBooleanNoResolve(condition, false);
|
||||
isStaticallyTrue = loopHasAtLeastOneIteration(node);
|
||||
}
|
||||
final Instruction head = myBuilder.prevInstruction;
|
||||
final PyElsePart elsePart = node.getElsePart();
|
||||
@@ -509,7 +509,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
}
|
||||
final Instruction head = myBuilder.prevInstruction;
|
||||
final PyElsePart elsePart = node.getElsePart();
|
||||
if (elsePart == null && !PyEvaluator.evaluateAsBooleanNoResolve(source, false)) {
|
||||
if (elsePart == null && !loopHasAtLeastOneIteration(node)) {
|
||||
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
||||
}
|
||||
final PyStatementList list = forPart.getStatementList();
|
||||
@@ -544,6 +544,16 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
myBuilder.flowAbrupted();
|
||||
}
|
||||
|
||||
private static boolean loopHasAtLeastOneIteration(@NotNull PyLoopStatement loopStatement) {
|
||||
final PyExpression expression = loopStatement instanceof PyForStatement
|
||||
? ((PyForStatement)loopStatement).getForPart().getSource()
|
||||
: loopStatement instanceof PyWhileStatement
|
||||
? ((PyWhileStatement)loopStatement).getWhilePart().getCondition()
|
||||
: null;
|
||||
|
||||
return PyEvaluator.evaluateAsBooleanNoResolve(expression, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyBreakStatement(final PyBreakStatement node) {
|
||||
myBuilder.startNode(node);
|
||||
@@ -569,6 +579,15 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
else {
|
||||
myBuilder.addPendingEdge(null, null);
|
||||
}
|
||||
|
||||
// There is no edge between loop statement and next after loop instruction
|
||||
// when loop has at least one iteration
|
||||
// so `continue` is marked as one more last instruction in the loop
|
||||
// see visitPyWhileStatement
|
||||
// see visitPyForStatement
|
||||
if (loopHasAtLeastOneIteration(loop)) {
|
||||
myBuilder.addPendingEdge(loop, myBuilder.prevInstruction);
|
||||
}
|
||||
}
|
||||
myBuilder.flowAbrupted();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import sys
|
||||
|
||||
for s in "abc":
|
||||
if len(s) == 1:
|
||||
continue
|
||||
sys.exit(0)
|
||||
raise Exception("the end")
|
||||
@@ -0,0 +1,16 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyImportStatement
|
||||
2(3) WRITE ACCESS: sys
|
||||
3(4) element: PyForStatement
|
||||
4(5) element: PyTargetExpression: s
|
||||
5(6) WRITE ACCESS: s
|
||||
6(7) element: PyIfStatement
|
||||
7(8) READ ACCESS: len
|
||||
8(9,11) READ ACCESS: s
|
||||
9(10) element: PyStatementList. Condition: len(s) == 1:true
|
||||
10(3,13) element: PyContinueStatement
|
||||
11(12) element: PyExpressionStatement
|
||||
12(15) READ ACCESS: sys
|
||||
13(14) element: PyRaiseStatement
|
||||
14(15) READ ACCESS: Exception
|
||||
15() element: null
|
||||
@@ -23,7 +23,7 @@
|
||||
22(23,37) element: PyIfPartElif. Condition: x == 0:false
|
||||
23(24,26,37) READ ACCESS: x
|
||||
24(25) element: PyStatementList. Condition: x == 1:true
|
||||
25(7,37) element: PyContinueStatement
|
||||
25(7,37,40) element: PyContinueStatement
|
||||
26(27,37) element: PyIfPartElif. Condition: x == 1:false
|
||||
27(28,31,37) READ ACCESS: x
|
||||
28(29) element: PyStatementList. Condition: x == 2:true
|
||||
|
||||
@@ -284,6 +284,11 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-29767
|
||||
public void testContinueInPositiveIteration() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTestFirstStatement() {
|
||||
final String testName = getTestName(false).toLowerCase();
|
||||
configureByFile(testName + ".py");
|
||||
|
||||
@@ -127,6 +127,19 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase {
|
||||
);
|
||||
}
|
||||
|
||||
// PY-29767
|
||||
public void testContinueInPositiveIterationWithExitPoint() {
|
||||
doTestByText(
|
||||
"import sys\n" +
|
||||
"\n" +
|
||||
"for s in \"abc\":\n" +
|
||||
" if len(s) == 1:\n" +
|
||||
" continue\n" +
|
||||
" sys.exit(0)\n" +
|
||||
"raise Exception(\"the end\")"
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user