From 85bea17b099db909943a6b19e2d385da5955e9f3 Mon Sep 17 00:00:00 2001 From: Semyon Proshev Date: Tue, 28 Nov 2017 15:37:59 +0300 Subject: [PATCH] Add pending edge from left operand in boolean expressions (PY-25974) --- .../codeFragment/PyCodeFragmentUtil.java | 13 +++++++-- .../controlflow/PyControlFlowBuilder.java | 5 +++- .../controlflow/andbooleanexpression.py | 8 +----- .../controlflow/andbooleanexpression.txt | 22 ++++----------- .../codeInsight/controlflow/forreturn.txt | 2 +- .../codeInsight/controlflow/function.txt | 4 +-- .../controlflow/orbooleanexpression.py | 12 +------- .../controlflow/orbooleanexpression.txt | 28 ++++--------------- .../typesinandbooleanexpression.py | 7 +++++ .../controlflow/typesinorbooleanexpression.py | 11 ++++++++ .../ExprOrSysExitAssignedToVar.py | 2 ++ .../python/PyControlFlowBuilderTest.java | 12 +++++++- .../PyUnreachableCodeInspectionTest.java | 5 ++++ 13 files changed, 67 insertions(+), 64 deletions(-) create mode 100644 python/testData/codeInsight/controlflow/typesinandbooleanexpression.py create mode 100644 python/testData/codeInsight/controlflow/typesinorbooleanexpression.py create mode 100644 python/testData/inspections/PyUnreachableCodeInspection/ExprOrSysExitAssignedToVar.py diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java index 33d9cbe3f879..f9c9f248f292 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java @@ -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); } diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index 00c98d032383..a5d2ff0d6d0e 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -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); diff --git a/python/testData/codeInsight/controlflow/andbooleanexpression.py b/python/testData/codeInsight/controlflow/andbooleanexpression.py index 1e6dfe20bde4..fc51ae4b2471 100644 --- a/python/testData/codeInsight/controlflow/andbooleanexpression.py +++ b/python/testData/codeInsight/controlflow/andbooleanexpression.py @@ -1,7 +1 @@ -class A: - pass - - -var = object() -if isinstance(var, A) and var: - pass \ No newline at end of file +foo = bar and baz \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/andbooleanexpression.txt b/python/testData/codeInsight/controlflow/andbooleanexpression.txt index 0af02b823a90..83abb003fb9a 100644 --- a/python/testData/codeInsight/controlflow/andbooleanexpression.txt +++ b/python/testData/codeInsight/controlflow/andbooleanexpression.txt @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/forreturn.txt b/python/testData/codeInsight/controlflow/forreturn.txt index b3b0d98c5f7b..f3faa655e705 100644 --- a/python/testData/codeInsight/controlflow/forreturn.txt +++ b/python/testData/codeInsight/controlflow/forreturn.txt @@ -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 diff --git a/python/testData/codeInsight/controlflow/function.txt b/python/testData/codeInsight/controlflow/function.txt index a89cd5395f88..689e69d58396 100644 --- a/python/testData/codeInsight/controlflow/function.txt +++ b/python/testData/codeInsight/controlflow/function.txt @@ -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 diff --git a/python/testData/codeInsight/controlflow/orbooleanexpression.py b/python/testData/codeInsight/controlflow/orbooleanexpression.py index 379dde4398d2..78caf3603b84 100644 --- a/python/testData/codeInsight/controlflow/orbooleanexpression.py +++ b/python/testData/codeInsight/controlflow/orbooleanexpression.py @@ -1,11 +1 @@ -class A: - pass - - -class B: - pass - - -if isinstance(var, A): - if isinstance(var, B) or var: - pass \ No newline at end of file +foo = bar or baz \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/orbooleanexpression.txt b/python/testData/codeInsight/controlflow/orbooleanexpression.txt index 2099f3866885..83abb003fb9a 100644 --- a/python/testData/codeInsight/controlflow/orbooleanexpression.txt +++ b/python/testData/codeInsight/controlflow/orbooleanexpression.txt @@ -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 \ No newline at end of file +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 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/typesinandbooleanexpression.py b/python/testData/codeInsight/controlflow/typesinandbooleanexpression.py new file mode 100644 index 000000000000..1e6dfe20bde4 --- /dev/null +++ b/python/testData/codeInsight/controlflow/typesinandbooleanexpression.py @@ -0,0 +1,7 @@ +class A: + pass + + +var = object() +if isinstance(var, A) and var: + pass \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/typesinorbooleanexpression.py b/python/testData/codeInsight/controlflow/typesinorbooleanexpression.py new file mode 100644 index 000000000000..379dde4398d2 --- /dev/null +++ b/python/testData/codeInsight/controlflow/typesinorbooleanexpression.py @@ -0,0 +1,11 @@ +class A: + pass + + +class B: + pass + + +if isinstance(var, A): + if isinstance(var, B) or var: + pass \ No newline at end of file diff --git a/python/testData/inspections/PyUnreachableCodeInspection/ExprOrSysExitAssignedToVar.py b/python/testData/inspections/PyUnreachableCodeInspection/ExprOrSysExitAssignedToVar.py new file mode 100644 index 000000000000..1b60b2a02a54 --- /dev/null +++ b/python/testData/inspections/PyUnreachableCodeInspection/ExprOrSysExitAssignedToVar.py @@ -0,0 +1,2 @@ +import sys +FOO = abc or sys.exit() \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java index ce039ac75d2b..252d04f3d40a 100644 --- a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java +++ b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java @@ -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(); } diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnreachableCodeInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnreachableCodeInspectionTest.java index 4e221c38d16f..add825bfbf6b 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnreachableCodeInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnreachableCodeInspectionTest.java @@ -33,6 +33,11 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase { runWithLanguageLevel(LanguageLevel.PYTHON26, () -> doTest()); } + // PY-25974 + public void testExprOrSysExitAssignedToVar() { + doTest(); + } + @NotNull @Override protected Class getInspectionClass() {