diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index d2e7203cc319..960bf7b250a5 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -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 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> 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)); diff --git a/python/testData/codeInsight/controlflow/if.py b/python/testData/codeInsight/controlflow/if.py index 84b47ac8259a..fa034e5ee479 100644 --- a/python/testData/codeInsight/controlflow/if.py +++ b/python/testData/codeInsight/controlflow/if.py @@ -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 diff --git a/python/testData/codeInsight/controlflow/if.txt b/python/testData/codeInsight/controlflow/if.txt index d1e5a89c7729..bcc46e28d089 100644 --- a/python/testData/codeInsight/controlflow/if.txt +++ b/python/testData/codeInsight/controlflow/if.txt @@ -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 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/ifeliffalse.py b/python/testData/codeInsight/controlflow/ifeliffalse.py new file mode 100644 index 000000000000..9d743793f964 --- /dev/null +++ b/python/testData/codeInsight/controlflow/ifeliffalse.py @@ -0,0 +1,11 @@ +if c: + pass +elif False: + a = 1 + +if d: + pass +elif False: + b = 1 +else: + pass \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/ifeliffalse.txt b/python/testData/codeInsight/controlflow/ifeliffalse.txt new file mode 100644 index 000000000000..1cf63c87ea29 --- /dev/null +++ b/python/testData/codeInsight/controlflow/ifeliffalse.txt @@ -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 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/ifeliftrue.py b/python/testData/codeInsight/controlflow/ifeliftrue.py new file mode 100644 index 000000000000..0f4147ab4019 --- /dev/null +++ b/python/testData/codeInsight/controlflow/ifeliftrue.py @@ -0,0 +1,11 @@ +if c: + pass +elif True: + pass + +if d: + pass +elif True: + pass +else: + e = 1 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/ifeliftrue.txt b/python/testData/codeInsight/controlflow/ifeliftrue.txt new file mode 100644 index 000000000000..2b008aca2df1 --- /dev/null +++ b/python/testData/codeInsight/controlflow/ifeliftrue.txt @@ -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 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/ifelsereturn.txt b/python/testData/codeInsight/controlflow/ifelsereturn.txt index d0513796c26a..6e9ddad7e26f 100644 --- a/python/testData/codeInsight/controlflow/ifelsereturn.txt +++ b/python/testData/codeInsight/controlflow/ifelsereturn.txt @@ -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 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/iffalse.py b/python/testData/codeInsight/controlflow/iffalse.py new file mode 100644 index 000000000000..9a9457128c40 --- /dev/null +++ b/python/testData/codeInsight/controlflow/iffalse.py @@ -0,0 +1,14 @@ +if False: + a = 1 + +if False: + b = 1 +else: + pass + +if False: + c = 1 +elif d: + pass +else: + pass \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/iffalse.txt b/python/testData/codeInsight/controlflow/iffalse.txt new file mode 100644 index 000000000000..5ebb1f5e500a --- /dev/null +++ b/python/testData/codeInsight/controlflow/iffalse.txt @@ -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 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/iftrue.py b/python/testData/codeInsight/controlflow/iftrue.py new file mode 100644 index 000000000000..dc80522c8893 --- /dev/null +++ b/python/testData/codeInsight/controlflow/iftrue.py @@ -0,0 +1,14 @@ +if True: + pass + +if True: + pass +else: + b = 1 + +if True: + pass +elif c: + d = 1 +else: + e = 1 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/iftrue.txt b/python/testData/codeInsight/controlflow/iftrue.txt new file mode 100644 index 000000000000..e208e361db0d --- /dev/null +++ b/python/testData/codeInsight/controlflow/iftrue.txt @@ -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 \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/isinstance.txt b/python/testData/codeInsight/controlflow/isinstance.txt index 9540334ec50f..246187305244 100644 --- a/python/testData/codeInsight/controlflow/isinstance.txt +++ b/python/testData/codeInsight/controlflow/isinstance.txt @@ -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 diff --git a/python/testData/codeInsight/controlflow/manyifs.txt b/python/testData/codeInsight/controlflow/manyifs.txt index c18688427ae1..904f24fc6c90 100644 --- a/python/testData/codeInsight/controlflow/manyifs.txt +++ b/python/testData/codeInsight/controlflow/manyifs.txt @@ -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 diff --git a/python/testData/codeInsight/controlflow/trytry.txt b/python/testData/codeInsight/controlflow/trytry.txt index 232f222181da..fa2030c84547 100644 --- a/python/testData/codeInsight/controlflow/trytry.txt +++ b/python/testData/codeInsight/controlflow/trytry.txt @@ -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 diff --git a/python/testData/inspections/PyUnreachableCodeInspection/Unreachable.py b/python/testData/inspections/PyUnreachableCodeInspection/Unreachable.py index 92b5ef423e0e..a5d3d11b04f7 100644 --- a/python/testData/inspections/PyUnreachableCodeInspection/Unreachable.py +++ b/python/testData/inspections/PyUnreachableCodeInspection/Unreachable.py @@ -4,7 +4,7 @@ def f(): def f(): - if 0: + if b: return a = 1 # pass diff --git a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java index 5cce49fdcd0a..7f63515f4c87 100644 --- a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java +++ b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java @@ -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"); diff --git a/python/testSrc/com/jetbrains/python/inspections/PyUnreachableCodeInspectionTest.java b/python/testSrc/com/jetbrains/python/inspections/PyUnreachableCodeInspectionTest.java index 04aea5c965b1..78c7cda03dac 100644 --- a/python/testSrc/com/jetbrains/python/inspections/PyUnreachableCodeInspectionTest.java +++ b/python/testSrc/com/jetbrains/python/inspections/PyUnreachableCodeInspectionTest.java @@ -43,6 +43,80 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase { doTest(); } + // PY-24750 + public void testIfFalse() { + doTestByText( + "if False:\n" + + " a = 1\n" + + "\n" + + "if False:\n" + + " b = 1\n" + + "else:\n" + + " pass\n" + + "\n" + + "if False:\n" + + " c = 1\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" + + " b = 1\n" + + "\n" + + "if True:\n" + + " pass\n" + + "elif c:\n" + + " d = 1\n" + + "else:\n" + + " e = 1\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" + + " e = 1\n" + ); + } + + // PY-24750 + public void testIfElifFalse() { + doTestByText( + "if c:\n" + + " pass\n" + + "elif False:\n" + + " a = 1\n" + + "\n" + + "if d:\n" + + " pass\n" + + "elif False:\n" + + " b = 1\n" + + "else:\n" + + " pass" + ); + } + @NotNull @Override protected Class getInspectionClass() {