void (final @NotNull PyTryExceptStatement node) { myBuilder.startNode(node); // Process try part final PyTryPart tryPart = node.getTryPart(); myBuilder.startNode(tryPart); tryPart.accept(this); // Goto else part after execution, or exit final PyElsePart elsePart = node.getElsePart(); if (elsePart != null) { myBuilder.startNode(elsePart); elsePart.accept(this); } myBuilder.addPendingEdge(node, myBuilder.prevInstruction); // Process except parts final List<Instruction> exceptInstructions = new ArrayList<>(); List<Pair<PsiElement, Instruction>> pendingBackup = new ArrayList<>(); for (PyExceptPart exceptPart : node.getExceptParts()) { pendingBackup.addAll(myBuilder.pending); myBuilder.pending = new ArrayList<>(); myBuilder.flowAbrupted(); final Instruction exceptInstruction = myBuilder.startNode(exceptPart); exceptPart.accept(this); myBuilder.addPendingEdge(node, myBuilder.prevInstruction); exceptInstructions.add(exceptInstruction); } for (Pair<PsiElement, Instruction> pair : pendingBackup) { myBuilder.addPendingEdge(pair.first, pair.second); } final List<Pair<PsiElement, Instruction>> pendingNormalExits = new ArrayList<>(); final PyFinallyPart finallyPart = node.getFinallyPart(); 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); } } }); } // Finally-fail part handling if (finallyPart != null) { myBuilder.flowAbrupted(); finallyFailInstruction = myBuilder.startNode(finallyPart); finallyPart.accept(this); myBuilder.addPendingEdge(null, myBuilder.prevInstruction); myBuilder.flowAbrupted(); } 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.getParentOfType(e, PyTryPart.class, false) == tryPart) { for (Instruction inst : exceptInstructions) { myBuilder.addEdge(instruction, inst); } if (finallyPart != null) { 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, false)) { myBuilder.addEdge(instruction, finallyFailInstruction); } } // All instructions inside the else part have edges to the finally part if (PsiTreeUtil.isAncestor(elsePart, e, false)) { myBuilder.addEdge(instruction, finallyFailInstruction); } } } if (finallyPart != null) { myBuilder.processPending((pendingScope, 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); } } }); // 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); myBuilder.pending = new ArrayList<>(); myBuilder.flowAbrupted(); final Instruction finallySuccessInstruction = myBuilder.startNode(finallyPart); finallyPart.accept(this); for (Pair<PsiElement, Instruction> pair : pendingBackup) { myBuilder.addPendingEdge(pair.first, pair.second); } finallyInstruction = finallySuccessInstruction; } else { finallyInstruction = finallyFailInstruction; } // 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; myBuilder.addEdge(instruction, finallyInstruction); // 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); } } } }