PY-21175 Add implicit negative type assertion after "if" in CFG unconditionally

The check that we should add such synthetic node only if there were no
pending edges inside the body of if statement (e.g. if it contained only
break/continue/return for the enclosing loop) seems doubtful and
doesn't cover the simplest/most common cases.
This commit is contained in:
Mikhail Golubev
2017-07-24 14:38:41 +03:00
parent f38910aa4b
commit bf22c95cc3
7 changed files with 72 additions and 30 deletions
@@ -365,13 +365,6 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
});
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
final Ref<Boolean> pendingInScopeEdges = Ref.create(false);
myBuilder.processPending((pendingScope, instruction) -> {
if (pendingScope != null && PsiTreeUtil.isAncestor(node, pendingScope, false)) {
pendingInScopeEdges.set(true);
}
myBuilder.addPendingEdge(pendingScope, instruction);
});
final PyTypeAssertionEvaluator negativeAssertionEvaluator = new PyTypeAssertionEvaluator(false);
final PyExpression ifCondition = ifPart.getCondition();
// TODO: Add support for 'elif'
@@ -386,15 +379,11 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator);
elseBranch.accept(this);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
} else {
if (!pendingInScopeEdges.get()) {
myBuilder.prevInstruction = lastBranchingPoint;
InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
else {
myBuilder.addPendingEdge(node, lastBranchingPoint);
}
}
else {
myBuilder.prevInstruction = lastBranchingPoint;
InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
}
@@ -0,0 +1,3 @@
if xs is None:
xs = [1, 2, 3]
print(xs)
@@ -0,0 +1,12 @@
0(1) element: null
1(2) element: PyIfStatement
2(3) READ ACCESS: xs
3(4,8) READ ACCESS: None
4(5) element: PyStatementList. Condition: xs is None:true
5(6) ASSERTTYPE ACCESS: xs
6(7) element: PyAssignmentStatement
7(9) WRITE ACCESS: xs
8(9) ASSERTTYPE ACCESS: xs
9(10) element: PyPrintStatement
10(11) READ ACCESS: xs
11() element: null
@@ -0,0 +1,5 @@
if not isinstance(x, str):
if x is None:
print(x)
print(x)
print(x)
@@ -0,0 +1,21 @@
0(1) element: null
1(2) element: PyIfStatement
2(3) READ ACCESS: isinstance
3(4) READ ACCESS: x
4(5,17) READ ACCESS: str
5(6) element: PyStatementList. Condition: not isinstance(x, str):true
6(7) ASSERTTYPE ACCESS: x
7(8) element: PyIfStatement
8(9) READ ACCESS: x
9(10,14) READ ACCESS: None
10(11) element: PyStatementList. Condition: x is None:true
11(12) ASSERTTYPE ACCESS: x
12(13) element: PyPrintStatement
13(15) READ ACCESS: x
14(15) ASSERTTYPE ACCESS: x
15(16) element: PyPrintStatement
16(18) READ ACCESS: x
17(18) ASSERTTYPE ACCESS: x
18(19) element: PyPrintStatement
19(20) READ ACCESS: x
20() element: null
@@ -6,23 +6,25 @@
5(6,9) element: PyTryPart
6(7,9) element: PyAssignmentStatement
7(8,9) READ ACCESS: open
8(9,17) WRITE ACCESS: status
8(9,18) WRITE ACCESS: status
9(10) element: PyFinallyPart
10(11) element: PyIfStatement
11(12) READ ACCESS: status
12(13,27) READ ACCESS: None
12(13,17) READ ACCESS: None
13(14) element: PyStatementList. Condition: status is not None:true
14(15) ASSERTTYPE ACCESS: status
15(16) element: PyPrintStatement
16(27) READ ACCESS: status
17(18) element: PyFinallyPart
18(19) element: PyIfStatement
19(20) READ ACCESS: status
20(21,25) READ ACCESS: None
21(22) element: PyStatementList. Condition: status is not None:true
22(23) ASSERTTYPE ACCESS: status
23(24) element: PyPrintStatement
24(25) READ ACCESS: status
25(26) element: PyExpressionStatement
26(27) READ ACCESS: status
27() element: null
16(29) READ ACCESS: status
17(29) ASSERTTYPE ACCESS: status
18(19) element: PyFinallyPart
19(20) element: PyIfStatement
20(21) READ ACCESS: status
21(22,26) READ ACCESS: None
22(23) element: PyStatementList. Condition: status is not None:true
23(24) ASSERTTYPE ACCESS: status
24(25) element: PyPrintStatement
25(27) READ ACCESS: status
26(27) ASSERTTYPE ACCESS: status
27(28) element: PyExpressionStatement
28(29) READ ACCESS: status
29() element: null
@@ -238,6 +238,16 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
runWithLanguageLevel(LanguageLevel.PYTHON36, this::doTest);
}
// PY-21175
public void testImplicitNegativeTypeAssertionAfterIf() {
doTest();
}
// PY-21175
public void testImplicitNegativeTypeAssertionAfterTwoNestedIf() {
doTest();
}
private void doTestFirstStatement() {
final String testName = getTestName(false).toLowerCase();
configureByFile(testName + ".py");