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;
|
boolean isStaticallyTrue = false;
|
||||||
if (condition != null) {
|
if (condition != null) {
|
||||||
condition.accept(this);
|
condition.accept(this);
|
||||||
isStaticallyTrue = PyEvaluator.evaluateAsBooleanNoResolve(condition, false);
|
isStaticallyTrue = loopHasAtLeastOneIteration(node);
|
||||||
}
|
}
|
||||||
final Instruction head = myBuilder.prevInstruction;
|
final Instruction head = myBuilder.prevInstruction;
|
||||||
final PyElsePart elsePart = node.getElsePart();
|
final PyElsePart elsePart = node.getElsePart();
|
||||||
@@ -509,7 +509,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
|||||||
}
|
}
|
||||||
final Instruction head = myBuilder.prevInstruction;
|
final Instruction head = myBuilder.prevInstruction;
|
||||||
final PyElsePart elsePart = node.getElsePart();
|
final PyElsePart elsePart = node.getElsePart();
|
||||||
if (elsePart == null && !PyEvaluator.evaluateAsBooleanNoResolve(source, false)) {
|
if (elsePart == null && !loopHasAtLeastOneIteration(node)) {
|
||||||
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
||||||
}
|
}
|
||||||
final PyStatementList list = forPart.getStatementList();
|
final PyStatementList list = forPart.getStatementList();
|
||||||
@@ -544,6 +544,16 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
|||||||
myBuilder.flowAbrupted();
|
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
|
@Override
|
||||||
public void visitPyBreakStatement(final PyBreakStatement node) {
|
public void visitPyBreakStatement(final PyBreakStatement node) {
|
||||||
myBuilder.startNode(node);
|
myBuilder.startNode(node);
|
||||||
@@ -569,6 +579,15 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
|||||||
else {
|
else {
|
||||||
myBuilder.addPendingEdge(null, null);
|
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();
|
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
|
22(23,37) element: PyIfPartElif. Condition: x == 0:false
|
||||||
23(24,26,37) READ ACCESS: x
|
23(24,26,37) READ ACCESS: x
|
||||||
24(25) element: PyStatementList. Condition: x == 1:true
|
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
|
26(27,37) element: PyIfPartElif. Condition: x == 1:false
|
||||||
27(28,31,37) READ ACCESS: x
|
27(28,31,37) READ ACCESS: x
|
||||||
28(29) element: PyStatementList. Condition: x == 2:true
|
28(29) element: PyStatementList. Condition: x == 2:true
|
||||||
|
|||||||
@@ -284,6 +284,11 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
|
|||||||
doTest();
|
doTest();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PY-29767
|
||||||
|
public void testContinueInPositiveIteration() {
|
||||||
|
doTest();
|
||||||
|
}
|
||||||
|
|
||||||
private void doTestFirstStatement() {
|
private void doTestFirstStatement() {
|
||||||
final String testName = getTestName(false).toLowerCase();
|
final String testName = getTestName(false).toLowerCase();
|
||||||
configureByFile(testName + ".py");
|
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
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
protected Class<? extends PyInspection> getInspectionClass() {
|
protected Class<? extends PyInspection> getInspectionClass() {
|
||||||
|
|||||||
Reference in New Issue
Block a user