use of com.intellij.codeInsight.controlflow.Instruction in project intellij-community by JetBrains.
the class PyCodeFragmentUtil method createCodeFragment.
@NotNull
public static PyCodeFragment createCodeFragment(@NotNull final ScopeOwner owner, @NotNull final PsiElement startInScope, @NotNull final PsiElement endInScope) throws CannotCreateCodeFragmentException {
final int start = startInScope.getTextOffset();
final int end = endInScope.getTextOffset() + endInScope.getTextLength();
final ControlFlow flow = ControlFlowCache.getControlFlow(owner);
if (flow == null) {
throw new CannotCreateCodeFragmentException(PyBundle.message("refactoring.extract.method.error.undetermined.execution.flow"));
}
final List<Instruction> graph = Arrays.asList(flow.getInstructions());
final List<Instruction> subGraph = getFragmentSubGraph(graph, start, end);
final AnalysisResult subGraphAnalysis = analyseSubGraph(subGraph, start, end);
if ((subGraphAnalysis.regularExits > 0 && subGraphAnalysis.returns > 0) || subGraphAnalysis.targetInstructions > 1 || subGraphAnalysis.outerLoopBreaks > 0) {
throw new CannotCreateCodeFragmentException(PyBundle.message("refactoring.extract.method.error.interrupted.execution.flow"));
}
if (subGraphAnalysis.starImports > 0) {
throw new CannotCreateCodeFragmentException(PyBundle.message("refactoring.extract.method.error.star.import"));
}
final Set<String> globalWrites = getGlobalWrites(subGraph, owner);
final Set<String> nonlocalWrites = getNonlocalWrites(subGraph, owner);
final Set<String> inputNames = new HashSet<>();
for (PsiElement element : filterElementsInScope(getInputElements(subGraph, graph), owner)) {
final String name = getName(element);
if (name != null) {
// Ignore "self" and "cls", they are generated automatically when extracting any method fragment
if (resolvesToBoundMethodParameter(element)) {
continue;
}
if (globalWrites.contains(name) || nonlocalWrites.contains(name)) {
continue;
}
inputNames.add(name);
}
}
final Set<String> outputNames = new HashSet<>();
for (PsiElement element : getOutputElements(subGraph, graph)) {
final String name = getName(element);
if (name != null) {
if (globalWrites.contains(name) || nonlocalWrites.contains(name)) {
continue;
}
outputNames.add(name);
}
}
final boolean yieldsFound = subGraphAnalysis.yieldExpressions > 0;
if (yieldsFound && LanguageLevel.forElement(owner).isOlderThan(LanguageLevel.PYTHON33)) {
throw new CannotCreateCodeFragmentException(PyBundle.message("refactoring.extract.method.error.yield"));
}
final boolean isAsync = owner instanceof PyFunction && ((PyFunction) owner).isAsync();
return new PyCodeFragment(inputNames, outputNames, globalWrites, nonlocalWrites, subGraphAnalysis.returns > 0, yieldsFound, isAsync);
}
use of com.intellij.codeInsight.controlflow.Instruction in project intellij-community by JetBrains.
the class PyControlFlowBuilder method visitPyTryExceptStatement.
@Override
public void visitPyTryExceptStatement(final 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 = emptyMutableList();
List<Pair<PsiElement, Instruction>> pendingBackup = emptyMutableList();
for (PyExceptPart exceptPart : node.getExceptParts()) {
pendingBackup.addAll(myBuilder.pending);
myBuilder.pending = emptyMutableList();
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<Instruction> normalExits = 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) {
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);
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);
} else // Connect pending fail edges to the finally-fail part
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 successfull exits from the
// try part. Otherwise a single CFG for finally provides the correct control flow
final Instruction finallyInstruction;
if (!normalExits.isEmpty()) {
// Finally-success part handling
pendingBackup = emptyMutableList();
pendingBackup.addAll(myBuilder.pending);
myBuilder.pending = emptyMutableList();
myBuilder.flowAbrupted();
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 (Instruction instr : normalExits) {
myBuilder.addEdge(instr, finallyInstruction);
}
}
}
use of com.intellij.codeInsight.controlflow.Instruction in project intellij-community by JetBrains.
the class PyControlFlowBuilder method visitPyWhileStatement.
@Override
public void visitPyWhileStatement(final PyWhileStatement node) {
final Instruction instruction = myBuilder.startNode(node);
final PyWhilePart whilePart = node.getWhilePart();
final PyExpression condition = whilePart.getCondition();
boolean isStaticallyTrue = false;
if (condition != null) {
condition.accept(this);
isStaticallyTrue = PyConstantExpressionEvaluator.evaluateBoolean(condition, false);
}
final Instruction head = myBuilder.prevInstruction;
final PyElsePart elsePart = node.getElsePart();
if (elsePart == null && !isStaticallyTrue) {
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
final PyStatementList list = whilePart.getStatementList();
myBuilder.startConditionalNode(list, condition, true);
list.accept(this);
// Loop edges
if (myBuilder.prevInstruction != null) {
myBuilder.addEdge(myBuilder.prevInstruction, instruction);
}
myBuilder.checkPending(instruction);
myBuilder.prevInstruction = head;
if (elsePart != null && !isStaticallyTrue) {
elsePart.accept(this);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
myBuilder.flowAbrupted();
}
use of com.intellij.codeInsight.controlflow.Instruction in project intellij-community by JetBrains.
the class PyControlFlowBuilder method visitPyForStatement.
@Override
public void visitPyForStatement(final PyForStatement node) {
myBuilder.startNode(node);
final PyForPart forPart = node.getForPart();
final PyExpression source = forPart.getSource();
if (source != null) {
source.accept(this);
}
final Instruction head = myBuilder.prevInstruction;
final PyElsePart elsePart = node.getElsePart();
if (elsePart == null) {
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
final PyStatementList list = forPart.getStatementList();
final Instruction body;
final PyExpression target = forPart.getTarget();
if (target != null) {
body = myBuilder.startNode(target);
target.accept(this);
} else {
body = myBuilder.startNode(list);
}
list.accept(this);
if (myBuilder.prevInstruction != null) {
//loop
myBuilder.addEdge(myBuilder.prevInstruction, body);
// exit
myBuilder.addPendingEdge(list, myBuilder.prevInstruction);
}
myBuilder.processPending((pendingScope, instruction) -> {
if (pendingScope != null && PsiTreeUtil.isAncestor(list, pendingScope, false)) {
//loop
myBuilder.addEdge(instruction, body);
// exit
myBuilder.addPendingEdge(list, instruction);
} else {
myBuilder.addPendingEdge(pendingScope, instruction);
}
});
myBuilder.prevInstruction = head;
if (elsePart != null) {
elsePart.accept(this);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
myBuilder.flowAbrupted();
}
use of com.intellij.codeInsight.controlflow.Instruction in project intellij-community by JetBrains.
the class PyControlFlowBuilder method visitPyConditionalExpression.
@Override
public void visitPyConditionalExpression(PyConditionalExpression node) {
myBuilder.startNode(node);
final PyExpression condition = node.getCondition();
final PyTypeAssertionEvaluator assertionEvaluator = new PyTypeAssertionEvaluator();
if (condition != null) {
condition.accept(this);
condition.accept(assertionEvaluator);
}
final Instruction branchingPoint = myBuilder.prevInstruction;
final PyExpression truePart = node.getTruePart();
final PyExpression falsePart = node.getFalsePart();
if (truePart != null) {
InstructionBuilder.addAssertInstructions(myBuilder, assertionEvaluator);
truePart.accept(this);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
if (falsePart != null) {
myBuilder.prevInstruction = branchingPoint;
falsePart.accept(this);
myBuilder.addPendingEdge(node, myBuilder.prevInstruction);
}
}
Aggregations