mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Fix processing normal exits from try-except when they goes out of scope (PY-22184)
This commit is contained in:
@@ -396,7 +396,8 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
}
|
||||
else {
|
||||
myBuilder.prevInstruction = null;
|
||||
final Instruction instruction = ContainerUtil.getFirstItem(InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator));
|
||||
final Instruction instruction =
|
||||
ContainerUtil.getFirstItem(InstructionBuilder.addAssertInstructions(myBuilder, negativeAssertionEvaluator));
|
||||
if (instruction != null) {
|
||||
lastBranchingPoints.forEach(p -> myBuilder.addEdge(p.getSecond(), instruction));
|
||||
}
|
||||
@@ -597,11 +598,11 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
||||
|
||||
// Process except parts
|
||||
final List<Instruction> exceptInstructions = emptyMutableList();
|
||||
List<Pair<PsiElement, Instruction>> pendingBackup = emptyMutableList();
|
||||
final List<Instruction> exceptInstructions = new ArrayList<>();
|
||||
List<Pair<PsiElement, Instruction>> pendingBackup = new ArrayList<>();
|
||||
for (PyExceptPart exceptPart : node.getExceptParts()) {
|
||||
pendingBackup.addAll(myBuilder.pending);
|
||||
myBuilder.pending = emptyMutableList();
|
||||
myBuilder.pending = new ArrayList<>();
|
||||
myBuilder.flowAbrupted();
|
||||
final Instruction exceptInstruction = myBuilder.startNode(exceptPart);
|
||||
exceptPart.accept(this);
|
||||
@@ -612,7 +613,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
myBuilder.addPendingEdge(pair.first, pair.second);
|
||||
}
|
||||
|
||||
final List<Instruction> normalExits = new ArrayList<>();
|
||||
final List<Pair<PsiElement, Instruction>> pendingNormalExits = new ArrayList<>();
|
||||
final PyFinallyPart finallyPart = node.getFinallyPart();
|
||||
final Instruction finallyFailInstruction;
|
||||
|
||||
@@ -624,7 +625,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
final boolean isPending = PsiTreeUtil.isAncestor(node, pendingElement, false) &&
|
||||
!PsiTreeUtil.isAncestor(finallyPart, pendingElement, false);
|
||||
if (isPending && pendingScope != null) {
|
||||
normalExits.add(instruction);
|
||||
pendingNormalExits.add(Pair.createNonNull(pendingScope, instruction));
|
||||
}
|
||||
else {
|
||||
myBuilder.addPendingEdge(pendingScope, instruction);
|
||||
@@ -640,7 +641,8 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
finallyPart.accept(this);
|
||||
myBuilder.addPendingEdge(null, myBuilder.prevInstruction);
|
||||
myBuilder.flowAbrupted();
|
||||
} else {
|
||||
}
|
||||
else {
|
||||
finallyFailInstruction = null;
|
||||
}
|
||||
|
||||
@@ -691,16 +693,15 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
}
|
||||
});
|
||||
|
||||
// Duplicate CFG for finally (-fail and -success) only if there are some successfull exits from the
|
||||
// Duplicate CFG for finally (-fail and -success) only if there are some successful exits from the
|
||||
// try part. Otherwise a single CFG for finally provides the correct control flow
|
||||
final Instruction finallyInstruction;
|
||||
if (!normalExits.isEmpty()) {
|
||||
if (!pendingNormalExits.isEmpty()) {
|
||||
// Finally-success part handling
|
||||
pendingBackup = emptyMutableList();
|
||||
pendingBackup.addAll(myBuilder.pending);
|
||||
myBuilder.pending = emptyMutableList();
|
||||
pendingBackup = new ArrayList<>(myBuilder.pending);
|
||||
myBuilder.pending = new ArrayList<>();
|
||||
myBuilder.flowAbrupted();
|
||||
Instruction finallySuccessInstruction = myBuilder.startNode(finallyPart);
|
||||
final Instruction finallySuccessInstruction = myBuilder.startNode(finallyPart);
|
||||
finallyPart.accept(this);
|
||||
for (Pair<PsiElement, Instruction> pair : pendingBackup) {
|
||||
myBuilder.addPendingEdge(pair.first, pair.second);
|
||||
@@ -712,16 +713,21 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
}
|
||||
|
||||
// Connect normal exits from try and else parts to the finally part
|
||||
for (Instruction instr : normalExits) {
|
||||
myBuilder.addEdge(instr, finallyInstruction);
|
||||
for (Pair<PsiElement, Instruction> pendingScopeAndInstruction : pendingNormalExits) {
|
||||
final PsiElement pendingScope = pendingScopeAndInstruction.first;
|
||||
final Instruction instruction = pendingScopeAndInstruction.second;
|
||||
|
||||
myBuilder.addEdge(instruction, finallyInstruction);
|
||||
|
||||
// When instruction continues outside of try-except statement scope
|
||||
// the last instruction in finally-block is marked as pointing to that continuation
|
||||
if (PsiTreeUtil.isAncestor(pendingScope, node, true)) {
|
||||
myBuilder.addPendingEdge(pendingScope, myBuilder.prevInstruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> List<T> emptyMutableList() {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyComprehensionElement(final PyComprehensionElement node) {
|
||||
PyExpression prevCondition = null;
|
||||
@@ -730,7 +736,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
|
||||
for (PyComprehensionComponent component : node.getComponents()) {
|
||||
if (component instanceof PyComprehensionForComponent) {
|
||||
final PyComprehensionForComponent c = (PyComprehensionForComponent) component;
|
||||
final PyComprehensionForComponent c = (PyComprehensionForComponent)component;
|
||||
final PyExpression iteratedList = c.getIteratedList();
|
||||
final PyExpression iteratorVariable = c.getIteratorVariable();
|
||||
if (prevCondition != null) {
|
||||
@@ -755,7 +761,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
iterators.add(iterator);
|
||||
}
|
||||
else if (component instanceof PyComprehensionIfComponent) {
|
||||
final PyComprehensionIfComponent c = (PyComprehensionIfComponent) component;
|
||||
final PyComprehensionIfComponent c = (PyComprehensionIfComponent)component;
|
||||
final PyExpression condition = c.getTest();
|
||||
if (condition == null) {
|
||||
continue;
|
||||
@@ -832,7 +838,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
else {
|
||||
myBuilder.addPendingEdge(pendingScope, instruction);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void abruptFlow(final PsiElement node) {
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
39(45) WRITE ACCESS: f
|
||||
40(41,45) element: PyFinallyPart
|
||||
41(42,45) element: PyAssignmentStatement
|
||||
42(43,45) WRITE ACCESS: f
|
||||
42(43,45,48) WRITE ACCESS: f
|
||||
43(44,45) element: PyAssignmentStatement
|
||||
44(45,48) WRITE ACCESS: g
|
||||
45(46,55) element: PyFinallyPart
|
||||
@@ -48,7 +48,7 @@
|
||||
47(55) WRITE ACCESS: h
|
||||
48(49,55) element: PyFinallyPart
|
||||
49(50,55) element: PyAssignmentStatement
|
||||
50(51,55) WRITE ACCESS: h
|
||||
50(51,53,55) WRITE ACCESS: h
|
||||
51(52,55) element: PyAssignmentStatement
|
||||
52(8,53,55) WRITE ACCESS: i
|
||||
53(54,55) element: PyAssignmentStatement
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
def foo():
|
||||
while True:
|
||||
try:
|
||||
break
|
||||
finally:
|
||||
pass
|
||||
print('b')
|
||||
@@ -38,6 +38,11 @@ public class PyUnreachableCodeInspectionTest extends PyInspectionTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-22184
|
||||
public void testWhileTrueTryBreakFinally() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
@NotNull
|
||||
@Override
|
||||
protected Class<? extends PyInspection> getInspectionClass() {
|
||||
|
||||
Reference in New Issue
Block a user