From 566d20977d0139b9eeac7b88b0345a4159d78ff9 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Fri, 13 May 2011 19:52:34 +0400 Subject: [PATCH 1/3] Fixed control flow break edges for try-except-finally (PY-3503) --- .../controlflow/PyControlFlowBuilder.java | 75 +++++++++++++------ .../codeInsight/controlflow/doubletry.txt | 4 +- .../controlflow/fortrycontinue.txt | 4 +- .../codeInsight/controlflow/raise.txt | 2 +- .../testData/codeInsight/controlflow/try.txt | 12 +-- .../codeInsight/controlflow/trybreak.txt | 4 +- .../controlflow/tryexceptelsefinally.txt | 10 +-- .../codeInsight/controlflow/tryfinally.txt | 2 +- .../expected.xml | 5 -- 9 files changed, 70 insertions(+), 48 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java index d7d6c584d8b4..28a3a7bf1eb0 100644 --- a/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java +++ b/python/src/com/jetbrains/python/codeInsight/controlflow/PyControlFlowBuilder.java @@ -110,7 +110,11 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { return false; } final PyNamedParameter named = params[0].getAsNamed(); - return named != null && named.getName().equals(qualifier.getText()); + if (named == null) { + return false; + } + final String name = named.getName(); + return name != null && name.equals(qualifier.getText()); } @Override @@ -414,13 +418,13 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { public void visitPyTryExceptStatement(final PyTryExceptStatement node) { myBuilder.startNode(node); -// process body + // Process body final PyTryPart tryPart = node.getTryPart(); myBuilder.startNode(tryPart); tryPart.accept(this); final Instruction lastBlockInstruction = myBuilder.prevInstruction; -// Goto else block after execution, or exit + // Goto else block after execution, or exit final PyElsePart elsePart = node.getElsePart(); if (elsePart != null) { myBuilder.startNode(elsePart); @@ -458,30 +462,46 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { lastFinallyInstruction = myBuilder.prevInstruction; myBuilder.addPendingEdge(finallyPart, lastFinallyInstruction); } + + 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.isAncestor(tryPart, e, true)) { + for (Instruction inst : exceptInstructions) { + myBuilder.addEdge(instruction, inst); + } + if (finallyPart != null) { + myBuilder.addEdge(instruction, finallyInstruction); + } + } + if (finallyPart != null) { + // All instructions inside except parts have edges to the finally part + for (PyExceptPart exceptPart : node.getExceptParts()) { + if (PsiTreeUtil.isAncestor(exceptPart, e, true)) { + myBuilder.addEdge(instruction, finallyInstruction); + } + } + // All instructions inside the else part have edges to the finally part + if (PsiTreeUtil.isAncestor(elsePart, e, true)) { + myBuilder.addEdge(instruction, finallyInstruction); + } + } + } + final Ref finallyRef = new Ref(finallyInstruction); final Ref lastFinallyRef = new Ref(lastFinallyInstruction); + myBuilder.processPending(new ControlFlowBuilder.PendingProcessor() { - @SuppressWarnings({"ConstantConditions"}) public void process(final PsiElement pendingScope, final Instruction instruction) { final PsiElement pendingElement = instruction.getElement(); - - // Handle raise statements inside try part - if (isRaiseInstruction(pendingElement) && PsiTreeUtil.isAncestor(tryPart, pendingElement, false)) { - for (Instruction inst : exceptInstructions) { - myBuilder.addEdge(instruction, inst); - } - if (finallyPart!=null) { - myBuilder.addEdge(instruction, finallyRef.get()); - } + if (pendingElement == null) { return; } - else if (pendingElement != null && PsiTreeUtil.isAncestor(tryPart, pendingElement, false)){ - for (Instruction inst : exceptInstructions) { - myBuilder.addEdge(instruction, inst); - } - } - // handle return pending instructions inside try if final block exists + // Handle return pending instructions inside try if final block exists final boolean isPending = PsiTreeUtil.isAncestor(node, pendingElement, false) && (finallyPart == null || !PsiTreeUtil.isAncestor(finallyPart, pendingElement, false)); if (!finallyRef.isNull() && isPending) { @@ -500,10 +520,6 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { }); } - private boolean isRaiseInstruction(final PsiElement pendingElement) { - return pendingElement != null && PsiTreeUtil.getParentOfType(pendingElement, PyRaiseStatement.class) != null; - } - @Override public void visitPyComprehensionElement(final PyComprehensionElement node) { PyExpression prevCondition = null; @@ -667,4 +683,15 @@ public class PyControlFlowBuilder extends PyRecursiveElementVisitor { myBuilder.flowAbrupted(); } -} \ No newline at end of file + private static boolean canRaiseExceptions(final Instruction instruction) { + if (instruction instanceof ReadWriteInstruction) { + return true; + } + return !PsiTreeUtil.instanceOf(instruction.getElement(), + PyReturnStatement.class, + PyAssignmentStatement.class, + PyRaiseStatement.class, + PyStatementList.class); + } +} + diff --git a/python/testData/codeInsight/controlflow/doubletry.txt b/python/testData/codeInsight/controlflow/doubletry.txt index 8a3fd8097c6f..4a98ba533cc8 100644 --- a/python/testData/codeInsight/controlflow/doubletry.txt +++ b/python/testData/codeInsight/controlflow/doubletry.txt @@ -1,8 +1,8 @@ 0(1) element: null 1(2) element: PyTryExceptStatement 2(3) element: PyTryPart -3(4) element: PyFromImportStatement -4(5) READ ACCESS: mercurial +3(4,6) element: PyFromImportStatement +4(5,6) READ ACCESS: mercurial 5(6,10) READ ACCESS: lsprof 6(7) element: PyExceptPart 7(8) READ ACCESS: ImportError diff --git a/python/testData/codeInsight/controlflow/fortrycontinue.txt b/python/testData/codeInsight/controlflow/fortrycontinue.txt index ca76031e4ac2..8180e38ea8b4 100644 --- a/python/testData/codeInsight/controlflow/fortrycontinue.txt +++ b/python/testData/codeInsight/controlflow/fortrycontinue.txt @@ -9,8 +9,8 @@ 8(9) element: PyTryPart 9(10,14,5,17) element: PyForStatement 10(11) element: PyStatementList -11(12) WRITE ACCESS: t -12(13) element: PyPrintStatement +11(12,14) WRITE ACCESS: t +12(13,14) element: PyPrintStatement 13(10,14,5,17) READ ACCESS: t 14(15) element: PyExceptPart 15(16) READ ACCESS: Exception diff --git a/python/testData/codeInsight/controlflow/raise.txt b/python/testData/codeInsight/controlflow/raise.txt index 1461a143404e..e3f9d533f122 100644 --- a/python/testData/codeInsight/controlflow/raise.txt +++ b/python/testData/codeInsight/controlflow/raise.txt @@ -2,7 +2,7 @@ 1(2) element: PyTryExceptStatement 2(3) element: PyTryPart 3(4) element: PyRaiseStatement -4(5) READ ACCESS: Exception +4(5,11) READ ACCESS: Exception 5(6) element: PyExceptPart 6(7) element: PyAssignmentStatement 7(8) READ ACCESS: Exception diff --git a/python/testData/codeInsight/controlflow/try.txt b/python/testData/codeInsight/controlflow/try.txt index f776b114ad50..763b1b9da84d 100644 --- a/python/testData/codeInsight/controlflow/try.txt +++ b/python/testData/codeInsight/controlflow/try.txt @@ -4,14 +4,14 @@ 3(4) element: PyTryExceptStatement 4(5) element: PyTryPart 5(6) element: PyAssignmentStatement -6(7) READ ACCESS: open -7(8) WRITE ACCESS: f +6(7,15,22,25) READ ACCESS: open +7(8,15,22,25) WRITE ACCESS: f 8(9) element: PyAssignmentStatement -9(10) READ ACCESS: f -10(11) WRITE ACCESS: s +9(10,15,22,25) READ ACCESS: f +10(11,15,22,25) WRITE ACCESS: s 11(12) element: PyAssignmentStatement -12(13) READ ACCESS: int -13(14) READ ACCESS: s +12(13,15,22,25) READ ACCESS: int +13(14,15,22,25) READ ACCESS: s 14(15,22,25,30) WRITE ACCESS: i 15(16) element: PyExceptPart 16(17) READ ACCESS: IOError diff --git a/python/testData/codeInsight/controlflow/trybreak.txt b/python/testData/codeInsight/controlflow/trybreak.txt index c6bfba1fd0f7..8015ea851fa7 100644 --- a/python/testData/codeInsight/controlflow/trybreak.txt +++ b/python/testData/codeInsight/controlflow/trybreak.txt @@ -1,10 +1,10 @@ 0(1) element: null 1(2) element: PyTryExceptStatement 2(3) element: PyTryPart -3(4) element: PyForStatement +3(4,8) element: PyForStatement 4(5,8,11) READ ACCESS: bar 5(6) element: PyStatementList -6(7) WRITE ACCESS: i +6(7,8) WRITE ACCESS: i 7(8,11) element: PyBreakStatement 8(9) element: PyExceptPart 9(10) element: PyRaiseStatement diff --git a/python/testData/codeInsight/controlflow/tryexceptelsefinally.txt b/python/testData/codeInsight/controlflow/tryexceptelsefinally.txt index fbee361d8659..cea2b6a1c881 100644 --- a/python/testData/codeInsight/controlflow/tryexceptelsefinally.txt +++ b/python/testData/codeInsight/controlflow/tryexceptelsefinally.txt @@ -5,14 +5,14 @@ 4(5) element: PyTryExceptStatement 5(6) element: PyTryPart 6(7) element: PyAssignmentStatement -7(8) element: PySubscriptionExpression -8(9) READ ACCESS: d -9(10,13) WRITE ACCESS: v +7(8,13,16) element: PySubscriptionExpression +8(9,13,16) READ ACCESS: d +9(10,13,16) WRITE ACCESS: v 10(11) element: PyElsePart -11(12) element: PyPrintStatement +11(12,16) element: PyPrintStatement 12(16) READ ACCESS: v 13(14) element: PyExceptPart -14(15) READ ACCESS: KeyError +14(15,16) READ ACCESS: KeyError 15(16) element: PyPrintStatement 16(17) element: PyFinallyPart 17(18) element: PyPrintStatement diff --git a/python/testData/codeInsight/controlflow/tryfinally.txt b/python/testData/codeInsight/controlflow/tryfinally.txt index 66914e427d74..db2646c389dd 100644 --- a/python/testData/codeInsight/controlflow/tryfinally.txt +++ b/python/testData/codeInsight/controlflow/tryfinally.txt @@ -5,7 +5,7 @@ 4(5) element: PyTryExceptStatement 5(6) element: PyTryPart 6(7) element: PyAssignmentStatement -7(8) READ ACCESS: open +7(8,9) READ ACCESS: open 8(9) WRITE ACCESS: status 9(10) element: PyFinallyPart 10(11) element: PyIfStatement diff --git a/python/testData/inspections/PyUnusedLocalVariableInspection/expected.xml b/python/testData/inspections/PyUnusedLocalVariableInspection/expected.xml index 11e552f5b12f..36f6272dafdd 100644 --- a/python/testData/inspections/PyUnusedLocalVariableInspection/expected.xml +++ b/python/testData/inspections/PyUnusedLocalVariableInspection/expected.xml @@ -45,11 +45,6 @@ 82 Parameter 'x' value is not used - - test.py - 106 - Local variable 'v' value is not used - test.py 114 From 486f42ebf4d470034c953791046bf6eba2240a53 Mon Sep 17 00:00:00 2001 From: Dmitry Cheryasov Date: Sun, 15 May 2011 00:59:41 +0300 Subject: [PATCH 2/3] Removed a dysfunctional non-__dict__ test. --- python/helpers/test_generator.py | 77 ++++++++++---------------------- 1 file changed, 23 insertions(+), 54 deletions(-) diff --git a/python/helpers/test_generator.py b/python/helpers/test_generator.py index 43aa7098466d..2a08d98e5c10 100644 --- a/python/helpers/test_generator.py +++ b/python/helpers/test_generator.py @@ -12,6 +12,7 @@ M = ModuleRedeclarator import sys IS_CLI = sys.platform == 'cli' +VERSION = sys.version_info[:2] # only (major, minor) class TestRestoreFuncByDocComment(unittest.TestCase): """ @@ -217,7 +218,7 @@ class TestAnnotatedParameters(unittest.TestCase): self.assertEquals(note, M.SIG_DOC_NOTE) -if not IS_CLI: +if not IS_CLI and VERSION < (3, 0): class TestInspect(unittest.TestCase): """ See that inspect actually works if needed @@ -234,8 +235,14 @@ if not IS_CLI: self.assertEquals(result, "(a, b, c=1, *d, **e)") def testNested(self): - def target(a, (b, c), d, e=1): - return a, b, c, d, e + # NOTE: Py3k can't handle nested tuple args, thus we compile it conditionally + code = ( + "def target(a, (b, c), d, e=1):\n" + " return a, b, c, d, e" + ) + namespace = {} + eval(compile(code, "__main__", "single"), namespace) + target = namespace['target'] result = self.m.restoreByInspect(target) self.assertEquals(result, "(a, (b, c), d, e=1)") @@ -247,8 +254,12 @@ class _DiffPrintingTestCase(unittest.TestCase): # print side by side ei = iter(etalon.split("\n")) si = iter(specimen.split("\n")) + if VERSION < (3, 0): + si_next = si.next + else: + si_next = si.__next__ for el in ei: - try: sl = si.next() + try: sl = si_next() except StopIteration: break # I wish the exception would just work as break if el != sl: print("!%s" % el) @@ -273,8 +284,7 @@ class TestSpecialCases(unittest.TestCase): def setUp(self): import sys - major_ver = sys.version_info[0] - if major_ver > 2: + if VERSION >= (3, 0): import builtins as the_builtins self.builtins_name = the_builtins.__name__ @@ -300,50 +310,7 @@ class TestSpecialCases(unittest.TestCase): def testFilter(self): self._testBuiltinFuncName("filter", "(function_or_none, sequence)") -if not IS_CLI: - class TestNonDictClasses(_DiffPrintingTestCase): - """ - Tests classes that don't have a __dict__ - """ - - def setUp(self): - self.m = ModuleRedeclarator(self, None, 4) - - def checkRedoClass(self, p_class, expected): - self.m.redoClass(self.m.classes_buf.out, p_class, p_class.__name__, 0) - result = "".join(self.m.classes_buf.data).strip() - self.assertEquals(expected, result) - - def testOne(self): - class One(object): - """Doc of One""" - __slots__ = ('A', 'B', 'foo') - A = 1 - B = "boo" - - def foo(self, x): - """blah foo(x) -> int""" - return x + 1 - - expected = "\n".join(( - "class One(object):", - ' """ Doc of One """', - ' def foo(self, x): # real signature unknown; restored from __doc__', - ' """ blah foo(x) -> int """', - ' return 0', - "", - " def __init__(self, *args, **kwargs): # real signature unknown", - " pass", - '', - ' A = 1', - " B = 'boo'", - " __slots__ = (", - " 'A',", - " 'B',", - " 'foo',", - " )", - )) - self.checkRedoClass(One, expected) + # we caould want to test a calss without __dict__, but it takes a C extension to really create one, class TestDataOutput(_DiffPrintingTestCase): """ @@ -426,10 +393,12 @@ if not IS_CLI: def testSimplePrefixObject(self): doc = "Makes an instance: object foo(bar)" self.checkRestoreFunction(doc, "object()") - - def testSimpleArrowFile(self): - doc = "Opens a file: foo(bar) -> file" - self.checkRestoreFunction(doc, "file('/dev/null')") + + if VERSION < (3, 0): + # TODO: we only support it in 2.x; must update when we do it in 3.x, too + def testSimpleArrowFile(self): + doc = "Opens a file: foo(bar) -> file" + self.checkRestoreFunction(doc, "file('/dev/null')") def testUnrelatedPrefix(self): doc = """ From 27bbb3e3595d409ecbebd252e7557bcf2c8e4a23 Mon Sep 17 00:00:00 2001 From: Roman Chernyatchik Date: Mon, 16 May 2011 00:25:41 -0400 Subject: [PATCH 3/3] fixed: [IDEA-69141] config dir named .${PRODUCT_SETTINGS_DIR} --- python/build/pycharm.exe4j | 2 +- python/build/pycharm_build.gant | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/python/build/pycharm.exe4j b/python/build/pycharm.exe4j index cc021c1b26b3..83ba648799f4 100644 --- a/python/build/pycharm.exe4j +++ b/python/build/pycharm.exe4j @@ -34,7 +34,7 @@