Search in sources :

Example 26 with Instruction

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);
}
Also used : CannotCreateCodeFragmentException(com.intellij.codeInsight.codeFragment.CannotCreateCodeFragmentException) ReadWriteInstruction(com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction) Instruction(com.intellij.codeInsight.controlflow.Instruction) ControlFlow(com.intellij.codeInsight.controlflow.ControlFlow) NotNull(org.jetbrains.annotations.NotNull)

Example 27 with Instruction

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);
        }
    }
}
Also used : ArrayList(java.util.ArrayList) Instruction(com.intellij.codeInsight.controlflow.Instruction) PsiElement(com.intellij.psi.PsiElement) Pair(com.intellij.openapi.util.Pair)

Example 28 with Instruction

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();
}
Also used : Instruction(com.intellij.codeInsight.controlflow.Instruction)

Example 29 with Instruction

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();
}
Also used : Instruction(com.intellij.codeInsight.controlflow.Instruction)

Example 30 with Instruction

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);
    }
}
Also used : Instruction(com.intellij.codeInsight.controlflow.Instruction)

Aggregations

Instruction (com.intellij.codeInsight.controlflow.Instruction)33 ReadWriteInstruction (com.jetbrains.python.codeInsight.controlflow.ReadWriteInstruction)15 NotNull (org.jetbrains.annotations.NotNull)12 PsiElement (com.intellij.psi.PsiElement)10 ControlFlow (com.intellij.codeInsight.controlflow.ControlFlow)7 ArrayList (java.util.ArrayList)5 ScopeOwner (com.jetbrains.python.codeInsight.controlflow.ScopeOwner)4 Pair (com.intellij.openapi.util.Pair)3 Scope (com.jetbrains.python.codeInsight.dataflow.scope.Scope)3 Project (com.intellij.openapi.project.Project)2 TextRange (com.intellij.openapi.util.TextRange)2 PyDefUseUtil (com.jetbrains.python.refactoring.PyDefUseUtil)2 TargetElementUtil (com.intellij.codeInsight.TargetElementUtil)1 CannotCreateCodeFragmentException (com.intellij.codeInsight.codeFragment.CannotCreateCodeFragmentException)1 HighlightManager (com.intellij.codeInsight.highlighting.HighlightManager)1 Language (com.intellij.lang.Language)1 InlineActionHandler (com.intellij.lang.refactoring.InlineActionHandler)1 ApplicationManager (com.intellij.openapi.application.ApplicationManager)1 CommandProcessor (com.intellij.openapi.command.CommandProcessor)1 Logger (com.intellij.openapi.diagnostic.Logger)1