diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/ConditionInstruction.java b/python/src/com/jetbrains/python/codeInsight/controlflow/ConditionInstruction.java new file mode 100644 index 000000000000..e8ec25e78870 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ConditionInstruction.java @@ -0,0 +1,11 @@ +package com.jetbrains.python.codeInsight.controlflow; + +import com.intellij.psi.PsiElement; + +/** + * @author oleg + */ +public interface ConditionInstruction extends Instruction { + boolean getResult(); + PsiElement getCondition(); +} diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlow.java b/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlow.java new file mode 100644 index 000000000000..ce01d3f3e047 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlow.java @@ -0,0 +1,8 @@ +package com.jetbrains.python.codeInsight.controlflow; + +/** + * @author oleg + */ +public interface ControlFlow { + Instruction[] getInstructions(); +} diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowOwner.java b/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowOwner.java new file mode 100644 index 000000000000..f32427a6ac2b --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/ControlFlowOwner.java @@ -0,0 +1,10 @@ +package com.jetbrains.python.codeInsight.controlflow; + +import com.jetbrains.python.psi.PyElement; + +/** + * @author oleg + */ +public interface ControlFlowOwner extends PyElement { + ControlFlow getControlFlow(); +} diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/Instruction.java b/python/src/com/jetbrains/python/codeInsight/controlflow/Instruction.java new file mode 100644 index 000000000000..05bd21d2fb71 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/Instruction.java @@ -0,0 +1,22 @@ +package com.jetbrains.python.codeInsight.controlflow; + +import com.jetbrains.python.psi.PyElement; +import org.jetbrains.annotations.Nullable; + +import java.util.Collection; + +/** + * @author oleg + */ +public interface Instruction { + @Nullable + public PyElement getElement(); + + public Collection allSucc(); + + public Collection allPred(); + + String getElementPresentation(); + + public int num(); +} diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java new file mode 100644 index 000000000000..9b039dac5aa5 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -0,0 +1,523 @@ +package com.jetbrains.python.codeInsight.controlflow; + +import com.intellij.openapi.util.Pair; +import com.intellij.openapi.util.Ref; +import com.intellij.psi.PsiElement; +import com.intellij.psi.util.PsiTreeUtil; +import com.jetbrains.python.psi.*; +import com.jetbrains.python.codeInsight.controlflow.impl.*; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author oleg + */ +public class PyControlFlowBuilder extends PyRecursiveElementVisitor { + // Here we store all the instructions + private List myInstructions; + + private Instruction myPrevInstruction; + + // Here we store all the pending instructions with their scope + private List> myPending; + + private int myInstructionNumber; + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +//// Control flow builder staff +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + + public ControlFlow buildControlFlow(@NotNull final ControlFlowOwner owner) { + myInstructions = new ArrayList(); + myPending = new ArrayList>(); + myInstructionNumber = 0; + + // create start pseudo node + startNode(null); + + owner.acceptChildren(this); + + // create end pseudo node and close all pending edges + checkPending(startNode(null)); + + return new ControlFlowImpl(myInstructions.toArray(new Instruction[myInstructions.size()])); + } + + @Nullable + private Instruction findInstructionByElement(final PsiElement element) { + for (int i = myInstructions.size() - 1; i >= 0; i--) { + final Instruction instruction = myInstructions.get(i); + if (element.equals(instruction.getElement())) { + return instruction; + } + } + return null; + } + + /** + * Adds edge between 2 edges + * + * @param beginInstruction Begin of new edge + * @param endInstruction End of new edge + */ + private static void addEdge(final Instruction beginInstruction, final Instruction endInstruction) { + if (beginInstruction == null || endInstruction == null) { + return; + } + if (!beginInstruction.allSucc().contains(endInstruction)) { + beginInstruction.allSucc().add(endInstruction); + } + + if (!endInstruction.allPred().contains(beginInstruction)) { + endInstruction.allPred().add(beginInstruction); + } + } + + /** + * Add new node and set prev instruction pointing to this instruction + * + * @param instruction new instruction + */ + private void addNode(final Instruction instruction) { + myInstructions.add(instruction); + if (myPrevInstruction != null) { + addEdge(myPrevInstruction, instruction); + } + myPrevInstruction = instruction; + } + + /** + * Stops control flow, used for break, next, redo + */ + private void flowAbrupted() { + myPrevInstruction = null; + } + + /** + * Adds pending edge in pendingScope + * + * @param pendingScope Scope for instruction + * @param instruction "Last" pending instruction + */ + private void addPendingEdge(final PsiElement pendingScope, final Instruction instruction) { + if (instruction == null) { + return; + } + + int i = 0; + // another optimization! Place pending before first scope, not contained in pendingScope + // the same logic is used in checkPending + if (pendingScope != null) { + for (; i < myPending.size(); i++) { + final Pair pair = myPending.get(i); + final PsiElement scope = pair.getFirst(); + if (scope == null) { + continue; + } + if (!PsiTreeUtil.isAncestor(scope, pendingScope, true)) { + break; + } + } + } + myPending.add(i, Pair.create(pendingScope, instruction)); + } + + private void checkPending(@NotNull final Instruction instruction) { + final PsiElement element = instruction.getElement(); + if (element == null) { + // if element is null (fake element, we just process all pending) + for (Pair pair : myPending) { + addEdge(pair.getSecond(), instruction); + } + myPending.clear(); + } + else { + // else we just all the pending with scope containing in element + // reverse order is just an optimization + for (int i = myPending.size() - 1; i >= 0; i--) { + final Pair pair = myPending.get(i); + final PsiElement scopeWhenToAdd = pair.getFirst(); + if (scopeWhenToAdd == null) { + continue; + } + if (!PsiTreeUtil.isAncestor(scopeWhenToAdd, element, false)) { + addEdge(pair.getSecond(), instruction); + myPending.remove(i); + } + else { + break; + } + } + } + } + + /** + * Creates instruction for given element, and adds it to myInstructionsStack + * Warning! Always call finishNode after startNode + * + * @param element Element to create instruction for + * @return new instruction + */ + private Instruction startNode(final PyElement element) { + final Instruction instruction = new InstructionImpl(element, myInstructionNumber++); + addNode(instruction); + checkPending(instruction); + return instruction; + } + + /** + * Creates conditional instruction for given element, and adds it to myInstructionsStack + * Warning! Always call finishNode after startNode + * + * @param element Element to create instruction for + * @return new instruction + */ + private Instruction startConditionalNode(final PyElement element, final PyElement condition, final boolean result) { + final ConditionInstruction instruction = new ConditionInstructionImpl(element, myInstructionNumber++, condition, result); + addNode(instruction); + checkPending(instruction); + return instruction; + } + + @Override + public void visitPyFunction(final PyFunction node) { + // Stop here + } + + @Override + public void visitPyClass(final PyClass node) { + // Stop here + } + + @Override + public void visitPyStatement(final PyStatement node) { + startNode(node); + super.visitPyStatement(node); + } + + @Override + public void visitPyAssignmentStatement(final PyAssignmentStatement node) { + startNode(node); + final PyExpression value = node.getAssignedValue(); + if (value != null) { + value.accept(this); + } + for (PyExpression expression : node.getTargets()) { + expression.accept(this); + } + } + + @Override + public void visitPyTargetExpression(final PyTargetExpression node) { + final WriteInstruction instruction = new WriteInstructionImpl(node, node.getName(), myInstructionNumber++); + addNode(instruction); + checkPending(instruction); + } + + @Override + public void visitPyNamedParameter(final PyNamedParameter node) { + final WriteInstruction instruction = new WriteInstructionImpl(node, node.getName(), myInstructionNumber++); + addNode(instruction); + checkPending(instruction); + } + + private Instruction getPrevInstruction(final PyElement condition) { + final Ref head = new Ref(myPrevInstruction); + processPending(new PendingProcessor() { + public void process(final PsiElement pendingScope, final Instruction instruction) { + if (pendingScope != null && PsiTreeUtil.isAncestor(condition, pendingScope, false)) { + head.set(instruction); + } + else { + addPendingEdge(pendingScope, instruction); + } + } + }); + return head.get(); + } + + @Override + public void visitPyIfStatement(final PyIfStatement node) { + startNode(node); + final PyIfPart ifPart = node.getIfPart(); + PyExpression condition = ifPart.getCondition(); + if (condition != null) { + condition.accept(this); + } + // Set the head as the last instruction of condition + Instruction head = getPrevInstruction(condition); + myPrevInstruction = head; + final PyStatementList thenStatements = ifPart.getStatementList(); + if (thenStatements != null) { + startConditionalNode(thenStatements, condition, true); + thenStatements.accept(this); + processPending(new PendingProcessor() { + public void process(final PsiElement pendingScope, final Instruction instruction) { + if (pendingScope != null && PsiTreeUtil.isAncestor(thenStatements, pendingScope, false)) { + addPendingEdge(node, instruction); + } + else { + addPendingEdge(pendingScope, instruction); + } + } + }); + addPendingEdge(node, myPrevInstruction); + } + for (PyIfPart part : node.getElifParts()) { + // restore head + myPrevInstruction = head; + condition = part.getCondition(); + if (condition != null) { + condition.accept(this); + } + // Set the head as the last instruction of condition + head = getPrevInstruction(condition); + myPrevInstruction = head; + startConditionalNode(ifPart, condition, true); + final PyStatementList statementList = part.getStatementList(); + if (statementList != null) { + statementList.accept(this); + } + processPending(new PendingProcessor() { + public void process(final PsiElement pendingScope, final Instruction instruction) { + if (pendingScope != null && PsiTreeUtil.isAncestor(ifPart, pendingScope, false)) { + addPendingEdge(node, instruction); + } + else { + addPendingEdge(pendingScope, instruction); + } + } + }); + addPendingEdge(node, myPrevInstruction); + } + // restore head + myPrevInstruction = head; + final PyElsePart elseBranch = node.getElsePart(); + if (elseBranch != null) { + startConditionalNode(elseBranch, condition, false); + elseBranch.accept(this); + addPendingEdge(node, myPrevInstruction); + } + + } + + @Override + public void visitPyWhileStatement(final PyWhileStatement node) { + final Instruction instruction = startNode(node); + final PyWhilePart whilePart = node.getWhilePart(); + final PyExpression condition = whilePart.getCondition(); + if (condition != null) { + condition.accept(this); + } + final Instruction head = getPrevInstruction(condition); + myPrevInstruction = head; + + // if condition was false + final PyElsePart elsePart = node.getElsePart(); + if (elsePart == null) { + addPendingEdge(node, myPrevInstruction); + } + + final PyStatementList statementList = whilePart.getStatementList(); + if (statementList != null) { + startConditionalNode(statementList, condition, true); + statementList.accept(this); + } + if (myPrevInstruction != null) { + addEdge(myPrevInstruction, instruction); //loop + } + // else part + if (elsePart != null) { + startConditionalNode(statementList, condition, false); + elsePart.accept(this); + addPendingEdge(node, myPrevInstruction); + } + flowAbrupted(); + checkPending(instruction); //check for breaks targeted here + } + + @Override + public void visitPyForStatement(final PyForStatement node) { + startNode(node); + final PyForPart forPart = node.getForPart(); + final PyExpression source = forPart.getSource(); + if (source != null) { + source.accept(this); + } + final Instruction head = myPrevInstruction; + final PyElsePart elsePart = node.getElsePart(); + if (elsePart == null) { + addPendingEdge(node, myPrevInstruction); + } + + final PyStatementList list = forPart.getStatementList(); + if (list != null) { + Instruction bodyInstruction = startNode(list); + final PyExpression target = forPart.getTarget(); + if (target != null) { + target.accept(this); + } + + list.accept(this); + + if (myPrevInstruction != null) { + addEdge(myPrevInstruction, bodyInstruction); //loop + addPendingEdge(node, myPrevInstruction); // exit + } + } + myPrevInstruction = head; + if (elsePart != null) { + elsePart.accept(this); + addPendingEdge(node, myPrevInstruction); // exit + } + flowAbrupted(); + } + + @Override + public void visitPyBreakStatement(final PyBreakStatement node) { + final Instruction breakInstruction = new InstructionImpl(node, myInstructionNumber++); + addNode(breakInstruction); + checkPending(breakInstruction); + final PyLoopStatement loop = node.getLoopStatement(); + if (loop != null) { + addPendingEdge(loop, myPrevInstruction); + flowAbrupted(); + } + } + + @Override + public void visitPyContinueStatement(final PyContinueStatement node) { + final Instruction nextInstruction = new InstructionImpl(node, myInstructionNumber++); + addNode(nextInstruction); + checkPending(nextInstruction); + final PyLoopStatement loop = node.getLoop(); + if (loop != null) { + final Instruction instruction = findInstructionByElement(loop); + if (instruction != null) { + addEdge(myPrevInstruction, instruction); + flowAbrupted(); + } + } + } + + @Override + public void visitPyReturnStatement(final PyReturnStatement node) { + final Instruction instruction = new InstructionImpl(node, myInstructionNumber++); + addNode(instruction); + checkPending(instruction); + final PyExpression expression = node.getExpression(); + if (expression != null) { + expression.accept(this); + } +// Here we process pending instructions!!! + final List> pending = myPending; + myPending = new ArrayList>(); + + for (Pair pair : pending) { + final PsiElement pendingScope = pair.getFirst(); + if (pendingScope != null && PsiTreeUtil.isAncestor(node, pendingScope, false)) { + final Instruction pendingInstruction = pair.getSecond(); + addPendingEdge(null, pendingInstruction); + } + else { + myPending.add(pair); + } + } + + addPendingEdge(null, myPrevInstruction); + flowAbrupted(); + } + + @Override + public void visitPyTryExceptStatement(final PyTryExceptStatement node) { + startNode(node); + +// process body + final PyTryPart tryPart = node.getTryPart(); + startNode(tryPart); + tryPart.accept(this); + final Instruction lastBlockInstruction = myPrevInstruction; + +// Goto else block after execution, or exit + final PyElsePart elsePart = node.getElsePart(); + if (elsePart != null) { + startNode(elsePart); + elsePart.accept(this); + addPendingEdge(node, myPrevInstruction); + } else { + addPendingEdge(node, myPrevInstruction); + } + + final ArrayList rescueInstructions = new ArrayList(); + for (PyExceptPart exceptPart : node.getExceptParts()) { + myPrevInstruction = lastBlockInstruction; + final Instruction rescueInstruction = startNode(exceptPart); + rescueInstructions.add(rescueInstruction); + exceptPart.accept(this); + addPendingEdge(node, myPrevInstruction); + } + + final PyFinallyPart finallyPart = node.getFinallyPart(); + Instruction finallyInstruction = null; + Instruction lastFinallyInstruction = null; + if (finallyPart != null) { + flowAbrupted(); + finallyInstruction = startNode(finallyPart); + finallyPart.accept(this); + lastFinallyInstruction = myPrevInstruction; + addPendingEdge(finallyPart, lastFinallyInstruction); + } + final Ref finallyRef = new Ref(finallyInstruction); + final Ref lastFinallyRef = new Ref(lastFinallyInstruction); + processPending(new PendingProcessor() { + public void process(final PsiElement pendingScope, final Instruction instruction) { + final PyElement pendingElement = instruction.getElement(); + + // handle raise instructions inside compound statement + if (pendingElement instanceof PyRaiseStatement && + PsiTreeUtil.isAncestor(tryPart, pendingElement, false)){ + for (Instruction rescueInstruction : rescueInstructions) { + addEdge(instruction, rescueInstruction); + } + return; + } + // handle return pending instructions inside body if ensure block exists + if (pendingElement instanceof PyReturnStatement && !finallyRef.isNull() && + PsiTreeUtil.isAncestor(node, pendingElement, false)) { + addEdge(instruction, finallyRef.get()); + addPendingEdge(null, lastFinallyRef.get()); + return; + } + + // Handle pending instructions inside body with ensure block + if (pendingElement != null && finallyPart!=null && pendingScope !=finallyPart && + PsiTreeUtil.isAncestor(node, pendingElement, false)) { + addEdge(instruction, finallyRef.get()); + return; + } + addPendingEdge(pendingScope, instruction); + } + }); + } + + @Override + public void visitPyListCompExpression(final PyListCompExpression node) { + super.visitPyListCompExpression(node); + } + + + private static interface PendingProcessor { + void process(PsiElement pendingScope, Instruction instruction); + } + + private void processPending(final PendingProcessor processor) { + final List> pending = myPending; + myPending = new ArrayList>(); + for (Pair pair : pending) { + processor.process(pair.getFirst(), pair.getSecond()); + } + } +} diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/WriteInstruction.java b/python/src/com/jetbrains/python/codeInsight/controlflow/WriteInstruction.java new file mode 100644 index 000000000000..c11f2b152cf2 --- /dev/null +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/WriteInstruction.java @@ -0,0 +1,8 @@ +package com.jetbrains.python.codeInsight.controlflow; + +/** + * @author oleg + */ +public interface WriteInstruction extends Instruction { + String getName(); +} diff --git a/python/src/com/jetbrains/python/psi/PyClass.java b/python/src/com/jetbrains/python/psi/PyClass.java index 0365eef2c835..4045da959cb1 100644 --- a/python/src/com/jetbrains/python/psi/PyClass.java +++ b/python/src/com/jetbrains/python/psi/PyClass.java @@ -3,7 +3,7 @@ package com.jetbrains.python.psi; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.StubBasedPsiElement; -import com.jetbrains.python.psi.controlflow.ControlFlowOwner; +import com.jetbrains.python.codeInsight.controlflow.ControlFlowOwner; import com.jetbrains.python.psi.stubs.PyClassStub; import org.jetbrains.annotations.NonNls; import org.jetbrains.annotations.NotNull; diff --git a/python/src/com/jetbrains/python/psi/PyFile.java b/python/src/com/jetbrains/python/psi/PyFile.java index 5d7fec7e3b63..057d3e743024 100644 --- a/python/src/com/jetbrains/python/psi/PyFile.java +++ b/python/src/com/jetbrains/python/psi/PyFile.java @@ -6,7 +6,7 @@ package com.jetbrains.python.psi; import com.intellij.openapi.util.Key; import com.intellij.psi.PsiElement; import com.intellij.psi.PsiFile; -import com.jetbrains.python.psi.controlflow.ControlFlowOwner; +import com.jetbrains.python.codeInsight.controlflow.ControlFlowOwner; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/python/src/com/jetbrains/python/psi/PyFunction.java b/python/src/com/jetbrains/python/psi/PyFunction.java index 9a0cef98b4ab..a12da2900f56 100644 --- a/python/src/com/jetbrains/python/psi/PyFunction.java +++ b/python/src/com/jetbrains/python/psi/PyFunction.java @@ -3,7 +3,7 @@ package com.jetbrains.python.psi; import com.intellij.lang.ASTNode; import com.intellij.psi.PsiNamedElement; import com.intellij.psi.StubBasedPsiElement; -import com.jetbrains.python.psi.controlflow.ControlFlowOwner; +import com.jetbrains.python.codeInsight.controlflow.ControlFlowOwner; import com.jetbrains.python.psi.stubs.PyFunctionStub; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; diff --git a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java index b6d196629583..ae78a316635e 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyClassImpl.java @@ -13,8 +13,8 @@ import com.jetbrains.python.PyNames; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonDosStringFinder; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.controlflow.ControlFlow; -import com.jetbrains.python.psi.controlflow.PyControlFlowBuilder; +import com.jetbrains.python.codeInsight.controlflow.ControlFlow; +import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder; import com.jetbrains.python.psi.resolve.PyResolveUtil; import com.jetbrains.python.psi.resolve.VariantsProcessor; import com.jetbrains.python.psi.stubs.PyClassStub; diff --git a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java index f764e4d8b1a8..f7a8a1f2154c 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFileImpl.java @@ -17,8 +17,8 @@ import com.jetbrains.python.PythonDosStringFinder; import com.jetbrains.python.PythonFileType; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.controlflow.ControlFlow; -import com.jetbrains.python.psi.controlflow.PyControlFlowBuilder; +import com.jetbrains.python.codeInsight.controlflow.ControlFlow; +import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder; import com.jetbrains.python.psi.resolve.PyResolveUtil; import com.jetbrains.python.psi.resolve.ResolveProcessor; import com.jetbrains.python.psi.types.PyModuleType; diff --git a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java index 11bbebbef963..1b97042a2e36 100644 --- a/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java +++ b/python/src/com/jetbrains/python/psi/impl/PyFunctionImpl.java @@ -28,8 +28,8 @@ import com.jetbrains.python.PyElementTypes; import com.jetbrains.python.PyTokenTypes; import com.jetbrains.python.PythonDosStringFinder; import com.jetbrains.python.psi.*; -import com.jetbrains.python.psi.controlflow.ControlFlow; -import com.jetbrains.python.psi.controlflow.PyControlFlowBuilder; +import com.jetbrains.python.codeInsight.controlflow.ControlFlow; +import com.jetbrains.python.codeInsight.controlflow.PyControlFlowBuilder; import com.jetbrains.python.psi.stubs.PyClassStub; import com.jetbrains.python.psi.stubs.PyFunctionStub; import com.jetbrains.python.toolbox.SingleIterable; diff --git a/python/testData/codeInsight/controlflow/break.py b/python/testData/codeInsight/controlflow/break.py new file mode 100644 index 000000000000..ad8de00e61ee --- /dev/null +++ b/python/testData/codeInsight/controlflow/break.py @@ -0,0 +1,4 @@ +while foo + if condition + break + puts "Hello" diff --git a/python/testData/codeInsight/controlflow/break.txt b/python/testData/codeInsight/controlflow/break.txt new file mode 100644 index 000000000000..205ce227ef59 --- /dev/null +++ b/python/testData/codeInsight/controlflow/break.txt @@ -0,0 +1,9 @@ +0(1) element: null +1(2,8) element: PyWhileStatement +2(3) element: PyStatementList. Condition: foo:true +3(4,6) element: PyIfStatement +4(5) element: PyStatementList. Condition: condition:true +5(8) element: PyBreakStatement +6(7) element: PyExpressionStatement +7(1) element: PyExpressionStatement +8() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/continue.py b/python/testData/codeInsight/controlflow/continue.py new file mode 100644 index 000000000000..30001de62917 --- /dev/null +++ b/python/testData/codeInsight/controlflow/continue.py @@ -0,0 +1,4 @@ +while foo + if condition + continue + puts "Hello" \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/continue.txt b/python/testData/codeInsight/controlflow/continue.txt new file mode 100644 index 000000000000..5f64d783a311 --- /dev/null +++ b/python/testData/codeInsight/controlflow/continue.txt @@ -0,0 +1,9 @@ +0(1) element: null +1(2,8) element: PyWhileStatement +2(3) element: PyStatementList. Condition: foo:true +3(4,6) element: PyIfStatement +4(5) element: PyStatementList. Condition: condition:true +5(1) element: PyContinueStatement +6(7) element: PyExpressionStatement +7(1) element: PyExpressionStatement +8() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/file.py b/python/testData/codeInsight/controlflow/file.py new file mode 100644 index 000000000000..cf94f66ad0db --- /dev/null +++ b/python/testData/codeInsight/controlflow/file.py @@ -0,0 +1,2 @@ +aaa = 12 +print(aaa) \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/file.txt b/python/testData/codeInsight/controlflow/file.txt new file mode 100644 index 000000000000..b9308de14866 --- /dev/null +++ b/python/testData/codeInsight/controlflow/file.txt @@ -0,0 +1,5 @@ +0(1) element: null +1(2) element: PyAssignmentStatement +2(3) WRITE ACCESS: aaa +3(4) element: PyPrintStatement +4() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/for.py b/python/testData/codeInsight/controlflow/for.py new file mode 100644 index 000000000000..4dd60ddc2fe0 --- /dev/null +++ b/python/testData/codeInsight/controlflow/for.py @@ -0,0 +1,4 @@ +for i in range(10) + print(i) +else + print "Something went wrong" \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/for.txt b/python/testData/codeInsight/controlflow/for.txt new file mode 100644 index 000000000000..b3cc468479ff --- /dev/null +++ b/python/testData/codeInsight/controlflow/for.txt @@ -0,0 +1,7 @@ +0(1) element: null +1(2,5) element: PyForStatement +2(3) element: PyStatementList +3(4) WRITE ACCESS: i +4(2,6) element: PyPrintStatement +5(6) element: PyPrintStatement +6() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/function.py b/python/testData/codeInsight/controlflow/function.py new file mode 100644 index 000000000000..e23c5cffc3eb --- /dev/null +++ b/python/testData/codeInsight/controlflow/function.py @@ -0,0 +1,14 @@ +def help(object, spacing=10, collapse=1): + """Выводит методы и строки документации. + + В качестве аргумента может использоваться модуль, класс, список, словарь + или строка.""" + methodList = [method for method in dir(object) if callable(getattr(object, method))] + processFunc = collapse and (lambda s: " ".join(s.split())) or (lambda s: s) + print "\n".join(["%s %s" % + (method.ljust(spacing), + processFunc(str(getattr(object, method).__doc__))) + for method in methodList]) + +if __name__ == "__main__": + print help.__doc__ \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/function.txt b/python/testData/codeInsight/controlflow/function.txt new file mode 100644 index 000000000000..7fc63b695986 --- /dev/null +++ b/python/testData/codeInsight/controlflow/function.txt @@ -0,0 +1,15 @@ +0(1) element: null +1(2) WRITE ACCESS: object +2(3) WRITE ACCESS: spacing +3(4) WRITE ACCESS: collapse +4(5) element: PyExpressionStatement +5(6) element: PyAssignmentStatement +6(7) WRITE ACCESS: method +7(8) WRITE ACCESS: methodList +8(9) element: PyAssignmentStatement +9(10) WRITE ACCESS: s +10(11) WRITE ACCESS: s +11(12) WRITE ACCESS: processFunc +12(13) element: PyPrintStatement +13(14) WRITE ACCESS: method +14() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/if.py b/python/testData/codeInsight/controlflow/if.py new file mode 100644 index 000000000000..84b47ac8259a --- /dev/null +++ b/python/testData/codeInsight/controlflow/if.py @@ -0,0 +1,19 @@ +if 0: + pass + +if 1: + pass +else: + pass + +if 2: + pass +elif 3: + pass + +if 4: + pass +elif 5: + pass +else: + 1 diff --git a/python/testData/codeInsight/controlflow/if.txt b/python/testData/codeInsight/controlflow/if.txt new file mode 100644 index 000000000000..a6589785062e --- /dev/null +++ b/python/testData/codeInsight/controlflow/if.txt @@ -0,0 +1,15 @@ +0(1) element: null +1(2,3) element: PyIfStatement +2(3) element: PyStatementList. Condition: 0:true +3(4,5) element: PyIfStatement +4(6) element: PyStatementList. Condition: 1:true +5(6) element: PyElsePart. Condition: 1:false +6(7,8,9) element: PyIfStatement +7(9) element: PyStatementList. Condition: 2:true +8(9) element: PyIfPartIf. Condition: 3:true +9(10,11,12) element: PyIfStatement +10(14) element: PyStatementList. Condition: 4:true +11(14) element: PyIfPartIf. Condition: 5:true +12(13) element: PyElsePart. Condition: 5:false +13(14) element: PyExpressionStatement +14() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/listcomp.py b/python/testData/codeInsight/controlflow/listcomp.py new file mode 100644 index 000000000000..1c5a37fccd65 --- /dev/null +++ b/python/testData/codeInsight/controlflow/listcomp.py @@ -0,0 +1 @@ +[k for k, v in params.items()] \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/listcomp.txt b/python/testData/codeInsight/controlflow/listcomp.txt new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/python/testData/codeInsight/controlflow/return.py b/python/testData/codeInsight/controlflow/return.py new file mode 100644 index 000000000000..07d52abdd31e --- /dev/null +++ b/python/testData/codeInsight/controlflow/return.py @@ -0,0 +1,4 @@ +while foo + if condition + return "Result" + puts "Hello" \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/return.txt b/python/testData/codeInsight/controlflow/return.txt new file mode 100644 index 000000000000..28c4603c6674 --- /dev/null +++ b/python/testData/codeInsight/controlflow/return.txt @@ -0,0 +1,9 @@ +0(1) element: null +1(2,8) element: PyWhileStatement +2(3) element: PyStatementList. Condition: foo:true +3(4,6) element: PyIfStatement +4(5) element: PyStatementList. Condition: condition:true +5(8) element: PyReturnStatement +6(7) element: PyExpressionStatement +7(1) element: PyExpressionStatement +8() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/try.py b/python/testData/codeInsight/controlflow/try.py new file mode 100644 index 000000000000..937bc42a8f82 --- /dev/null +++ b/python/testData/codeInsight/controlflow/try.py @@ -0,0 +1,13 @@ +import sys + +try: + f = open('myfile.txt') + s = f.readline() + i = int(s.strip()) +except IOError as (errno, strerror): + print "I/O error({0}): {1}".format(errno, strerror) +except ValueError: + print "Could not convert data to an integer." +except: + print "Unexpected error:", sys.exc_info()[0] + raise \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/try.txt b/python/testData/codeInsight/controlflow/try.txt new file mode 100644 index 000000000000..fb51604bd6df --- /dev/null +++ b/python/testData/codeInsight/controlflow/try.txt @@ -0,0 +1,20 @@ +0(1) element: null +1(2) element: PyImportStatement +2(3) element: PyTryExceptStatement +3(4) element: PyTryPart +4(5) element: PyAssignmentStatement +5(6) WRITE ACCESS: f +6(7) element: PyAssignmentStatement +7(8) WRITE ACCESS: s +8(9) element: PyAssignmentStatement +9(10,14,16,19) WRITE ACCESS: i +10(11) element: PyExceptPart +11(12) WRITE ACCESS: errno +12(13) WRITE ACCESS: strerror +13(19) element: PyPrintStatement +14(15) element: PyExceptPart +15(19) element: PyPrintStatement +16(17) element: PyExceptPart +17(18) element: PyPrintStatement +18(19) element: PyRaiseStatement +19() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/while.py b/python/testData/codeInsight/controlflow/while.py new file mode 100644 index 000000000000..04c4f9cf3854 --- /dev/null +++ b/python/testData/codeInsight/controlflow/while.py @@ -0,0 +1,4 @@ +while condition + print("Alloha!") +else: + print("Bye") \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/while.txt b/python/testData/codeInsight/controlflow/while.txt new file mode 100644 index 000000000000..fc68c9ef50a3 --- /dev/null +++ b/python/testData/codeInsight/controlflow/while.txt @@ -0,0 +1,7 @@ +0(1) element: null +1(2) element: PyWhileStatement +2(3) element: PyStatementList. Condition: condition:true +3(1,4) element: PyPrintStatement +4(5) element: PyStatementList. Condition: condition:false +5(6) element: PyPrintStatement +6() element: null \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java index 463ffeff8be4..4764d2cefaea 100644 --- a/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java +++ b/python/testSrc/com/jetbrains/python/PyControlFlowBuilderTest.java @@ -7,8 +7,8 @@ import com.intellij.openapi.vfs.VirtualFile; import com.jetbrains.python.fixtures.LightMarkedTestCase; import com.jetbrains.python.psi.PyFile; import com.jetbrains.python.psi.PyFunction; -import com.jetbrains.python.psi.controlflow.ControlFlow; -import com.jetbrains.python.psi.controlflow.Instruction; +import com.jetbrains.python.codeInsight.controlflow.ControlFlow; +import com.jetbrains.python.codeInsight.controlflow.Instruction; import java.io.File; import java.io.IOException; @@ -19,7 +19,7 @@ import java.io.IOException; public class PyControlFlowBuilderTest extends LightMarkedTestCase { public String getTestDataPath() { - return PythonTestUtil.getTestDataPath() + "/psi/controlflow/"; + return PythonTestUtil.getTestDataPath() + "/codeInsight/controlflow/"; } private void doTest() throws Exception {