mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Update CFG to honour condition values in if statements (PY-24750)
This commit is contained in:
@@ -23,6 +23,7 @@ import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiNamedElement;
|
||||
import com.intellij.psi.util.PsiTreeUtil;
|
||||
import com.intellij.psi.util.QualifiedName;
|
||||
import com.intellij.util.SmartList;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import com.jetbrains.python.PyNames;
|
||||
import com.jetbrains.python.PyTokenTypes;
|
||||
@@ -325,6 +326,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
myBuilder.startNode(node);
|
||||
final PyIfPart ifPart = node.getIfPart();
|
||||
PyExpression condition = ifPart.getCondition();
|
||||
final List<Boolean> conditionResults = new SmartList<>(PyEvaluator.evaluateAsBooleanNoResolve(condition));
|
||||
PyTypeAssertionEvaluator assertionEvaluator = new PyTypeAssertionEvaluator();
|
||||
if (condition != null) {
|
||||
condition.accept(this);
|
||||
@@ -334,7 +336,9 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
PyElement lastCondition = condition;
|
||||
|
||||
List<Pair<PsiElement, Instruction>> lastBranchingPoints = getPrevInstructions(condition);
|
||||
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
|
||||
if (ContainerUtil.getLastItem(conditionResults) != Boolean.FALSE) {
|
||||
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
|
||||
}
|
||||
myBuilder.prevInstruction = null;
|
||||
|
||||
final PyStatementList thenStatements = ifPart.getStatementList();
|
||||
@@ -352,11 +356,14 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
||||
for (final PyIfPart part : node.getElifParts()) {
|
||||
// Set the head as the false branch
|
||||
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
|
||||
if (!ContainerUtil.exists(conditionResults, Boolean.TRUE::equals)) {
|
||||
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
|
||||
}
|
||||
myBuilder.prevInstruction = null;
|
||||
|
||||
myBuilder.startConditionalNode(part, lastCondition, false);
|
||||
condition = part.getCondition();
|
||||
conditionResults.add(PyEvaluator.evaluateAsBooleanNoResolve(condition));
|
||||
assertionEvaluator = new PyTypeAssertionEvaluator();
|
||||
if (condition != null) {
|
||||
lastCondition = condition;
|
||||
@@ -365,11 +372,13 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
lastBranchingPoints = getPrevInstructions(lastCondition);
|
||||
}
|
||||
// Set the head as the last instruction of condition
|
||||
getPrevInstructions(lastCondition).forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
|
||||
if (ContainerUtil.getLastItem(conditionResults) != Boolean.FALSE) {
|
||||
getPrevInstructions(lastCondition).forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
|
||||
}
|
||||
myBuilder.prevInstruction = null;
|
||||
|
||||
myBuilder.startConditionalNode(part, lastCondition, true);
|
||||
final PyStatementList statementList = part.getStatementList();
|
||||
myBuilder.startConditionalNode(statementList, lastCondition, true);
|
||||
InstructionBuilder.addAssertInstructions(myBuilder, assertionEvaluator);
|
||||
statementList.accept(this);
|
||||
myBuilder.processPending((pendingScope, instruction) -> {
|
||||
@@ -391,15 +400,18 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
final PyElsePart elseBranch = node.getElsePart();
|
||||
if (elseBranch != null) {
|
||||
// Set the head as the false branch
|
||||
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
|
||||
if (!ContainerUtil.exists(conditionResults, Boolean.TRUE::equals)) {
|
||||
lastBranchingPoints.forEach(pair -> myBuilder.addPendingEdge(pair.getFirst(), pair.getSecond()));
|
||||
}
|
||||
myBuilder.prevInstruction = null;
|
||||
|
||||
myBuilder.startConditionalNode(elseBranch, lastCondition, false);
|
||||
final PyStatementList statementList = elseBranch.getStatementList();
|
||||
myBuilder.startConditionalNode(statementList, lastCondition, false);
|
||||
InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator);
|
||||
elseBranch.accept(this);
|
||||
statementList.accept(this);
|
||||
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
||||
}
|
||||
else {
|
||||
else if (ContainerUtil.getLastItem(conditionResults) != Boolean.TRUE) {
|
||||
myBuilder.prevInstruction = null;
|
||||
final Instruction instruction =
|
||||
ContainerUtil.getFirstItem(InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator));
|
||||
|
||||
@@ -1,19 +1,19 @@
|
||||
if 0:
|
||||
if 0j:
|
||||
pass
|
||||
|
||||
if 1:
|
||||
if 1j:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
|
||||
if 2:
|
||||
if 2j:
|
||||
pass
|
||||
elif 3:
|
||||
elif 3j:
|
||||
pass
|
||||
|
||||
if 4:
|
||||
if 4j:
|
||||
pass
|
||||
elif 5:
|
||||
elif 5j:
|
||||
pass
|
||||
else:
|
||||
1
|
||||
|
||||
@@ -1,17 +1,17 @@
|
||||
0(1) element: null
|
||||
1(2,3) element: PyIfStatement
|
||||
2(3) element: PyStatementList. Condition: 0:true
|
||||
2(3) element: PyStatementList. Condition: 0j:true
|
||||
3(4,5) element: PyIfStatement
|
||||
4(6) element: PyStatementList. Condition: 1:true
|
||||
5(6) element: PyElsePart. Condition: 1:false
|
||||
4(6) element: PyStatementList. Condition: 1j:true
|
||||
5(6) element: PyStatementList. Condition: 1j:false
|
||||
6(7,8) element: PyIfStatement
|
||||
7(10) element: PyStatementList. Condition: 2:true
|
||||
8(9,10) element: PyIfPartElif. Condition: 2:false
|
||||
9(10) element: PyIfPartElif. Condition: 3:true
|
||||
7(10) element: PyStatementList. Condition: 2j:true
|
||||
8(9,10) element: PyIfPartElif. Condition: 2j:false
|
||||
9(10) element: PyStatementList. Condition: 3j:true
|
||||
10(11,12) element: PyIfStatement
|
||||
11(16) element: PyStatementList. Condition: 4:true
|
||||
12(13,14) element: PyIfPartElif. Condition: 4:false
|
||||
13(16) element: PyIfPartElif. Condition: 5:true
|
||||
14(15) element: PyElsePart. Condition: 5:false
|
||||
11(16) element: PyStatementList. Condition: 4j:true
|
||||
12(13,14) element: PyIfPartElif. Condition: 4j:false
|
||||
13(16) element: PyStatementList. Condition: 5j:true
|
||||
14(15) element: PyStatementList. Condition: 5j:false
|
||||
15(16) element: PyExpressionStatement
|
||||
16() element: null
|
||||
@@ -0,0 +1,11 @@
|
||||
if c:
|
||||
pass
|
||||
elif False:
|
||||
a = 1
|
||||
|
||||
if d:
|
||||
pass
|
||||
elif False:
|
||||
b = 1
|
||||
else:
|
||||
pass
|
||||
@@ -0,0 +1,23 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyIfStatement
|
||||
2(3,5) READ ACCESS: c
|
||||
3(4) element: PyStatementList. Condition: c:true
|
||||
4(11) ASSERTTYPE ACCESS: c
|
||||
5(6) element: PyIfPartElif. Condition: c:false
|
||||
6(11) READ ACCESS: False
|
||||
7(8) element: PyStatementList. Condition: False:true
|
||||
8(9) ASSERTTYPE ACCESS: False
|
||||
9(10) element: PyAssignmentStatement
|
||||
10(11) WRITE ACCESS: a
|
||||
11(12) element: PyIfStatement
|
||||
12(13,15) READ ACCESS: d
|
||||
13(14) element: PyStatementList. Condition: d:true
|
||||
14(22) ASSERTTYPE ACCESS: d
|
||||
15(16) element: PyIfPartElif. Condition: d:false
|
||||
16(21) READ ACCESS: False
|
||||
17(18) element: PyStatementList. Condition: False:true
|
||||
18(19) ASSERTTYPE ACCESS: False
|
||||
19(20) element: PyAssignmentStatement
|
||||
20(22) WRITE ACCESS: b
|
||||
21(22) element: PyStatementList. Condition: False:false
|
||||
22() element: null
|
||||
@@ -0,0 +1,11 @@
|
||||
if c:
|
||||
pass
|
||||
elif True:
|
||||
pass
|
||||
|
||||
if d:
|
||||
pass
|
||||
elif True:
|
||||
pass
|
||||
else:
|
||||
e = 1
|
||||
@@ -0,0 +1,21 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyIfStatement
|
||||
2(3,5) READ ACCESS: c
|
||||
3(4) element: PyStatementList. Condition: c:true
|
||||
4(9) ASSERTTYPE ACCESS: c
|
||||
5(6) element: PyIfPartElif. Condition: c:false
|
||||
6(7) READ ACCESS: True
|
||||
7(8) element: PyStatementList. Condition: True:true
|
||||
8(9) ASSERTTYPE ACCESS: True
|
||||
9(10) element: PyIfStatement
|
||||
10(11,13) READ ACCESS: d
|
||||
11(12) element: PyStatementList. Condition: d:true
|
||||
12(20) ASSERTTYPE ACCESS: d
|
||||
13(14) element: PyIfPartElif. Condition: d:false
|
||||
14(15) READ ACCESS: True
|
||||
15(16) element: PyStatementList. Condition: True:true
|
||||
16(20) ASSERTTYPE ACCESS: True
|
||||
17(18) element: PyStatementList. Condition: True:false
|
||||
18(19) element: PyAssignmentStatement
|
||||
19(20) WRITE ACCESS: e
|
||||
20() element: null
|
||||
@@ -11,7 +11,7 @@
|
||||
10(11) READ ACCESS: KeyError
|
||||
11(12) element: PyRaiseStatement
|
||||
12(16) READ ACCESS: Error
|
||||
13(14) element: PyElsePart. Condition: markup:false
|
||||
13(14) element: PyStatementList. Condition: markup:false
|
||||
14(15) element: PyReturnStatement
|
||||
15(16) READ ACCESS: body
|
||||
16() element: null
|
||||
@@ -0,0 +1,14 @@
|
||||
if False:
|
||||
a = 1
|
||||
|
||||
if False:
|
||||
b = 1
|
||||
else:
|
||||
pass
|
||||
|
||||
if False:
|
||||
c = 1
|
||||
elif d:
|
||||
pass
|
||||
else:
|
||||
pass
|
||||
@@ -0,0 +1,26 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyIfStatement
|
||||
2(7) READ ACCESS: False
|
||||
3(4) element: PyStatementList. Condition: False:true
|
||||
4(5) ASSERTTYPE ACCESS: False
|
||||
5(6) element: PyAssignmentStatement
|
||||
6(7) WRITE ACCESS: a
|
||||
7(8) element: PyIfStatement
|
||||
8(13) READ ACCESS: False
|
||||
9(10) element: PyStatementList. Condition: False:true
|
||||
10(11) ASSERTTYPE ACCESS: False
|
||||
11(12) element: PyAssignmentStatement
|
||||
12(14) WRITE ACCESS: b
|
||||
13(14) element: PyStatementList. Condition: False:false
|
||||
14(15) element: PyIfStatement
|
||||
15(20) READ ACCESS: False
|
||||
16(17) element: PyStatementList. Condition: False:true
|
||||
17(18) ASSERTTYPE ACCESS: False
|
||||
18(19) element: PyAssignmentStatement
|
||||
19(25) WRITE ACCESS: c
|
||||
20(21) element: PyIfPartElif. Condition: False:false
|
||||
21(22,24) READ ACCESS: d
|
||||
22(23) element: PyStatementList. Condition: d:true
|
||||
23(25) ASSERTTYPE ACCESS: d
|
||||
24(25) element: PyStatementList. Condition: d:false
|
||||
25() element: null
|
||||
@@ -0,0 +1,14 @@
|
||||
if True:
|
||||
pass
|
||||
|
||||
if True:
|
||||
pass
|
||||
else:
|
||||
b = 1
|
||||
|
||||
if True:
|
||||
pass
|
||||
elif c:
|
||||
d = 1
|
||||
else:
|
||||
e = 1
|
||||
@@ -0,0 +1,26 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyIfStatement
|
||||
2(3) READ ACCESS: True
|
||||
3(4) element: PyStatementList. Condition: True:true
|
||||
4(5) ASSERTTYPE ACCESS: True
|
||||
5(6) element: PyIfStatement
|
||||
6(7) READ ACCESS: True
|
||||
7(8) element: PyStatementList. Condition: True:true
|
||||
8(12) ASSERTTYPE ACCESS: True
|
||||
9(10) element: PyStatementList. Condition: True:false
|
||||
10(11) element: PyAssignmentStatement
|
||||
11(12) WRITE ACCESS: b
|
||||
12(13) element: PyIfStatement
|
||||
13(14) READ ACCESS: True
|
||||
14(15) element: PyStatementList. Condition: True:true
|
||||
15(25) ASSERTTYPE ACCESS: True
|
||||
16(17) element: PyIfPartElif. Condition: True:false
|
||||
17(18) READ ACCESS: c
|
||||
18(19) element: PyStatementList. Condition: c:true
|
||||
19(20) ASSERTTYPE ACCESS: c
|
||||
20(21) element: PyAssignmentStatement
|
||||
21(25) WRITE ACCESS: d
|
||||
22(23) element: PyStatementList. Condition: c:false
|
||||
23(24) element: PyAssignmentStatement
|
||||
24(25) WRITE ACCESS: e
|
||||
25() element: null
|
||||
@@ -10,7 +10,7 @@
|
||||
9(10) element: PyAssignmentStatement
|
||||
10(11) READ ACCESS: s
|
||||
11(17) WRITE ACCESS: s2
|
||||
12(13) element: PyElsePart. Condition: (isinstance(s, basestring)):false
|
||||
12(13) element: PyStatementList. Condition: (isinstance(s, basestring)):false
|
||||
13(14) ASSERTTYPE ACCESS: s
|
||||
14(15) element: PyAssignmentStatement
|
||||
15(16) READ ACCESS: s
|
||||
|
||||
@@ -10,7 +10,7 @@
|
||||
9(10) element: PyIfPartElif. Condition: a == b:false
|
||||
10(11) READ ACCESS: aa
|
||||
11(12,22) READ ACCESS: bb
|
||||
12(13) element: PyIfPartElif. Condition: aa == bb:true
|
||||
12(13) element: PyStatementList. Condition: aa == bb:true
|
||||
13(14) element: PyAssignmentStatement
|
||||
14(15) READ ACCESS: same_changet_expression
|
||||
15(16) WRITE ACCESS: bbb
|
||||
@@ -20,7 +20,7 @@
|
||||
19(20) ASSERTTYPE ACCESS: bbb
|
||||
20(21) element: PyAssignmentStatement
|
||||
21(25) WRITE ACCESS: var
|
||||
22(23) element: PyElsePart. Condition: aa == bb:false
|
||||
22(23) element: PyStatementList. Condition: aa == bb:false
|
||||
23(24) element: PyAssignmentStatement
|
||||
24(25) WRITE ACCESS: var
|
||||
25(26) element: PyReturnStatement
|
||||
|
||||
@@ -22,16 +22,16 @@
|
||||
21(37,40) element: PyBreakStatement
|
||||
22(23,37) element: PyIfPartElif. Condition: x == 0:false
|
||||
23(24,26,37) READ ACCESS: x
|
||||
24(25,37) element: PyIfPartElif. Condition: x == 1:true
|
||||
24(25) element: PyStatementList. Condition: x == 1:true
|
||||
25(7,37) element: PyContinueStatement
|
||||
26(27,37) element: PyIfPartElif. Condition: x == 1:false
|
||||
27(28,31,37) READ ACCESS: x
|
||||
28(29,37) element: PyIfPartElif. Condition: x == 2:true
|
||||
28(29) element: PyStatementList. Condition: x == 2:true
|
||||
29(30,37) element: PyRaiseStatement
|
||||
30(37) READ ACCESS: Exception
|
||||
31(32,37) element: PyIfPartElif. Condition: x == 2:false
|
||||
32(33,35,37) READ ACCESS: x
|
||||
33(34,37) element: PyIfPartElif. Condition: x == 3:true
|
||||
33(34) element: PyStatementList. Condition: x == 3:true
|
||||
34(37) element: PyReturnStatement
|
||||
35(36,37) element: PyAssignmentStatement
|
||||
36(37,40) WRITE ACCESS: e
|
||||
|
||||
@@ -4,7 +4,7 @@ def f():
|
||||
|
||||
|
||||
def f():
|
||||
if 0:
|
||||
if b:
|
||||
return
|
||||
a = 1 # pass
|
||||
|
||||
|
||||
@@ -254,6 +254,26 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-24750
|
||||
public void testIfFalse() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-24750
|
||||
public void testIfTrue() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-24750
|
||||
public void testIfElifTrue() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-24750
|
||||
public void testIfElifFalse() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTestFirstStatement() {
|
||||
final String testName = getTestName(false).toLowerCase();
|
||||
configureByFile(testName + ".py");
|
||||
|
||||
@@ -43,6 +43,80 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-24750
|
||||
public void testIfFalse() {
|
||||
doTestByText(
|
||||
"if False:\n" +
|
||||
" <warning descr=\"This code is unreachable\">a = 1</warning>\n" +
|
||||
"\n" +
|
||||
"if False:\n" +
|
||||
" <warning descr=\"This code is unreachable\">b = 1</warning>\n" +
|
||||
"else:\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"if False:\n" +
|
||||
" <warning descr=\"This code is unreachable\">c = 1</warning>\n" +
|
||||
"elif d:\n" +
|
||||
" pass\n" +
|
||||
"else:\n" +
|
||||
" pass\n"
|
||||
);
|
||||
}
|
||||
|
||||
// PY-24750
|
||||
public void testIfTrue() {
|
||||
doTestByText(
|
||||
"if True:\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"if True:\n" +
|
||||
" pass\n" +
|
||||
"else:\n" +
|
||||
" <warning descr=\"This code is unreachable\">b = 1</warning>\n" +
|
||||
"\n" +
|
||||
"if True:\n" +
|
||||
" pass\n" +
|
||||
"<warning descr=\"This code is unreachable\">elif c:\n" +
|
||||
" d = 1</warning>\n" +
|
||||
"else:\n" +
|
||||
" <warning descr=\"This code is unreachable\">e = 1</warning>\n"
|
||||
);
|
||||
}
|
||||
|
||||
// PY-24750
|
||||
public void testIfElifTrue() {
|
||||
doTestByText(
|
||||
"if c:\n" +
|
||||
" pass\n" +
|
||||
"elif True:\n" +
|
||||
" pass\n" +
|
||||
"\n" +
|
||||
"if d:\n" +
|
||||
" pass\n" +
|
||||
"elif True:\n" +
|
||||
" pass\n" +
|
||||
"else:\n" +
|
||||
" <warning descr=\"This code is unreachable\">e = 1</warning>\n"
|
||||
);
|
||||
}
|
||||
|
||||
// PY-24750
|
||||
public void testIfElifFalse() {
|
||||
doTestByText(
|
||||
"if c:\n" +
|
||||
" pass\n" +
|
||||
"elif False:\n" +
|
||||
" <warning descr=\"This code is unreachable\">a = 1</warning>\n" +
|
||||
"\n" +
|
||||
"if d:\n" +
|
||||
" pass\n" +
|
||||
"elif False:\n" +
|
||||
" <warning descr=\"This code is unreachable\">b = 1</warning>\n" +
|
||||
"else:\n" +
|
||||
" pass"
|
||||
);
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user