Add pending edge from left operand in boolean expressions (PY-25974)

This commit is contained in:
Semyon Proshev
2017-12-25 14:24:04 +03:00
parent 1d8fda0172
commit 85bea17b09
13 changed files with 67 additions and 64 deletions
@@ -20,13 +20,13 @@ import com.intellij.codeInsight.codeFragment.CodeFragmentUtil;
import com.intellij.codeInsight.codeFragment.Position;
import com.intellij.codeInsight.controlflow.ControlFlow;
import com.intellij.codeInsight.controlflow.Instruction;
import com.intellij.openapi.util.Condition;
import com.intellij.openapi.util.Pair;
import com.intellij.psi.*;
import com.intellij.psi.util.PsiTreeUtil;
import com.intellij.usageView.UsageInfo;
import com.intellij.util.ArrayUtil;
import com.intellij.util.containers.ContainerUtil;
import com.jetbrains.python.PyBundle;
import com.jetbrains.python.PyTokenTypes;
import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache;
import com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction;
import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
@@ -218,7 +218,14 @@ public class PyCodeFragmentUtil {
final boolean isExceptTarget = target instanceof PyExceptPart || target instanceof PyFinallyPart;
final boolean isLoopTarget = target instanceof PyWhileStatement || PyForStatementNavigator.getPyForStatementByIterable(target) != null;
if (target != null && !isExceptTarget && !isLoopTarget) {
final PyBinaryExpression binaryExpression = PsiTreeUtil.getParentOfType(source, PyBinaryExpression.class);
final boolean isOppositeBinaryTarget =
binaryExpression != null &&
ArrayUtil.contains(binaryExpression.getOperator(), PyTokenTypes.AND_KEYWORD, PyTokenTypes.OR_KEYWORD) &&
binaryExpression.getLeftExpression() == source &&
binaryExpression.getRightExpression() == target;
if (target != null && !isExceptTarget && !isLoopTarget && !isOppositeBinaryTarget) {
targetInstructions.add(targetInstruction);
}
@@ -415,12 +415,15 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
final PyElementType operator = node.getOperator();
if (operator == PyTokenTypes.AND_KEYWORD || operator == PyTokenTypes.OR_KEYWORD) {
myBuilder.startNode(node);
final PyExpression left = node.getLeftExpression();
final PyTypeAssertionEvaluator assertionEvaluator = new PyTypeAssertionEvaluator(operator == PyTokenTypes.AND_KEYWORD);
final PyExpression left = node.getLeftExpression();
if (left != null) {
left.accept(this);
left.accept(assertionEvaluator);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
final PyExpression right = node.getRightExpression();
if (right != null) {
InstructionBuilder.addAssertInstructions(myBuilder, assertionEvaluator);
@@ -1,7 +1 @@
class A:
pass
var = object()
if isinstance(var, A) and var:
pass
foo = bar and baz
@@ -1,17 +1,7 @@
0(1) element: null
1(2) element: PyClass: A
2(3) WRITE ACCESS: A
3(4) element: PyAssignmentStatement
4(5) READ ACCESS: object
5(6) WRITE ACCESS: var
6(7) element: PyIfStatement
7(8) element: PyBinaryExpression
8(9) READ ACCESS: isinstance
9(10) READ ACCESS: var
10(11) READ ACCESS: A
11(12) ASSERTTYPE ACCESS: var
12(13,15) READ ACCESS: var
13(14) element: PyStatementList. Condition: isinstance(var, A) and var:true
14(16) ASSERTTYPE ACCESS: var
15(16) ASSERTTYPE ACCESS: var
16() element: null
1(2) element: PyAssignmentStatement
2(3) element: PyBinaryExpression
3(4,5) READ ACCESS: bar
4(5) READ ACCESS: baz
5(6) WRITE ACCESS: foo
6() element: null
@@ -8,7 +8,7 @@
7(8) element: PyIfStatement
8(9) element: PyBinaryExpression
9(10) READ ACCESS: hour
10(11) READ ACCESS: start
10(11,13,3,16) READ ACCESS: start
11(12) READ ACCESS: hour
12(13,3,16) READ ACCESS: end
13(14) element: PyStatementList. Condition: hour >= start and hour < end:true
@@ -22,8 +22,8 @@
21(22) element: PyAssignmentStatement
22(23) element: PyBinaryExpression
23(24) element: PyBinaryExpression
24(25) READ ACCESS: collapse
25(26) element: PyLambdaExpression
24(25,26) READ ACCESS: collapse
25(26,27) element: PyLambdaExpression
26(27) element: PyLambdaExpression
27(28) WRITE ACCESS: processFunc
28(29) element: PyPrintStatement
@@ -1,11 +1 @@
class A:
pass
class B:
pass
if isinstance(var, A):
if isinstance(var, B) or var:
pass
foo = bar or baz
@@ -1,23 +1,7 @@
0(1) element: null
1(2) element: PyClass: A
2(3) WRITE ACCESS: A
3(4) element: PyClass: B
4(5) WRITE ACCESS: B
5(6) element: PyIfStatement
6(7) READ ACCESS: isinstance
7(8) READ ACCESS: var
8(9,21) READ ACCESS: A
9(10) element: PyStatementList. Condition: isinstance(var, A):true
10(11) ASSERTTYPE ACCESS: var
11(12) element: PyIfStatement
12(13) element: PyBinaryExpression
13(14) READ ACCESS: isinstance
14(15) READ ACCESS: var
15(16) READ ACCESS: B
16(17) ASSERTTYPE ACCESS: var
17(18,20) READ ACCESS: var
18(19) element: PyStatementList. Condition: isinstance(var, B) or var:true
19(22) ASSERTTYPE ACCESS: var
20(22) ASSERTTYPE ACCESS: var
21(22) ASSERTTYPE ACCESS: var
22() element: null
1(2) element: PyAssignmentStatement
2(3) element: PyBinaryExpression
3(4,5) READ ACCESS: bar
4(5) READ ACCESS: baz
5(6) WRITE ACCESS: foo
6() element: null
@@ -0,0 +1,7 @@
class A:
pass
var = object()
if isinstance(var, A) and var:
pass
@@ -0,0 +1,11 @@
class A:
pass
class B:
pass
if isinstance(var, A):
if isinstance(var, B) or var:
pass
@@ -0,0 +1,2 @@
import sys
FOO = abc or sys.exit()
@@ -229,11 +229,21 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
}
// PY-20889
public void testAndBooleanExpression() {
public void testTypesInAndBooleanExpression() {
doTest();
}
// PY-20889
public void testTypesInOrBooleanExpression() {
doTest();
}
// PY-25974
public void testAndBooleanExpression() {
doTest();
}
// PY-25974
public void testOrBooleanExpression() {
doTest();
}
@@ -33,6 +33,11 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase {
runWithLanguageLevel(LanguageLevel.PYTHON26, () -> doTest());
}
// PY-25974
public void testExprOrSysExitAssignedToVar() {
doTest();
}
@NotNull
@Override
protected Class<? extends PyInspection> getInspectionClass() {