mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
Separated success and failure control flow for finally clause if no except clauses found (PY-4102)
This commit is contained in:
+101
-44
@@ -27,6 +27,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
public static final TokenSet CALL_OR_REF_EXPR = TokenSet.create(PyElementTypes.CALL_EXPRESSION, PyElementTypes.REFERENCE_EXPRESSION);
|
||||
public static final String SELF_ASSERT_RAISES = "self.assertRaises";
|
||||
private final ControlFlowBuilder myBuilder = new ControlFlowBuilder();
|
||||
private List<Pair<PsiElement, Instruction>> myPendindBackup = null;
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//// Control flow builder staff
|
||||
@@ -431,14 +432,6 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
abruptFlow(node);
|
||||
}
|
||||
|
||||
private Instruction addBlockConnectedViaPending(final PsiElement node, final PsiElement scope) {
|
||||
myBuilder.flowAbrupted();
|
||||
final Instruction result = myBuilder.startNode(node);
|
||||
node.accept(this);
|
||||
myBuilder.addPendingEdge(scope, myBuilder.prevInstruction);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitPyTryExceptStatement(final PyTryExceptStatement node) {
|
||||
myBuilder.startNode(node);
|
||||
@@ -456,75 +449,133 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
}
|
||||
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
||||
|
||||
// Process except parts
|
||||
final ArrayList<Instruction> exceptInstructions = new ArrayList<Instruction>();
|
||||
// Store pending and clear it
|
||||
final List<Pair<PsiElement, Instruction>> pending = myBuilder.pending;
|
||||
myBuilder.pending = new ArrayList<Pair<PsiElement, Instruction>>();
|
||||
storeAndClearPending();
|
||||
for (PyExceptPart exceptPart : node.getExceptParts()) {
|
||||
final Instruction exceptInstrcution = addBlockConnectedViaPending(exceptPart, node);
|
||||
myBuilder.flowAbrupted();
|
||||
final Instruction exceptInstrcution = myBuilder.startNode(exceptPart);
|
||||
exceptPart.accept(this);
|
||||
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
|
||||
exceptInstructions.add(exceptInstrcution);
|
||||
}
|
||||
// Restore pending
|
||||
for (Pair<PsiElement, Instruction> pair : pending) {
|
||||
myBuilder.addPendingEdge(pair.first, pair.second);
|
||||
}
|
||||
// Finally part handling
|
||||
restorePending();
|
||||
|
||||
final List<Instruction> normalExits = new ArrayList<Instruction>();
|
||||
final PyFinallyPart finallyPart = node.getFinallyPart();
|
||||
Instruction finallyInstruction = null;
|
||||
Instruction lastFinallyInstruction = null;
|
||||
final Instruction finallyFailInstruction;
|
||||
|
||||
// Store pending normal exit instructions from try-except-else parts
|
||||
if (finallyPart != null) {
|
||||
finallyInstruction = addBlockConnectedViaPending(finallyPart, node);
|
||||
lastFinallyInstruction = myBuilder.prevInstruction;
|
||||
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
|
||||
public void process(final PsiElement pendingScope, final Instruction instruction) {
|
||||
final PsiElement pendingElement = instruction.getElement();
|
||||
if (pendingElement != null) {
|
||||
final boolean isPending = PsiTreeUtil.isAncestor(node, pendingElement, false) &&
|
||||
!PsiTreeUtil.isAncestor(finallyPart, pendingElement, false);
|
||||
if (isPending && pendingScope != null) {
|
||||
normalExits.add(instruction);
|
||||
}
|
||||
else {
|
||||
myBuilder.addPendingEdge(pendingScope, instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Finally-fail part handling
|
||||
if (finallyPart != null) {
|
||||
myBuilder.flowAbrupted();
|
||||
finallyFailInstruction = myBuilder.startNode(finallyPart);
|
||||
finallyPart.accept(this);
|
||||
myBuilder.addPendingEdge(null, myBuilder.prevInstruction);
|
||||
} else {
|
||||
finallyFailInstruction = null;
|
||||
}
|
||||
|
||||
// Create exception edges
|
||||
for (Instruction instruction : myBuilder.instructions) {
|
||||
final PsiElement e = instruction.getElement();
|
||||
if (e == null || !canRaiseExceptions(instruction)) {
|
||||
continue;
|
||||
}
|
||||
// All instructions inside the try part have edges to except and finally parts
|
||||
if (PsiTreeUtil.isAncestor(tryPart, e, true)) {
|
||||
if (PsiTreeUtil.getParentOfType(e, PyTryPart.class, true) == tryPart) {
|
||||
for (Instruction inst : exceptInstructions) {
|
||||
myBuilder.addEdge(instruction, inst);
|
||||
}
|
||||
if (finallyPart != null) {
|
||||
myBuilder.addEdge(instruction, finallyInstruction);
|
||||
myBuilder.addEdge(instruction, finallyFailInstruction);
|
||||
}
|
||||
}
|
||||
if (finallyPart != null) {
|
||||
// All instructions inside except parts have edges to the finally part
|
||||
for (PyExceptPart exceptPart : node.getExceptParts()) {
|
||||
if (PsiTreeUtil.isAncestor(exceptPart, e, true)) {
|
||||
myBuilder.addEdge(instruction, finallyInstruction);
|
||||
myBuilder.addEdge(instruction, finallyFailInstruction);
|
||||
}
|
||||
}
|
||||
// All instructions inside the else part have edges to the finally part
|
||||
if (PsiTreeUtil.isAncestor(elsePart, e, true)) {
|
||||
myBuilder.addEdge(instruction, finallyInstruction);
|
||||
myBuilder.addEdge(instruction, finallyFailInstruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
final Ref<Instruction> finallyRef = new Ref<Instruction>(finallyInstruction);
|
||||
final Ref<Instruction> lastFinallyRef = new Ref<Instruction>(lastFinallyInstruction);
|
||||
if (finallyPart != null) {
|
||||
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
|
||||
@Override
|
||||
public void process(PsiElement pendingScope, Instruction instruction) {
|
||||
final PsiElement e = instruction.getElement();
|
||||
if (e != null) {
|
||||
// Change the scope of pending edges from finally-fail part to point to the last instruction
|
||||
if (PsiTreeUtil.isAncestor(finallyPart, e, false)) {
|
||||
myBuilder.addPendingEdge(null, instruction);
|
||||
}
|
||||
// Connect pending fail edges to the finally-fail part
|
||||
else if (pendingScope == null && PsiTreeUtil.isAncestor(node, e, false)) {
|
||||
myBuilder.addEdge(instruction, finallyFailInstruction);
|
||||
}
|
||||
else {
|
||||
myBuilder.addPendingEdge(pendingScope, instruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() {
|
||||
public void process(final PsiElement pendingScope, final Instruction instruction) {
|
||||
final PsiElement pendingElement = instruction.getElement();
|
||||
if (pendingElement == null) {
|
||||
return;
|
||||
}
|
||||
// Handle pending exit edges from try-except-else parts (in the node scope) if finally part exists
|
||||
final boolean isPending = PsiTreeUtil.isAncestor(node, pendingElement, false) &&
|
||||
(finallyPart == null || !PsiTreeUtil.isAncestor(finallyPart, pendingElement, false));
|
||||
if (!finallyRef.isNull() && isPending) {
|
||||
myBuilder.addEdge(instruction, finallyRef.get());
|
||||
myBuilder.addPendingEdge(null, lastFinallyRef.get());
|
||||
return;
|
||||
}
|
||||
myBuilder.addPendingEdge(pendingScope, instruction);
|
||||
// Duplicate CFG for finally (-fail and -success) only if there are no except parts and there are some successfull exits from the
|
||||
// try part. Otherwise a single CFG for finally provides the correct control flow
|
||||
final Instruction finallyInstruction;
|
||||
if (node.getExceptParts().length == 0 && !normalExits.isEmpty()) {
|
||||
// Finally-success part handling
|
||||
storeAndClearPending();
|
||||
myBuilder.flowAbrupted();
|
||||
Instruction finallySuccessInstruction = myBuilder.startNode(finallyPart);
|
||||
finallyPart.accept(this);
|
||||
restorePending();
|
||||
finallyInstruction = finallySuccessInstruction;
|
||||
}
|
||||
});
|
||||
else {
|
||||
finallyInstruction = finallyFailInstruction;
|
||||
}
|
||||
|
||||
// Connect normal exits from try and else parts to the finally part
|
||||
for (Instruction instr : normalExits) {
|
||||
myBuilder.addEdge(instr, finallyInstruction);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void storeAndClearPending() {
|
||||
myPendindBackup = myBuilder.pending;
|
||||
myBuilder.pending = new ArrayList<Pair<PsiElement, Instruction>>();
|
||||
}
|
||||
|
||||
private void restorePending() {
|
||||
for (Pair<PsiElement, Instruction> pair : myPendindBackup) {
|
||||
myBuilder.addPendingEdge(pair.first, pair.second);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -677,9 +728,15 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
}
|
||||
return !PsiTreeUtil.instanceOf(instruction.getElement(),
|
||||
PyReturnStatement.class,
|
||||
PyBreakStatement.class,
|
||||
PyContinueStatement.class,
|
||||
PyAssignmentStatement.class,
|
||||
PyRaiseStatement.class,
|
||||
PyStatementList.class);
|
||||
PyStatementList.class,
|
||||
PyTryExceptStatement.class,
|
||||
PyTryPart.class,
|
||||
PyExceptPart.class,
|
||||
PyFinallyPart.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -157,7 +157,7 @@ public class PyUnboundLocalVariableInspection extends PyInspection {
|
||||
ControlFlowUtil.iteratePrev(number, instructions, new Function<Instruction, ControlFlowUtil.Operation>() {
|
||||
public ControlFlowUtil.Operation fun(final Instruction inst) {
|
||||
try {
|
||||
if (inst.num() == number){
|
||||
if (inst.num() == number) {
|
||||
return ControlFlowUtil.Operation.NEXT;
|
||||
}
|
||||
if (inst instanceof ReadWriteInstruction) {
|
||||
@@ -190,15 +190,15 @@ public class PyUnboundLocalVariableInspection extends PyInspection {
|
||||
if (readAccessSeen.get()){
|
||||
return;
|
||||
}
|
||||
if (resolve2Scope){
|
||||
if (resolve2Scope) {
|
||||
if (owner instanceof PyFile){
|
||||
registerProblem(node, PyBundle.message("INSP.unbound.name.not.defined", name));
|
||||
}
|
||||
else {
|
||||
registerUnboundLocal(node);
|
||||
}
|
||||
} else
|
||||
if (owner instanceof PyFunction && PsiTreeUtil.getParentOfType(owner, PyClass.class, PyFile.class) instanceof PyFile){
|
||||
}
|
||||
else if (owner instanceof PyFunction && PsiTreeUtil.getParentOfType(owner, PyClass.class, PyFile.class) instanceof PyFile){
|
||||
registerUnboundLocal(node);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
try:
|
||||
from mercurial import lsprof
|
||||
from mercurial import lsprof
|
||||
except ImportError:
|
||||
raise Error
|
||||
raise Error
|
||||
p = 123
|
||||
try:
|
||||
return foo
|
||||
return foo
|
||||
x = 1
|
||||
finally:
|
||||
print(p)
|
||||
print(p)
|
||||
y = 2
|
||||
|
||||
@@ -6,14 +6,21 @@
|
||||
5(6) element: PyExceptPart
|
||||
6(7) READ ACCESS: ImportError
|
||||
7(8) element: PyRaiseStatement
|
||||
8(18) READ ACCESS: Error
|
||||
8(25) READ ACCESS: Error
|
||||
9(10) element: PyAssignmentStatement
|
||||
10(11) WRITE ACCESS: p
|
||||
11(12) element: PyTryExceptStatement
|
||||
12(13) element: PyTryPart
|
||||
13(14) element: PyReturnStatement
|
||||
14(15) READ ACCESS: foo
|
||||
15(16) element: PyFinallyPart
|
||||
16(17) element: PyPrintStatement
|
||||
17(18) READ ACCESS: p
|
||||
18() element: null
|
||||
14(17) READ ACCESS: foo
|
||||
15(16) element: PyAssignmentStatement
|
||||
16(17,20) WRITE ACCESS: x
|
||||
17(18) element: PyFinallyPart
|
||||
18(19) element: PyPrintStatement
|
||||
19(25) READ ACCESS: p
|
||||
20(21) element: PyFinallyPart
|
||||
21(22) element: PyPrintStatement
|
||||
22(23) READ ACCESS: p
|
||||
23(24) element: PyAssignmentStatement
|
||||
24(25) WRITE ACCESS: y
|
||||
25() element: null
|
||||
@@ -5,7 +5,7 @@
|
||||
4(5,8,11) READ ACCESS: bar
|
||||
5(6) element: PyStatementList
|
||||
6(7,8) WRITE ACCESS: i
|
||||
7(8,11) element: PyBreakStatement
|
||||
7(11) element: PyBreakStatement
|
||||
8(9) element: PyExceptPart
|
||||
9(10) element: PyRaiseStatement
|
||||
10(12) READ ACCESS: Exception
|
||||
|
||||
@@ -6,14 +6,21 @@
|
||||
5(6) element: PyTryPart
|
||||
6(7) element: PyAssignmentStatement
|
||||
7(8,9) READ ACCESS: open
|
||||
8(9) WRITE ACCESS: status
|
||||
8(9,16) WRITE ACCESS: status
|
||||
9(10) element: PyFinallyPart
|
||||
10(11) element: PyIfStatement
|
||||
11(12) READ ACCESS: status
|
||||
12(13,16) READ ACCESS: None
|
||||
12(13,25) READ ACCESS: None
|
||||
13(14) element: PyStatementList. Condition: status is not None:true
|
||||
14(15) element: PyPrintStatement
|
||||
15(16,18) READ ACCESS: status
|
||||
16(17) element: PyExpressionStatement
|
||||
17(18) READ ACCESS: status
|
||||
18() element: null
|
||||
15(25) READ ACCESS: status
|
||||
16(17) element: PyFinallyPart
|
||||
17(18) element: PyIfStatement
|
||||
18(19) READ ACCESS: status
|
||||
19(20,23) READ ACCESS: None
|
||||
20(21) element: PyStatementList. Condition: status is not None:true
|
||||
21(22) element: PyPrintStatement
|
||||
22(23) READ ACCESS: status
|
||||
23(24) element: PyExpressionStatement
|
||||
24(25) READ ACCESS: status
|
||||
25() element: null
|
||||
@@ -0,0 +1,27 @@
|
||||
a = 1
|
||||
try:
|
||||
b = 2
|
||||
for x in [1, 2, 3]: # loop:8
|
||||
try:
|
||||
c = 3
|
||||
try:
|
||||
d = 4
|
||||
if x == 0:
|
||||
break
|
||||
elif x == 1:
|
||||
continue
|
||||
elif x == 2:
|
||||
raise Exception()
|
||||
elif x == 3:
|
||||
return 42
|
||||
e = 5
|
||||
finally: # f:37,s:40
|
||||
f = 6
|
||||
g = 7
|
||||
finally: # f:45,s:48
|
||||
h = 8
|
||||
i = 9
|
||||
j = 10
|
||||
finally: # f:55,s:58
|
||||
k = 11
|
||||
l = 12
|
||||
@@ -0,0 +1,64 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyAssignmentStatement
|
||||
2(3) WRITE ACCESS: a
|
||||
3(4) element: PyTryExceptStatement
|
||||
4(5) element: PyTryPart
|
||||
5(6) element: PyAssignmentStatement
|
||||
6(7,55) WRITE ACCESS: b
|
||||
7(8,53,55) element: PyForStatement
|
||||
8(9) element: PyStatementList
|
||||
9(10,55) WRITE ACCESS: x
|
||||
10(11) element: PyTryExceptStatement
|
||||
11(12) element: PyTryPart
|
||||
12(13) element: PyAssignmentStatement
|
||||
13(14,45) WRITE ACCESS: c
|
||||
14(15) element: PyTryExceptStatement
|
||||
15(16) element: PyTryPart
|
||||
16(17) element: PyAssignmentStatement
|
||||
17(18,37) WRITE ACCESS: d
|
||||
18(19,37) element: PyIfStatement
|
||||
19(20,22,37) READ ACCESS: x
|
||||
20(21) element: PyStatementList. Condition: x == 0:true
|
||||
21(40) element: PyBreakStatement
|
||||
22(23,26,37) element: PyIfPartElif. Condition: x == 0:false
|
||||
23(24,37) READ ACCESS: x
|
||||
24(25,37) element: PyIfPartElif. Condition: x == 1:true
|
||||
25(7) element: PyContinueStatement
|
||||
26(27,31,37) element: PyIfPartElif. Condition: x == 1:false
|
||||
27(28,37) READ ACCESS: x
|
||||
28(29,37) element: PyIfPartElif. Condition: x == 2:true
|
||||
29(30) element: PyRaiseStatement
|
||||
30(37) READ ACCESS: Exception
|
||||
31(32,35,37) element: PyIfPartElif. Condition: x == 2:false
|
||||
32(33,37) READ ACCESS: x
|
||||
33(34,37) element: PyIfPartElif. Condition: x == 3:true
|
||||
34(37) element: PyReturnStatement
|
||||
35(36) element: PyAssignmentStatement
|
||||
36(37,40) WRITE ACCESS: e
|
||||
37(38) element: PyFinallyPart
|
||||
38(39) element: PyAssignmentStatement
|
||||
39(45) WRITE ACCESS: f
|
||||
40(41) element: PyFinallyPart
|
||||
41(42) element: PyAssignmentStatement
|
||||
42(43,45) WRITE ACCESS: f
|
||||
43(44) element: PyAssignmentStatement
|
||||
44(45,48) WRITE ACCESS: g
|
||||
45(46) element: PyFinallyPart
|
||||
46(47) element: PyAssignmentStatement
|
||||
47(55) WRITE ACCESS: h
|
||||
48(49) element: PyFinallyPart
|
||||
49(50) element: PyAssignmentStatement
|
||||
50(51,55) WRITE ACCESS: h
|
||||
51(52) element: PyAssignmentStatement
|
||||
52(8,53,55) WRITE ACCESS: i
|
||||
53(54) element: PyAssignmentStatement
|
||||
54(55,58) WRITE ACCESS: j
|
||||
55(56) element: PyFinallyPart
|
||||
56(57) element: PyAssignmentStatement
|
||||
57(63) WRITE ACCESS: k
|
||||
58(59) element: PyFinallyPart
|
||||
59(60) element: PyAssignmentStatement
|
||||
60(61) WRITE ACCESS: k
|
||||
61(62) element: PyAssignmentStatement
|
||||
62(63) WRITE ACCESS: l
|
||||
63() element: null
|
||||
@@ -137,6 +137,10 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testTryTry() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
public void testIsinstance() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user