From 99941e03ffc4848f60ff0a47dd80b203e2e72449 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Mon, 25 Apr 2011 17:02:20 +0400 Subject: [PATCH] Fixed bugs in control flow for Python list comprehensions (PY-2574) --- .../controlflow/PyControlFlowBuilder.java | 83 ++++++++++++++----- .../codeInsight/controlflow/function.txt | 69 ++++++++------- .../codeInsight/controlflow/lambda.txt | 17 ++-- .../codeInsight/controlflow/listcomp.py | 5 +- .../codeInsight/controlflow/listcomp.txt | 27 ++++-- .../codeInsight/controlflow/returnfor.txt | 13 +-- 6 files changed, 139 insertions(+), 75 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index d212d6751be2..d7d6c584d8b4 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -506,36 +506,75 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { @Override public void visitPyComprehensionElement(final PyComprehensionElement node) { - myBuilder.startNode(node); PyExpression prevCondition = null; - for (ComprhIfComponent component : node.getIfComponents()) { - final PyExpression condition = component.getTest(); - if (condition != null) { - condition.accept(this); - final Instruction head = myBuilder.prevInstruction; - final Instruction prevInstruction = - prevCondition != null ? myBuilder.startConditionalNode(condition, prevCondition, true) : myBuilder.startNode(condition); - prevCondition = condition; - // restore head - myBuilder.prevInstruction = head; - myBuilder.addPendingEdge(node, head); // false condition - myBuilder.prevInstruction = prevInstruction; - } - } + myBuilder.startNode(node); + List iterators = new ArrayList(); - for (ComprhForComponent forComponent : node.getForComponents()) { - final PyExpression iteratedList = forComponent.getIteratedList(); - if (prevCondition != null) { - myBuilder.startConditionalNode(iteratedList, prevCondition, true); - prevCondition = null; + for (ComprehensionComponent component : node.getComponents()) { + if (component instanceof ComprhForComponent) { + final ComprhForComponent c = (ComprhForComponent) component; + final PyExpression iteratedList = c.getIteratedList(); + final PyExpression iteratorVariable = c.getIteratorVariable(); + if (prevCondition != null) { + myBuilder.startConditionalNode(iteratedList, prevCondition, true); + prevCondition = null; + } + else { + myBuilder.startNode(iteratedList); + } + iteratedList.accept(this); + + // for-loop continue and exit + for (Instruction i : iterators) { + myBuilder.addEdge(myBuilder.prevInstruction, i); + } + myBuilder.addPendingEdge(node, myBuilder.prevInstruction); + + final Instruction iterator = myBuilder.startNode(iteratorVariable); + iteratorVariable.accept(this); + + // Inner "for" and "if" constructs will be linked to all outer iterators + iterators.add(iterator); + } + else if (component instanceof ComprhIfComponent) { + final ComprhIfComponent c = (ComprhIfComponent) component; + final PyExpression condition = c.getTest(); + if (condition == null) { + continue; + } + if (prevCondition != null) { + myBuilder.startConditionalNode(condition, prevCondition, true); + } + else { + myBuilder.startNode(condition); + } + condition.accept(this); + + // Condition is true for nested "for" and "if" constructs, next startNode() should create a conditional node + prevCondition = condition; + + // for-loop continue and exit + for (Instruction i : iterators) { + myBuilder.addEdge(myBuilder.prevInstruction, i); + } + myBuilder.addPendingEdge(node, myBuilder.prevInstruction); } - iteratedList.accept(this); - forComponent.getIteratorVariable().accept(this); } final PyExpression result = node.getResultExpression(); if (result != null) { + if (prevCondition != null) { + myBuilder.startConditionalNode(result, prevCondition, true); + } + else { + myBuilder.startNode(result); + } result.accept(this); + + // for-loop continue + for (Instruction i : iterators) { + myBuilder.addEdge(myBuilder.prevInstruction, i); + } } } diff --git a/python/testData/codeInsight/controlflow/function.txt b/python/testData/codeInsight/controlflow/function.txt index a7171a58cb30..3cc397d79d4b 100644 --- a/python/testData/codeInsight/controlflow/function.txt +++ b/python/testData/codeInsight/controlflow/function.txt @@ -5,35 +5,40 @@ 4(5) element: PyExpressionStatement 5(6) element: PyAssignmentStatement 6(7) element: PyListCompExpression -7(8) READ ACCESS: callable -8(9) READ ACCESS: getattr -9(10) READ ACCESS: object -10(11,17) READ ACCESS: method -11(12) element: PyCallExpression: callable -12(13) element: PyCallExpression: dir. Condition: callable(getattr(object, method)):true -13(14) READ ACCESS: dir -14(15) READ ACCESS: object -15(16) WRITE ACCESS: method -16(17) READ ACCESS: method -17(18) WRITE ACCESS: methodList -18(19) element: PyAssignmentStatement -19(20) READ ACCESS: collapse -20(21) element: PyLambdaExpression -21(22) WRITE ACCESS: s -22(23) READ ACCESS: s -23(24) element: PyLambdaExpression -24(25) WRITE ACCESS: s -25(26) READ ACCESS: s -26(27) WRITE ACCESS: processFunc -27(28) element: PyPrintStatement -28(29) element: PyListCompExpression -29(30) READ ACCESS: methodList -30(31) WRITE ACCESS: method -31(32) READ ACCESS: method -32(33) READ ACCESS: spacing -33(34) READ ACCESS: processFunc -34(35) READ ACCESS: str -35(36) READ ACCESS: getattr -36(37) READ ACCESS: object -37(38) READ ACCESS: method -38() element: null \ No newline at end of file +7(8) element: PyCallExpression: dir +8(9) READ ACCESS: dir +9(10,19) READ ACCESS: object +10(11) element: PyTargetExpression: method +11(12) WRITE ACCESS: method +12(13) element: PyCallExpression: callable +13(14) READ ACCESS: callable +14(15) READ ACCESS: getattr +15(16) READ ACCESS: object +16(10,17,19) READ ACCESS: method +17(18) element: PyReferenceExpression: method. Condition: callable(getattr(object, method)):true +18(10,19) READ ACCESS: method +19(20) WRITE ACCESS: methodList +20(21) element: PyAssignmentStatement +21(22) READ ACCESS: collapse +22(23) element: PyLambdaExpression +23(24) WRITE ACCESS: s +24(25) READ ACCESS: s +25(26) element: PyLambdaExpression +26(27) WRITE ACCESS: s +27(28) READ ACCESS: s +28(29) WRITE ACCESS: processFunc +29(30) element: PyPrintStatement +30(31) element: PyListCompExpression +31(32) element: PyReferenceExpression: methodList +32(33,43) READ ACCESS: methodList +33(34) element: PyTargetExpression: method +34(35) WRITE ACCESS: method +35(36) element: PyBinaryExpression +36(37) READ ACCESS: method +37(38) READ ACCESS: spacing +38(39) READ ACCESS: processFunc +39(40) READ ACCESS: str +40(41) READ ACCESS: getattr +41(42) READ ACCESS: object +42(33,43) READ ACCESS: method +43() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/lambda.txt b/python/testData/codeInsight/controlflow/lambda.txt index 26bc5414d098..1a740c2d5ed7 100644 --- a/python/testData/codeInsight/controlflow/lambda.txt +++ b/python/testData/codeInsight/controlflow/lambda.txt @@ -1,10 +1,13 @@ 0(1) element: null 1(2) element: PyExpressionStatement 2(3) element: PySetCompExpression -3(4) READ ACCESS: range -4(5) WRITE ACCESS: i -5(6) element: PyLambdaExpression -6(7) READ ACCESS: i -7(8) WRITE ACCESS: i -8(9) READ ACCESS: i -9() element: null \ No newline at end of file +3(4) element: PyCallExpression: range +4(5,12) READ ACCESS: range +5(6) element: PyTargetExpression: i +6(7) WRITE ACCESS: i +7(8) element: PyParenthesizedExpression +8(9) element: PyLambdaExpression +9(10) READ ACCESS: i +10(11) WRITE ACCESS: i +11(5,12) READ ACCESS: i +12() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/listcomp.py b/python/testData/codeInsight/controlflow/listcomp.py index 5b7e3856adb6..95270a72b437 100644 --- a/python/testData/codeInsight/controlflow/listcomp.py +++ b/python/testData/codeInsight/controlflow/listcomp.py @@ -1 +1,4 @@ -[k for k, v in params.items() if foo] \ No newline at end of file +[(x, y) for k, v in params.items() + if k + for x, y in v + if x > y] diff --git a/python/testData/codeInsight/controlflow/listcomp.txt b/python/testData/codeInsight/controlflow/listcomp.txt index 7c3dcadd6199..55c360495da0 100644 --- a/python/testData/codeInsight/controlflow/listcomp.txt +++ b/python/testData/codeInsight/controlflow/listcomp.txt @@ -1,11 +1,22 @@ 0(1) element: null 1(2) element: PyExpressionStatement 2(3) element: PyListCompExpression -3(4,10) READ ACCESS: foo -4(5) element: PyReferenceExpression: foo -5(6) element: PyCallExpression: params.items. Condition: foo:true -6(7) READ ACCESS: params -7(8) WRITE ACCESS: k -8(9) WRITE ACCESS: v -9(10) READ ACCESS: k -10() element: null \ No newline at end of file +3(4) element: PyCallExpression: params.items +4(5,21) READ ACCESS: params +5(6) element: PyTupleExpression +6(7) WRITE ACCESS: k +7(8) WRITE ACCESS: v +8(9) element: PyReferenceExpression: k +9(5,10,21) READ ACCESS: k +10(11) element: PyReferenceExpression: v. Condition: k:true +11(5,12,21) READ ACCESS: v +12(13) element: PyTupleExpression +13(14) WRITE ACCESS: x +14(15) WRITE ACCESS: y +15(16) element: PyBinaryExpression +16(17) READ ACCESS: x +17(5,12,18,21) READ ACCESS: y +18(19) element: PyParenthesizedExpression. Condition: x > y:true +19(20) READ ACCESS: x +20(5,12,21) READ ACCESS: y +21() element: null \ No newline at end of file diff --git a/python/testData/codeInsight/controlflow/returnfor.txt b/python/testData/codeInsight/controlflow/returnfor.txt index 1dfe5f1bdb70..6bbaac259c87 100644 --- a/python/testData/codeInsight/controlflow/returnfor.txt +++ b/python/testData/codeInsight/controlflow/returnfor.txt @@ -2,8 +2,11 @@ 1(2) element: PyReturnStatement 2(3) READ ACCESS: dict 3(4) element: PyGeneratorExpression -4(5) WRITE ACCESS: name -5(6) WRITE ACCESS: val -6(7) READ ACCESS: name -7(8) READ ACCESS: val -8() element: null \ No newline at end of file +4(5,11) element: PyDictLiteralExpression +5(6) element: PyTupleExpression +6(7) WRITE ACCESS: name +7(8) WRITE ACCESS: val +8(9) element: PyParenthesizedExpression +9(10) READ ACCESS: name +10(5,11) READ ACCESS: val +11() element: null \ No newline at end of file