mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-01-08 15:09:39 +07:00
PY-79910 Variable incorrectly marked as unused or redeclared without usage in nested try/if blocks
When there is no `finally` in try-except statement, use transparent exit instruction to tie all normal exits from try-, else- and except- parts to the next instruction Merge-request: IJ-MR-158265 Merged-by: Aleksandr Govenko <aleksandr.govenko@jetbrains.com> GitOrigin-RevId: 734581b732a1a558b72811fdda977c470d045cc9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
89191fbef6
commit
df545f8047
@@ -751,21 +751,18 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
final Instruction finallyFailInstruction;
|
||||
|
||||
// Store pending normal exit instructions from try-except-else parts
|
||||
if (finallyPart != null) {
|
||||
myBuilder.processPending((pendingScope, 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) {
|
||||
pendingNormalExits.add(Pair.createNonNull(pendingScope, instruction));
|
||||
}
|
||||
else {
|
||||
myBuilder.addPendingEdge(pendingScope, instruction);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
myBuilder.processPending((pendingScope, instruction) -> {
|
||||
final PsiElement pendingElement = instruction.getElement();
|
||||
final boolean isPending = pendingElement == null ||
|
||||
PsiTreeUtil.isAncestor(node, pendingElement, false) &&
|
||||
!PsiTreeUtil.isAncestor(finallyPart, pendingElement, false);
|
||||
if (isPending && pendingScope != null) {
|
||||
pendingNormalExits.add(Pair.createNonNull(pendingScope, instruction));
|
||||
}
|
||||
else {
|
||||
myBuilder.addPendingEdge(pendingScope, instruction);
|
||||
}
|
||||
});
|
||||
|
||||
// Finally-fail part handling
|
||||
if (finallyPart != null) {
|
||||
@@ -809,6 +806,7 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
final Instruction exitInstruction;
|
||||
if (finallyPart != null) {
|
||||
myBuilder.processPending((pendingScope, instruction) -> {
|
||||
final PsiElement e = instruction.getElement();
|
||||
@@ -829,7 +827,6 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
|
||||
// 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 (!pendingNormalExits.isEmpty()) {
|
||||
// Finally-success part handling
|
||||
pendingBackup = new ArrayList<>(myBuilder.pending);
|
||||
@@ -840,24 +837,28 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor {
|
||||
for (Pair<PsiElement, Instruction> pair : pendingBackup) {
|
||||
myBuilder.addPendingEdge(pair.first, pair.second);
|
||||
}
|
||||
finallyInstruction = finallySuccessInstruction;
|
||||
exitInstruction = finallySuccessInstruction;
|
||||
}
|
||||
else {
|
||||
finallyInstruction = finallyFailInstruction;
|
||||
exitInstruction = finallyFailInstruction;
|
||||
}
|
||||
}
|
||||
else {
|
||||
exitInstruction = addTransparentInstruction();
|
||||
myBuilder.prevInstruction = exitInstruction;
|
||||
}
|
||||
|
||||
// Connect normal exits from try and else parts to the finally part
|
||||
for (Pair<PsiElement, Instruction> pendingScopeAndInstruction : pendingNormalExits) {
|
||||
final PsiElement pendingScope = pendingScopeAndInstruction.first;
|
||||
final Instruction instruction = pendingScopeAndInstruction.second;
|
||||
// Connect normal exits from try and else parts to the finally part or exit instruction
|
||||
for (Pair<PsiElement, Instruction> pendingScopeAndInstruction : pendingNormalExits) {
|
||||
final PsiElement pendingScope = pendingScopeAndInstruction.first;
|
||||
final Instruction instruction = pendingScopeAndInstruction.second;
|
||||
|
||||
myBuilder.addEdge(instruction, finallyInstruction);
|
||||
myBuilder.addEdge(instruction, exitInstruction);
|
||||
|
||||
// When instruction continues outside 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);
|
||||
}
|
||||
// When instruction continues outside 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
try:
|
||||
value = 42
|
||||
except ValueError:
|
||||
value = 13
|
||||
except SomethingElse:
|
||||
value = 1342
|
||||
raise
|
||||
else:
|
||||
value = 0
|
||||
|
||||
print(value)
|
||||
@@ -0,0 +1,20 @@
|
||||
0(1) element: null
|
||||
1(2) element: PyTryExceptStatement
|
||||
2(3,8,12) element: PyTryPart
|
||||
3(4,8,12) element: PyAssignmentStatement
|
||||
4(5,8,12) WRITE ACCESS: value
|
||||
5(6) element: PyElsePart
|
||||
6(7) element: PyAssignmentStatement
|
||||
7(17) WRITE ACCESS: value
|
||||
8(9) element: PyExceptPart
|
||||
9(10) READ ACCESS: ValueError
|
||||
10(11) element: PyAssignmentStatement
|
||||
11(17) WRITE ACCESS: value
|
||||
12(13) element: PyExceptPart
|
||||
13(14) READ ACCESS: SomethingElse
|
||||
14(15) element: PyAssignmentStatement
|
||||
15(16) WRITE ACCESS: value
|
||||
16(19) raise: PyRaiseStatement
|
||||
17(18) element: PyPrintStatement
|
||||
18(19) READ ACCESS: value
|
||||
19() element: null
|
||||
@@ -578,6 +578,11 @@ public class PyControlFlowBuilderTest extends LightMarkedTestCase {
|
||||
doTest();
|
||||
}
|
||||
|
||||
// PY-79910
|
||||
public void testTryExceptNoFinally() {
|
||||
doTest();
|
||||
}
|
||||
|
||||
private void doTestFirstStatement() {
|
||||
final String testName = getTestName(false);
|
||||
configureByFile(testName + ".py");
|
||||
|
||||
@@ -126,6 +126,24 @@ public class PyUnusedLocalInspectionTest extends PyInspectionTestCase {
|
||||
inspection.ignoreTupleUnpacking = false;
|
||||
doTest(inspection);
|
||||
}
|
||||
|
||||
// PY-79910
|
||||
public void testTryExceptInsideIfInsideFunction() {
|
||||
doTestByText("""
|
||||
def test():
|
||||
num = 7
|
||||
if num < 10:
|
||||
try:
|
||||
next_num = input() # used
|
||||
except ValueError:
|
||||
next_num = None
|
||||
else:
|
||||
next_num = 0
|
||||
|
||||
return next_num
|
||||
"""
|
||||
);
|
||||
}
|
||||
|
||||
// PY-16419, PY-26417
|
||||
public void testPotentiallySuppressedExceptions() {
|
||||
|
||||
Reference in New Issue
Block a user