From c10a567b738068024bc214a939c5788eac2630ae Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Tue, 22 May 2012 22:23:35 +0400 Subject: [PATCH 1/6] Refactored extract method tests --- .../extractmethod/BinaryExpression.after.py | 4 + .../extractmethod/BinaryExpression.before.py | 1 + .../extractmethod/BreakAst.after.py | 6 + .../extractmethod/BreakAst.before.py | 2 + .../extractmethod/ClassContext.after.py | 5 + .../extractmethod/ClassContext.before.py | 2 + ...ssmethod.after.py => ClassMethod.after.py} | 0 ...method.before.py => ClassMethod.before.py} | 0 .../{comment.after.py => Comment.after.py} | 0 .../{comment.before.py => Comment.before.py} | 0 .../{comment2.after.py => Comment2.after.py} | 0 ...{comment2.before.py => Comment2.before.py} | 0 ....before.py => ConditionalReturn.before.py} | 0 .../{elsebody.after.py => ElseBody.after.py} | 0 ...{elsebody.before.py => ElseBody.before.py} | 0 .../extractmethod/Expression.after.py | 8 ++ .../extractmethod/Expression.before.py | 4 + .../{file.after.py => File.after.py} | 0 .../{file.before.py => File.before.py} | 0 .../extractmethod/MethodContext.after.py | 7 + .../extractmethod/MethodContext.before.py | 4 + ...ethodInIf.after.py => MethodInIf.after.py} | 0 ...hodInIf.before.py => MethodInIf.before.py} | 0 .../extractmethod/MethodIndent.after.py | 8 ++ .../extractmethod/MethodIndent.before.py | 5 + .../extractmethod/MethodReturn.after.py | 9 ++ .../extractmethod/MethodReturn.before.py | 6 + .../NameCollisionClass.before.py | 5 + .../extractmethod/NameCollisionFile.before.py | 4 + .../NameCollisionSuperClass.before.py | 7 + .../OutNotEmptyStatements.after.py | 12 ++ .../OutNotEmptyStatements.before.py | 7 + .../OutNotEmptyStatements2.after.py | 12 ++ .../OutNotEmptyStatements2.before.py | 7 + .../extractmethod/Parameter.after.py | 6 + .../extractmethod/Parameter.before.py | 2 + .../refactoring/extractmethod/Py479.after.py | 25 ++++ .../refactoring/extractmethod/Py479.before.py | 22 ++++ ...rn_tuple.after.py => ReturnTuple.after.py} | 0 ..._tuple.before.py => ReturnTuple.before.py} | 0 .../extractmethod/Statement.after.py | 8 ++ .../extractmethod/Statement.before.py | 4 + .../extractmethod/StatementReturn.after.py | 10 ++ .../extractmethod/StatementReturn.before.py | 8 ++ .../extractmethod/Statements.after.py | 10 ++ .../extractmethod/Statements.before.py | 6 + ...cmethod.after.py => StaticMethod.after.py} | 0 ...ethod.before.py => StaticMethod.before.py} | 0 .../extractmethod/WhileOutput.after.py | 11 ++ .../extractmethod/WhileOutput.before.py | 6 + .../WrongSelectionFromImportStar.before.py | 1 + .../WrongSelectionIfPart.before.py | 13 ++ .../refactoring/PyExtractMethodTest.java | 122 +++++++++--------- 53 files changed, 321 insertions(+), 58 deletions(-) create mode 100644 python/testData/refactoring/extractmethod/BinaryExpression.after.py create mode 100644 python/testData/refactoring/extractmethod/BinaryExpression.before.py create mode 100644 python/testData/refactoring/extractmethod/BreakAst.after.py create mode 100644 python/testData/refactoring/extractmethod/BreakAst.before.py create mode 100644 python/testData/refactoring/extractmethod/ClassContext.after.py create mode 100644 python/testData/refactoring/extractmethod/ClassContext.before.py rename python/testData/refactoring/extractmethod/{classmethod.after.py => ClassMethod.after.py} (100%) rename python/testData/refactoring/extractmethod/{classmethod.before.py => ClassMethod.before.py} (100%) rename python/testData/refactoring/extractmethod/{comment.after.py => Comment.after.py} (100%) rename python/testData/refactoring/extractmethod/{comment.before.py => Comment.before.py} (100%) rename python/testData/refactoring/extractmethod/{comment2.after.py => Comment2.after.py} (100%) rename python/testData/refactoring/extractmethod/{comment2.before.py => Comment2.before.py} (100%) rename python/testData/refactoring/extractmethod/{conditionalreturn.before.py => ConditionalReturn.before.py} (100%) rename python/testData/refactoring/extractmethod/{elsebody.after.py => ElseBody.after.py} (100%) rename python/testData/refactoring/extractmethod/{elsebody.before.py => ElseBody.before.py} (100%) create mode 100644 python/testData/refactoring/extractmethod/Expression.after.py create mode 100644 python/testData/refactoring/extractmethod/Expression.before.py rename python/testData/refactoring/extractmethod/{file.after.py => File.after.py} (100%) rename python/testData/refactoring/extractmethod/{file.before.py => File.before.py} (100%) create mode 100644 python/testData/refactoring/extractmethod/MethodContext.after.py create mode 100644 python/testData/refactoring/extractmethod/MethodContext.before.py rename python/testData/refactoring/extractmethod/{methodInIf.after.py => MethodInIf.after.py} (100%) rename python/testData/refactoring/extractmethod/{methodInIf.before.py => MethodInIf.before.py} (100%) create mode 100644 python/testData/refactoring/extractmethod/MethodIndent.after.py create mode 100644 python/testData/refactoring/extractmethod/MethodIndent.before.py create mode 100644 python/testData/refactoring/extractmethod/MethodReturn.after.py create mode 100644 python/testData/refactoring/extractmethod/MethodReturn.before.py create mode 100644 python/testData/refactoring/extractmethod/NameCollisionClass.before.py create mode 100644 python/testData/refactoring/extractmethod/NameCollisionFile.before.py create mode 100644 python/testData/refactoring/extractmethod/NameCollisionSuperClass.before.py create mode 100644 python/testData/refactoring/extractmethod/OutNotEmptyStatements.after.py create mode 100644 python/testData/refactoring/extractmethod/OutNotEmptyStatements.before.py create mode 100644 python/testData/refactoring/extractmethod/OutNotEmptyStatements2.after.py create mode 100644 python/testData/refactoring/extractmethod/OutNotEmptyStatements2.before.py create mode 100644 python/testData/refactoring/extractmethod/Parameter.after.py create mode 100644 python/testData/refactoring/extractmethod/Parameter.before.py create mode 100644 python/testData/refactoring/extractmethod/Py479.after.py create mode 100644 python/testData/refactoring/extractmethod/Py479.before.py rename python/testData/refactoring/extractmethod/{return_tuple.after.py => ReturnTuple.after.py} (100%) rename python/testData/refactoring/extractmethod/{return_tuple.before.py => ReturnTuple.before.py} (100%) create mode 100644 python/testData/refactoring/extractmethod/Statement.after.py create mode 100644 python/testData/refactoring/extractmethod/Statement.before.py create mode 100644 python/testData/refactoring/extractmethod/StatementReturn.after.py create mode 100644 python/testData/refactoring/extractmethod/StatementReturn.before.py create mode 100644 python/testData/refactoring/extractmethod/Statements.after.py create mode 100644 python/testData/refactoring/extractmethod/Statements.before.py rename python/testData/refactoring/extractmethod/{staticmethod.after.py => StaticMethod.after.py} (100%) rename python/testData/refactoring/extractmethod/{staticmethod.before.py => StaticMethod.before.py} (100%) create mode 100644 python/testData/refactoring/extractmethod/WhileOutput.after.py create mode 100644 python/testData/refactoring/extractmethod/WhileOutput.before.py create mode 100644 python/testData/refactoring/extractmethod/WrongSelectionFromImportStar.before.py create mode 100644 python/testData/refactoring/extractmethod/WrongSelectionIfPart.before.py diff --git a/python/testData/refactoring/extractmethod/BinaryExpression.after.py b/python/testData/refactoring/extractmethod/BinaryExpression.after.py new file mode 100644 index 000000000000..97e855544d46 --- /dev/null +++ b/python/testData/refactoring/extractmethod/BinaryExpression.after.py @@ -0,0 +1,4 @@ +def foo(): + return bbb + +aaa = foo() && ccc \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/BinaryExpression.before.py b/python/testData/refactoring/extractmethod/BinaryExpression.before.py new file mode 100644 index 000000000000..714e824993dd --- /dev/null +++ b/python/testData/refactoring/extractmethod/BinaryExpression.before.py @@ -0,0 +1 @@ +aaa = bbb && ccc \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/BreakAst.after.py b/python/testData/refactoring/extractmethod/BreakAst.after.py new file mode 100644 index 000000000000..bf980f363971 --- /dev/null +++ b/python/testData/refactoring/extractmethod/BreakAst.after.py @@ -0,0 +1,6 @@ +def bar(r_new): + return PI * r_new ** 2 + + +def cylinder_volume(r, h): + h * bar(r) diff --git a/python/testData/refactoring/extractmethod/BreakAst.before.py b/python/testData/refactoring/extractmethod/BreakAst.before.py new file mode 100644 index 000000000000..0abd37a690d9 --- /dev/null +++ b/python/testData/refactoring/extractmethod/BreakAst.before.py @@ -0,0 +1,2 @@ +def cylinder_volume(r, h): + h * PI * r ** 2 diff --git a/python/testData/refactoring/extractmethod/ClassContext.after.py b/python/testData/refactoring/extractmethod/ClassContext.after.py new file mode 100644 index 000000000000..d8d43cddfcb9 --- /dev/null +++ b/python/testData/refactoring/extractmethod/ClassContext.after.py @@ -0,0 +1,5 @@ +class PyCharm: + def bar(): + print("Hello Pycharm!") + + bar() \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/ClassContext.before.py b/python/testData/refactoring/extractmethod/ClassContext.before.py new file mode 100644 index 000000000000..b33d3dea282a --- /dev/null +++ b/python/testData/refactoring/extractmethod/ClassContext.before.py @@ -0,0 +1,2 @@ +class PyCharm: + print("Hello Pycharm!") \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/classmethod.after.py b/python/testData/refactoring/extractmethod/ClassMethod.after.py similarity index 100% rename from python/testData/refactoring/extractmethod/classmethod.after.py rename to python/testData/refactoring/extractmethod/ClassMethod.after.py diff --git a/python/testData/refactoring/extractmethod/classmethod.before.py b/python/testData/refactoring/extractmethod/ClassMethod.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/classmethod.before.py rename to python/testData/refactoring/extractmethod/ClassMethod.before.py diff --git a/python/testData/refactoring/extractmethod/comment.after.py b/python/testData/refactoring/extractmethod/Comment.after.py similarity index 100% rename from python/testData/refactoring/extractmethod/comment.after.py rename to python/testData/refactoring/extractmethod/Comment.after.py diff --git a/python/testData/refactoring/extractmethod/comment.before.py b/python/testData/refactoring/extractmethod/Comment.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/comment.before.py rename to python/testData/refactoring/extractmethod/Comment.before.py diff --git a/python/testData/refactoring/extractmethod/comment2.after.py b/python/testData/refactoring/extractmethod/Comment2.after.py similarity index 100% rename from python/testData/refactoring/extractmethod/comment2.after.py rename to python/testData/refactoring/extractmethod/Comment2.after.py diff --git a/python/testData/refactoring/extractmethod/comment2.before.py b/python/testData/refactoring/extractmethod/Comment2.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/comment2.before.py rename to python/testData/refactoring/extractmethod/Comment2.before.py diff --git a/python/testData/refactoring/extractmethod/conditionalreturn.before.py b/python/testData/refactoring/extractmethod/ConditionalReturn.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/conditionalreturn.before.py rename to python/testData/refactoring/extractmethod/ConditionalReturn.before.py diff --git a/python/testData/refactoring/extractmethod/elsebody.after.py b/python/testData/refactoring/extractmethod/ElseBody.after.py similarity index 100% rename from python/testData/refactoring/extractmethod/elsebody.after.py rename to python/testData/refactoring/extractmethod/ElseBody.after.py diff --git a/python/testData/refactoring/extractmethod/elsebody.before.py b/python/testData/refactoring/extractmethod/ElseBody.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/elsebody.before.py rename to python/testData/refactoring/extractmethod/ElseBody.before.py diff --git a/python/testData/refactoring/extractmethod/Expression.after.py b/python/testData/refactoring/extractmethod/Expression.after.py new file mode 100644 index 000000000000..5ef6f9653efb --- /dev/null +++ b/python/testData/refactoring/extractmethod/Expression.after.py @@ -0,0 +1,8 @@ +def plus(a_new, b_new): + return a_new + b_new * 123 + + +def f(): + a = 1 + b = 1 + c = plus(a, b) \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Expression.before.py b/python/testData/refactoring/extractmethod/Expression.before.py new file mode 100644 index 000000000000..58e1958353a2 --- /dev/null +++ b/python/testData/refactoring/extractmethod/Expression.before.py @@ -0,0 +1,4 @@ +def f(): + a = 1 + b = 1 + c = a + b * 123 \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/file.after.py b/python/testData/refactoring/extractmethod/File.after.py similarity index 100% rename from python/testData/refactoring/extractmethod/file.after.py rename to python/testData/refactoring/extractmethod/File.after.py diff --git a/python/testData/refactoring/extractmethod/file.before.py b/python/testData/refactoring/extractmethod/File.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/file.before.py rename to python/testData/refactoring/extractmethod/File.before.py diff --git a/python/testData/refactoring/extractmethod/MethodContext.after.py b/python/testData/refactoring/extractmethod/MethodContext.after.py new file mode 100644 index 000000000000..894730b4a9e4 --- /dev/null +++ b/python/testData/refactoring/extractmethod/MethodContext.after.py @@ -0,0 +1,7 @@ +class C: + def bar(self): + for x in [1, 2]: + print x + + def foo(self): + self.bar() \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/MethodContext.before.py b/python/testData/refactoring/extractmethod/MethodContext.before.py new file mode 100644 index 000000000000..3fbab10fdfa0 --- /dev/null +++ b/python/testData/refactoring/extractmethod/MethodContext.before.py @@ -0,0 +1,4 @@ +class C: + def foo(self): + for x in [1, 2]: + print x \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/methodInIf.after.py b/python/testData/refactoring/extractmethod/MethodInIf.after.py similarity index 100% rename from python/testData/refactoring/extractmethod/methodInIf.after.py rename to python/testData/refactoring/extractmethod/MethodInIf.after.py diff --git a/python/testData/refactoring/extractmethod/methodInIf.before.py b/python/testData/refactoring/extractmethod/MethodInIf.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/methodInIf.before.py rename to python/testData/refactoring/extractmethod/MethodInIf.before.py diff --git a/python/testData/refactoring/extractmethod/MethodIndent.after.py b/python/testData/refactoring/extractmethod/MethodIndent.after.py new file mode 100644 index 000000000000..f725cb8f8982 --- /dev/null +++ b/python/testData/refactoring/extractmethod/MethodIndent.after.py @@ -0,0 +1,8 @@ +class Foo(X, Y, Z): + def bar(self, base_new): + try: base_new.__init__(self) + except AttributeError: pass + + def __init__(self): + for base in self__class__.__bases__: + self.bar(base) diff --git a/python/testData/refactoring/extractmethod/MethodIndent.before.py b/python/testData/refactoring/extractmethod/MethodIndent.before.py new file mode 100644 index 000000000000..43ae5769b400 --- /dev/null +++ b/python/testData/refactoring/extractmethod/MethodIndent.before.py @@ -0,0 +1,5 @@ +class Foo(X, Y, Z): + def __init__(self): + for base in self__class__.__bases__: + try: base.__init__(self) + except AttributeError: pass diff --git a/python/testData/refactoring/extractmethod/MethodReturn.after.py b/python/testData/refactoring/extractmethod/MethodReturn.after.py new file mode 100644 index 000000000000..c284021950fd --- /dev/null +++ b/python/testData/refactoring/extractmethod/MethodReturn.after.py @@ -0,0 +1,9 @@ +class C: + def bar(self): + if (self.cond): + return 1 + else: + return 2 + + def foo(self): + return self.bar() \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/MethodReturn.before.py b/python/testData/refactoring/extractmethod/MethodReturn.before.py new file mode 100644 index 000000000000..70e2a62bffce --- /dev/null +++ b/python/testData/refactoring/extractmethod/MethodReturn.before.py @@ -0,0 +1,6 @@ +class C: + def foo(self): + if (self.cond): + return 1 + else: + return 2 \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/NameCollisionClass.before.py b/python/testData/refactoring/extractmethod/NameCollisionClass.before.py new file mode 100644 index 000000000000..5aa02294eab8 --- /dev/null +++ b/python/testData/refactoring/extractmethod/NameCollisionClass.before.py @@ -0,0 +1,5 @@ +class A: + def hello(): + pass + + print("Hello") diff --git a/python/testData/refactoring/extractmethod/NameCollisionFile.before.py b/python/testData/refactoring/extractmethod/NameCollisionFile.before.py new file mode 100644 index 000000000000..4638816d913c --- /dev/null +++ b/python/testData/refactoring/extractmethod/NameCollisionFile.before.py @@ -0,0 +1,4 @@ +def hello(): + pass + +print("Hello") \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/NameCollisionSuperClass.before.py b/python/testData/refactoring/extractmethod/NameCollisionSuperClass.before.py new file mode 100644 index 000000000000..9f06672765ba --- /dev/null +++ b/python/testData/refactoring/extractmethod/NameCollisionSuperClass.before.py @@ -0,0 +1,7 @@ +class A: + def hello(self): + pass + +class B(A): + def foo(self): + print("Hello") diff --git a/python/testData/refactoring/extractmethod/OutNotEmptyStatements.after.py b/python/testData/refactoring/extractmethod/OutNotEmptyStatements.after.py new file mode 100644 index 000000000000..e76646160990 --- /dev/null +++ b/python/testData/refactoring/extractmethod/OutNotEmptyStatements.after.py @@ -0,0 +1,12 @@ +def sum_squares(a_new, result_new): + while a_new < 10: + result_new += a_new * a_new + a_new += 1 + return result_new + + +def f(): + a = 10 + result = 0 + result = sum_squares(a, result) + print("Sum of squares: " + result) diff --git a/python/testData/refactoring/extractmethod/OutNotEmptyStatements.before.py b/python/testData/refactoring/extractmethod/OutNotEmptyStatements.before.py new file mode 100644 index 000000000000..dbc4dda006e4 --- /dev/null +++ b/python/testData/refactoring/extractmethod/OutNotEmptyStatements.before.py @@ -0,0 +1,7 @@ +def f(): + a = 10 + result = 0 + while a < 10: + result += a * a + a += 1 + print("Sum of squares: " + result) diff --git a/python/testData/refactoring/extractmethod/OutNotEmptyStatements2.after.py b/python/testData/refactoring/extractmethod/OutNotEmptyStatements2.after.py new file mode 100644 index 000000000000..31e3e2fde502 --- /dev/null +++ b/python/testData/refactoring/extractmethod/OutNotEmptyStatements2.after.py @@ -0,0 +1,12 @@ +def sum_squares(a_new): + result = 0 + while a_new < 10: + result += a_new * a_new + a_new += 1 + return a_new, result + + +def f(): + a = 1 + a, result = sum_squares(a) + print("Sum of squares: " + a + " = " + result) diff --git a/python/testData/refactoring/extractmethod/OutNotEmptyStatements2.before.py b/python/testData/refactoring/extractmethod/OutNotEmptyStatements2.before.py new file mode 100644 index 000000000000..8618a55f7fd5 --- /dev/null +++ b/python/testData/refactoring/extractmethod/OutNotEmptyStatements2.before.py @@ -0,0 +1,7 @@ +def f(): + a = 1 + result = 0 + while a < 10: + result += a * a + a += 1 + print("Sum of squares: " + a + " = " + result) diff --git a/python/testData/refactoring/extractmethod/Parameter.after.py b/python/testData/refactoring/extractmethod/Parameter.after.py new file mode 100644 index 000000000000..d8a11e00ef99 --- /dev/null +++ b/python/testData/refactoring/extractmethod/Parameter.after.py @@ -0,0 +1,6 @@ +def bar(a_new): + print(a_new) + + +def foo(a): + bar(a) diff --git a/python/testData/refactoring/extractmethod/Parameter.before.py b/python/testData/refactoring/extractmethod/Parameter.before.py new file mode 100644 index 000000000000..66dd68486827 --- /dev/null +++ b/python/testData/refactoring/extractmethod/Parameter.before.py @@ -0,0 +1,2 @@ +def foo(a): + print(a) diff --git a/python/testData/refactoring/extractmethod/Py479.after.py b/python/testData/refactoring/extractmethod/Py479.after.py new file mode 100644 index 000000000000..6de82ec0c85a --- /dev/null +++ b/python/testData/refactoring/extractmethod/Py479.after.py @@ -0,0 +1,25 @@ +class Foo: + def __init__(self): + self.tmp = False + + def bar(self, condition3_new, condition4_new): + self.tmp2 = True + if condition3_new: + print(condition3_new) + if condition4_new: + print(condition4_new) + print("misterious extract method test") + + def extract_method(self, condition1, condition2, condition3, condition4): + list = (1, 2, 3) + a = 6 + b = False + if a in list or self.tmp: + if condition1: + print(condition1) + if b is not condition2: + print(b) + else: + self.bar(condition3, condition4) +f = Foo() +f.extract_method(True, True, True, True) \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Py479.before.py b/python/testData/refactoring/extractmethod/Py479.before.py new file mode 100644 index 000000000000..33abf490a9f1 --- /dev/null +++ b/python/testData/refactoring/extractmethod/Py479.before.py @@ -0,0 +1,22 @@ +class Foo: + def __init__(self): + self.tmp = False + + def extract_method(self, condition1, condition2, condition3, condition4): + list = (1, 2, 3) + a = 6 + b = False + if a in list or self.tmp: + if condition1: + print(condition1) + if b is not condition2: + print(b) + else: + self.tmp2 = True + if condition3: + print(condition3) + if condition4: + print(condition4) + print("misterious extract method test") +f = Foo() +f.extract_method(True, True, True, True) \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/return_tuple.after.py b/python/testData/refactoring/extractmethod/ReturnTuple.after.py similarity index 100% rename from python/testData/refactoring/extractmethod/return_tuple.after.py rename to python/testData/refactoring/extractmethod/ReturnTuple.after.py diff --git a/python/testData/refactoring/extractmethod/return_tuple.before.py b/python/testData/refactoring/extractmethod/ReturnTuple.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/return_tuple.before.py rename to python/testData/refactoring/extractmethod/ReturnTuple.before.py diff --git a/python/testData/refactoring/extractmethod/Statement.after.py b/python/testData/refactoring/extractmethod/Statement.after.py new file mode 100644 index 000000000000..b8cd0525969d --- /dev/null +++ b/python/testData/refactoring/extractmethod/Statement.after.py @@ -0,0 +1,8 @@ +def foo(a_new, b_new): + print(a_new + b_new * 123) + + +def f(): + a = 1 + b = 1 + foo(a, b) \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Statement.before.py b/python/testData/refactoring/extractmethod/Statement.before.py new file mode 100644 index 000000000000..32c0393c2407 --- /dev/null +++ b/python/testData/refactoring/extractmethod/Statement.before.py @@ -0,0 +1,4 @@ +def f(): + a = 1 + b = 1 + print(a + b * 123) \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/StatementReturn.after.py b/python/testData/refactoring/extractmethod/StatementReturn.after.py new file mode 100644 index 000000000000..61694ca944de --- /dev/null +++ b/python/testData/refactoring/extractmethod/StatementReturn.after.py @@ -0,0 +1,10 @@ +def foo(): + if foo(): + return -1 + else: + return 1 + +return foo() + + +bar \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/StatementReturn.before.py b/python/testData/refactoring/extractmethod/StatementReturn.before.py new file mode 100644 index 000000000000..148938b0d851 --- /dev/null +++ b/python/testData/refactoring/extractmethod/StatementReturn.before.py @@ -0,0 +1,8 @@ + +if foo(): + return -1 +else: + return 1 + + +bar \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Statements.after.py b/python/testData/refactoring/extractmethod/Statements.after.py new file mode 100644 index 000000000000..70aa5c212096 --- /dev/null +++ b/python/testData/refactoring/extractmethod/Statements.after.py @@ -0,0 +1,10 @@ +def foo(a_new, b_new): + puts(a_new + b_new * 123) + print("Hello from extract method") + + +def f(): + a = 1 + b = 1 + foo(a, b) + diff --git a/python/testData/refactoring/extractmethod/Statements.before.py b/python/testData/refactoring/extractmethod/Statements.before.py new file mode 100644 index 000000000000..c8e804887c89 --- /dev/null +++ b/python/testData/refactoring/extractmethod/Statements.before.py @@ -0,0 +1,6 @@ +def f(): + a = 1 + b = 1 + puts(a + b * 123) + print("Hello from extract method") + diff --git a/python/testData/refactoring/extractmethod/staticmethod.after.py b/python/testData/refactoring/extractmethod/StaticMethod.after.py similarity index 100% rename from python/testData/refactoring/extractmethod/staticmethod.after.py rename to python/testData/refactoring/extractmethod/StaticMethod.after.py diff --git a/python/testData/refactoring/extractmethod/staticmethod.before.py b/python/testData/refactoring/extractmethod/StaticMethod.before.py similarity index 100% rename from python/testData/refactoring/extractmethod/staticmethod.before.py rename to python/testData/refactoring/extractmethod/StaticMethod.before.py diff --git a/python/testData/refactoring/extractmethod/WhileOutput.after.py b/python/testData/refactoring/extractmethod/WhileOutput.after.py new file mode 100644 index 000000000000..502f8d2ee25b --- /dev/null +++ b/python/testData/refactoring/extractmethod/WhileOutput.after.py @@ -0,0 +1,11 @@ +def bar(a_new): + do_smth + a_new += 1 + return a_new + + +def f(): + a = 1 + while a < 10: + a = bar(a) + print(a) diff --git a/python/testData/refactoring/extractmethod/WhileOutput.before.py b/python/testData/refactoring/extractmethod/WhileOutput.before.py new file mode 100644 index 000000000000..7dc2ed5893d1 --- /dev/null +++ b/python/testData/refactoring/extractmethod/WhileOutput.before.py @@ -0,0 +1,6 @@ +def f(): + a = 1 + while a < 10: + do_smth + a+=1 + print(a) diff --git a/python/testData/refactoring/extractmethod/WrongSelectionFromImportStar.before.py b/python/testData/refactoring/extractmethod/WrongSelectionFromImportStar.before.py new file mode 100644 index 000000000000..a1f3184f5249 --- /dev/null +++ b/python/testData/refactoring/extractmethod/WrongSelectionFromImportStar.before.py @@ -0,0 +1 @@ +from mymodule import * diff --git a/python/testData/refactoring/extractmethod/WrongSelectionIfPart.before.py b/python/testData/refactoring/extractmethod/WrongSelectionIfPart.before.py new file mode 100644 index 000000000000..9274a2c1a07b --- /dev/null +++ b/python/testData/refactoring/extractmethod/WrongSelectionIfPart.before.py @@ -0,0 +1,13 @@ +class Settings(patterns.Observer, UnicodeAwareConfigParser): + def __init__(self): + pass + def set(self, section, option, value, new=False): + if new: + currentValue = 'a new option, so use something as current value'\ + ' that is unlikely to be equal to the new value' + else: + currentValue = self.get(section, option) + if value != currentValue: + patterns.Event('before.%s.%s'%(section, option), self, value).send() + super(Settings, self).set(section, option, value) + patterns.Event('%s.%s'%(section, option), self, value).send() diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java index 84b1b40d3222..a2d9dc970a85 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -1,6 +1,8 @@ package com.jetbrains.python.refactoring; import com.intellij.lang.LanguageRefactoringSupport; +import com.intellij.lang.refactoring.RefactoringSupportProvider; +import com.intellij.openapi.editor.Editor; import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.refactoring.RefactoringActionHandler; import com.jetbrains.python.PythonLanguage; @@ -11,152 +13,156 @@ import com.jetbrains.python.refactoring.extractmethod.PyExtractMethodUtil; * @author oleg */ public class PyExtractMethodTest extends LightMarkedTestCase { - private void doTest(final String testPath, - final String name, - final String result) { - - myFixture.configureByFile("/refactoring/extractmethod/" + testPath); - final RefactoringActionHandler handler = LanguageRefactoringSupport.INSTANCE.forLanguage(PythonLanguage.getInstance()).getExtractMethodHandler(); - try { - System.setProperty(PyExtractMethodUtil.NAME, name); - try { - handler.invoke(myFixture.getProject(), myFixture.getEditor(), myFixture.getFile(), ((EditorEx) myFixture.getEditor()).getDataContext()); - } - catch (Exception e) { - if (result.endsWith(".py")) { // expected output file, not an exception - e.printStackTrace(); - } - assertEquals(result, e.getMessage()); - return; - } - } finally { - System.clearProperty(PyExtractMethodUtil.NAME); - } - myFixture.checkResultByFile("/refactoring/extractmethod/" + result); - } - private void doTest(String newName) { final String testName = getTestName(false); - doTest(testName + ".before.py", newName, testName + ".after.py"); + final String beforeName = testName + ".before.py"; + final String afterName = testName + ".after.py"; + final String dir = "refactoring/extractmethod/"; + + myFixture.configureByFile(dir + beforeName); + final RefactoringSupportProvider provider = LanguageRefactoringSupport.INSTANCE.forLanguage(PythonLanguage.getInstance()); + assertNotNull(provider); + final RefactoringActionHandler handler = provider.getExtractMethodHandler(); + assertNotNull(handler); + final Editor editor = myFixture.getEditor(); + assertInstanceOf(editor, EditorEx.class); + System.setProperty(PyExtractMethodUtil.NAME, newName); + try { + handler.invoke(myFixture.getProject(), editor, myFixture.getFile(), ((EditorEx)editor).getDataContext()); + } + finally { + System.clearProperty(PyExtractMethodUtil.NAME); + } + myFixture.checkResultByFile(dir + afterName); + } + + private void doFail(String newName, String message) { + try { + doTest(newName); + } + catch (Exception e) { + assertEquals(message, e.getMessage()); + return; + } + fail("No exception was thrown"); } public void testParameter() { - doTest("outEmpty/parameter.before.py", "bar", "outEmpty/parameter.after.py"); + doTest("bar"); } public void testBreakAst() { - doTest("outEmpty/break_ast.before.py", "bar", "outEmpty/break_ast.after.py"); + doTest("bar"); } public void testExpression() { - doTest("outEmpty/expression.before.py", "plus", "outEmpty/expression.after.py"); + doTest("plus"); } public void testStatement() { - doTest("outEmpty/statement.before.py", "foo", "outEmpty/statement.after.py"); + doTest("foo"); } public void testStatements() { - doTest("outEmpty/statements.before.py", "foo", "outEmpty/statements.after.py"); + doTest("foo"); } public void testStatementReturn() { - doTest("outEmpty/statement_return.before.py", "foo", "outEmpty/statement_return.after.py"); + doTest("foo"); } public void testBinaryExpression() { - doTest("controlFlow/binary_expr.before.py", "foo", "controlFlow/binary_expr.after.py"); + doTest("foo"); } public void testWhileOutput() { - doTest("controlFlow/while_output.before.py", "bar", "controlFlow/while_output.after.py"); + doTest("bar"); } public void testNameCollisionClass() { - doTest("namesCollision/class.before.py", "hello", "Method name clashes with already existing method name"); + doFail("hello", "Method name clashes with already existing method name"); } public void testNameCollisionFile() { - doTest("namesCollision/file.before.py", "hello", "Method name clashes with already existing method name"); + doFail("hello", "Method name clashes with already existing method name"); } public void testNameCollisionSuperClass() { - doTest("namesCollision/superclass.before.py", "hello", "Method name clashes with already existing method name"); + doFail("hello", "Method name clashes with already existing method name"); } public void testOutNotEmptyStatements() { - doTest("outNotEmpty/statements.before.py", "sum_squares", "outNotEmpty/statements.after.py"); + doTest("sum_squares"); } public void testOutNotEmptyStatements2() { - doTest("outNotEmpty/statements2.before.py", "sum_squares", "outNotEmpty/statements2.after.py"); + doTest("sum_squares"); } // PY-2903 public void _testComment() { - doTest("comment.before.py", "bar", "comment.after.py"); + doTest("bar"); } public void testFile() { - doTest("file.before.py", "bar", "file.after.py"); + doTest("bar"); } - - public void testMethod() { - doTest("context/method.before.py", "bar", "context/method.after.py"); + public void testMethodContext() { + doTest("bar"); } public void testMethodIndent() { - doTest("context/methodindent.before.py", "bar", "context/methodindent.after.py"); + doTest("bar"); } public void testMethodReturn() { - doTest("context/methodreturn.before.py", "bar", "context/methodreturn.after.py"); + doTest("bar"); } public void testWrongSelectionIfPart() { - doTest("wrongSelection/ifpart.before.py", "bar", "Cannot perform extract method using selected element(s)"); + doFail("bar", "Cannot perform extract method using selected element(s)"); } - public void testFromImportStatement() { - doTest("wrongSelection/fromimport.before.py", "bar", "Cannot perform refactoring with from import statement inside code block"); + public void testWrongSelectionFromImportStar() { + doFail("bar", "Cannot perform refactoring with from import statement inside code block"); } public void testPy479() { - doTest("outEmpty/py479.before.py", "bar", "outEmpty/py479.after.py"); + doTest("bar"); } - public void testClass() { - doTest("context/class.before.py", "bar", "context/class.after.py"); + public void testClassContext() { + doTest("bar"); } public void testConditionalReturn() { - doTest("conditionalreturn.before.py", "bar", "Cannot perform refactoring when execution flow is interrupted"); + doFail("bar", "Cannot perform refactoring when execution flow is interrupted"); } public void testReturnTuple() { - doTest("return_tuple.before.py", "bar", "return_tuple.after.py"); + doTest("bar"); } public void testComment2() { - doTest("comment2.before.py", "baz", "comment2.after.py"); + doTest("baz"); } public void testElseBody() { - doTest("elsebody.before.py", "baz", "elsebody.after.py"); + doTest("baz"); } public void testClassMethod() { - doTest("classmethod.before.py", "baz", "classmethod.after.py"); + doTest("baz"); } public void testStaticMethod() { - doTest("staticmethod.before.py", "baz", "staticmethod.after.py"); + doTest("baz"); } // PY-5123 public void testMethodInIf() { - doTest("methodInIf.before.py", "baz", "methodInIf.after.py"); + doTest("baz"); } // PY-6081 From aa5d3f07baebcb6ff9db346d04e2043a56b4c1e8 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 23 May 2012 17:26:46 +0400 Subject: [PATCH 2/6] Fixed detection of loop parts with continue and break in extract method refactoring (PY-6623) --- .../codeFragment/PyCodeFragmentUtil.java | 25 ++++++++++++------- python/testData/__init__.py | 1 - .../extractmethod/ForLoopContinue.before.py | 4 +++ .../refactoring/PyExtractMethodTest.java | 5 ++++ 4 files changed, 25 insertions(+), 10 deletions(-) delete mode 100644 python/testData/__init__.py create mode 100644 python/testData/refactoring/extractmethod/ForLoopContinue.before.py diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java index dc9b93666abd..9737cef22b23 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java @@ -42,11 +42,9 @@ public class PyCodeFragmentUtil { final List graph = Arrays.asList(flow.getInstructions()); final List subGraph = getFragmentSubGraph(graph, start, end); final AnalysisResult subGraphAnalysis = analyseSubGraph(subGraph, start, end); - if (subGraphAnalysis.regularExits > 0 && subGraphAnalysis.returns > 0) { - throw new CannotCreateCodeFragmentException( - PyBundle.message("refactoring.extract.method.error.cannot.perform.refactoring.when.execution.flow.is.interrupted")); - } - if (subGraphAnalysis.targetInstructions > 1) { + if ((subGraphAnalysis.regularExits > 0 && subGraphAnalysis.returns > 0) || + subGraphAnalysis.targetInstructions > 1 || + subGraphAnalysis.outerLoopBreaks > 0) { throw new CannotCreateCodeFragmentException( PyBundle.message("refactoring.extract.method.error.cannot.perform.refactoring.when.execution.flow.is.interrupted")); } @@ -135,12 +133,14 @@ public class PyCodeFragmentUtil { private final int targetInstructions; private final int regularExits; private final int returns; + private final int outerLoopBreaks; - public AnalysisResult(int starImports, int targetInstructions, int returns, int regularExits) { + public AnalysisResult(int starImports, int targetInstructions, int returns, int regularExits, int outerLoopBreaks) { this.starImports = starImports; this.targetInstructions = targetInstructions; this.regularExits = regularExits; this.returns = returns; + this.outerLoopBreaks = outerLoopBreaks; } } @@ -150,6 +150,7 @@ public class PyCodeFragmentUtil { int regularSources = 0; final Set targetInstructions = new HashSet(); int starImports = 0; + int outerLoopBreaks = 0; for (Pair edge : getOutgoingEdges(subGraph)) { final Instruction sourceInstruction = edge.getFirst(); @@ -173,17 +174,23 @@ public class PyCodeFragmentUtil { } } - for (Instruction instruction : subGraph) { - final PsiElement element = instruction.getElement(); + final Set subGraphElements = getSubGraphElements(subGraph); + for (PsiElement element : subGraphElements) { if (element instanceof PyFromImportStatement) { final PyFromImportStatement fromImportStatement = (PyFromImportStatement)element; if (fromImportStatement.getStarImportElement() != null) { starImports++; } } + if (element instanceof PyContinueStatement || element instanceof PyBreakStatement) { + final PyLoopStatement loopStatement = PsiTreeUtil.getParentOfType(element, PyLoopStatement.class); + if (loopStatement != null && !subGraphElements.contains(loopStatement)) { + outerLoopBreaks++; + } + } } - return new AnalysisResult(starImports, targetInstructions.size(), returnSources, regularSources); + return new AnalysisResult(starImports, targetInstructions.size(), returnSources, regularSources, outerLoopBreaks); } @NotNull diff --git a/python/testData/__init__.py b/python/testData/__init__.py deleted file mode 100644 index e6b28fd8607e..000000000000 --- a/python/testData/__init__.py +++ /dev/null @@ -1 +0,0 @@ -__author__ = 'user' diff --git a/python/testData/refactoring/extractmethod/ForLoopContinue.before.py b/python/testData/refactoring/extractmethod/ForLoopContinue.before.py new file mode 100644 index 000000000000..fe54f22473a9 --- /dev/null +++ b/python/testData/refactoring/extractmethod/ForLoopContinue.before.py @@ -0,0 +1,4 @@ +def foo(xs): + for x in xs: + if not x: + continue diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java index a2d9dc970a85..c110fafd6a18 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -209,4 +209,9 @@ public class PyExtractMethodTest extends LightMarkedTestCase { public void testGlobalToplevelAssignment() { doTest("bar"); } + + // PY-6623 + public void testForLoopContinue() { + doFail("bar", "Cannot perform refactoring when execution flow is interrupted"); + } } From f3dd3841c9084e9437311c0a5305942bc55913b2 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 23 May 2012 18:25:55 +0400 Subject: [PATCH 3/6] Fixed extracting fragments with inner classes without constructors (PY-6622) --- .../codeInsight/codeFragment/PyCodeFragmentUtil.java | 5 +++++ .../extractmethod/ClassWithoutInit.after.py | 11 +++++++++++ .../extractmethod/ClassWithoutInit.before.py | 5 +++++ .../python/refactoring/PyExtractMethodTest.java | 5 +++++ 4 files changed, 26 insertions(+) create mode 100644 python/testData/refactoring/extractmethod/ClassWithoutInit.after.py create mode 100644 python/testData/refactoring/extractmethod/ClassWithoutInit.before.py diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java index 9737cef22b23..7e0b3e6d5244 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java @@ -317,6 +317,11 @@ public class PyCodeFragmentUtil { resolved.add(element); } } + for (PsiElement element : resolved) { + if (element instanceof PyClass) { + return Collections.singletonList(element); + } + } return resolved; } final PsiElement element = reference.resolve(); diff --git a/python/testData/refactoring/extractmethod/ClassWithoutInit.after.py b/python/testData/refactoring/extractmethod/ClassWithoutInit.after.py new file mode 100644 index 000000000000..95009f8d323e --- /dev/null +++ b/python/testData/refactoring/extractmethod/ClassWithoutInit.after.py @@ -0,0 +1,11 @@ +def bar(): + class C(object): + pass + + c = C() + return c + + +def foo(): + c = bar() + return c \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/ClassWithoutInit.before.py b/python/testData/refactoring/extractmethod/ClassWithoutInit.before.py new file mode 100644 index 000000000000..81f4603ae070 --- /dev/null +++ b/python/testData/refactoring/extractmethod/ClassWithoutInit.before.py @@ -0,0 +1,5 @@ +def foo(): + class C(object): + pass + c = C() + return c \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java index c110fafd6a18..dcd9ae850589 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -214,4 +214,9 @@ public class PyExtractMethodTest extends LightMarkedTestCase { public void testForLoopContinue() { doFail("bar", "Cannot perform refactoring when execution flow is interrupted"); } + + // PY-6622 + public void testClassWithoutInit() { + doTest("bar"); + } } From 9544579b0ea2db7ced903fdade5d39d5ae60615b Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 23 May 2012 19:03:22 +0400 Subject: [PATCH 4/6] Fixed function scopes in name clash detection in extract method (PY-6626) --- .../extractmethod/PyExtractMethodUtil.java | 28 ++++++++++++++++--- 1 file changed, 24 insertions(+), 4 deletions(-) diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index 5dc6808e3baf..beb9f09177bb 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -34,6 +34,10 @@ import com.jetbrains.python.PyBundle; import com.jetbrains.python.PyNames; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.codeInsight.codeFragment.PyCodeFragment; +import com.jetbrains.python.codeInsight.controlflow.ControlFlowCache; +import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.codeInsight.dataflow.scope.Scope; +import com.jetbrains.python.codeInsight.dataflow.scope.ScopeUtil; import com.jetbrains.python.psi.*; import com.jetbrains.python.psi.impl.PyFunctionBuilder; import com.jetbrains.python.psi.impl.PyPsiUtils; @@ -523,7 +527,13 @@ public class PyExtractMethodUtil { public PyExtractMethodValidator(final PsiElement element, final Project project) { myElement = element; myProject = project; - final PsiNamedElement parent = PsiTreeUtil.getParentOfType(myElement, PyFile.class, PyClass.class); + + ScopeOwner owner = ScopeUtil.getScopeOwner(myElement); + if (owner instanceof PyFunction) { + owner = ScopeUtil.getScopeOwner(owner); + } + final ScopeOwner parent = owner; + if (parent instanceof PyFile){ final List functions = ((PyFile)parent).getTopLevelFunctions(); myFunction = new Function() { @@ -536,14 +546,24 @@ public class PyExtractMethodUtil { return true; } }; - } else - if (parent instanceof PyClass){ + } + else if (parent instanceof PyClass){ myFunction = new Function() { public Boolean fun(@NotNull final String s) { return ((PyClass) parent).findMethodByName(s, true) == null; } }; - } else { + } + else if (parent instanceof PyFunction) { + final Scope scope = ControlFlowCache.getScope(parent); + myFunction = new Function() { + @Override + public Boolean fun(String s) { + return !scope.containsDeclaration(s); + } + }; + } + else { myFunction = null; } } From f4f66b0f32b18f39fcde7c776d5bbb8558471ce9 Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 23 May 2012 19:36:51 +0400 Subject: [PATCH 5/6] Fixed unnecessary cls argument when extracting @classmethod (PY-6624) --- .../codeFragment/PyCodeFragmentUtil.java | 31 +++++++++++++++++-- .../extractmethod/ClassMethod.after.py | 4 +-- .../extractmethod/ClassMethod.before.py | 2 +- 3 files changed, 32 insertions(+), 5 deletions(-) diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java index 7e0b3e6d5244..025b3c573d87 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java @@ -59,8 +59,8 @@ public class PyCodeFragmentUtil { for (PsiElement element : filterElementsInScope(getInputElements(subGraph, graph), owner)) { final String name = getName(element); if (name != null) { - // Ignore "self", it is generated automatically when extracting any method fragment - if (PyPsiUtils.isMethodContext(element) && "self".equals(name)) { + // Ignore "self" and "cls", they are generated automatically when extracting any method fragment + if (resolvesToBoundMethodParameter(element)) { continue; } if (globalWrites.contains(name)) { @@ -82,6 +82,33 @@ public class PyCodeFragmentUtil { return new PyCodeFragment(inputNames, outputNames, globalWrites, subGraphAnalysis.returns > 0); } + private static boolean resolvesToBoundMethodParameter(@NotNull PsiElement element) { + if (PyPsiUtils.isMethodContext(element)) { + final PyFunction function = PsiTreeUtil.getParentOfType(element, PyFunction.class); + if (function != null) { + final PsiReference reference = element.getReference(); + if (reference != null) { + final PsiElement resolved = reference.resolve(); + if (resolved instanceof PyParameter) { + final PyParameterList parameterList = PsiTreeUtil.getParentOfType(resolved, PyParameterList.class); + if (parameterList != null) { + final PyParameter[] parameters = parameterList.getParameters(); + if (parameters.length > 0) { + if (resolved == parameters[0]) { + final PyFunction.Modifier modifier = function.getModifier(); + if (modifier == null || modifier == PyFunction.Modifier.CLASSMETHOD) { + return true; + } + } + } + } + } + } + } + } + return false; + } + @Nullable private static String getName(@NotNull PsiElement element) { if (element instanceof PsiNamedElement) { diff --git a/python/testData/refactoring/extractmethod/ClassMethod.after.py b/python/testData/refactoring/extractmethod/ClassMethod.after.py index b1305936b574..52040554331e 100644 --- a/python/testData/refactoring/extractmethod/ClassMethod.after.py +++ b/python/testData/refactoring/extractmethod/ClassMethod.after.py @@ -1,8 +1,8 @@ class C: @classmethod def baz(cls): - print "hello world" + print('foo', cls) @classmethod def foo(cls): - cls.baz() \ No newline at end of file + cls.baz() diff --git a/python/testData/refactoring/extractmethod/ClassMethod.before.py b/python/testData/refactoring/extractmethod/ClassMethod.before.py index 55471bc18288..79ac2f015574 100644 --- a/python/testData/refactoring/extractmethod/ClassMethod.before.py +++ b/python/testData/refactoring/extractmethod/ClassMethod.before.py @@ -1,4 +1,4 @@ class C: @classmethod def foo(cls): - print "hello world" \ No newline at end of file + print('foo', cls) From bd42933bd25e2a11d6b941e8e461623b5099040d Mon Sep 17 00:00:00 2001 From: Andrey Vlasovskikh Date: Wed, 23 May 2012 20:22:13 +0400 Subject: [PATCH 6/6] Fixed extract method for fragments with nonlocal variables (PY-6625) --- .../codeFragment/PyCodeFragment.java | 20 ++++++--- .../codeFragment/PyCodeFragmentUtil.java | 24 ++++++++-- .../extractmethod/PyExtractMethodUtil.java | 44 +++++++++++++++---- .../codefragment/importinsideuseafter.test | 11 ++--- .../codeInsight/codefragment/out.test | 11 ++--- .../GlobalToplevelAssignment.after.py | 8 ++-- .../GlobalToplevelAssignment.before.py | 3 +- .../extractmethod/Nonlocal.after.py | 13 ++++++ .../extractmethod/Nonlocal.before.py | 8 ++++ .../refactoring/PyExtractMethodTest.java | 16 +++++++ 10 files changed, 126 insertions(+), 32 deletions(-) create mode 100644 python/testData/refactoring/extractmethod/Nonlocal.after.py create mode 100644 python/testData/refactoring/extractmethod/Nonlocal.before.py diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragment.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragment.java index 7f575bab9e2b..dc01ddba6036 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragment.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragment.java @@ -8,14 +8,24 @@ import java.util.Set; * @author vlan */ public class PyCodeFragment extends CodeFragment { - private final Set myGlobals; + private final Set myGlobalWrites; + private final Set myNonlocalWrites; - public PyCodeFragment(final Set input, final Set output, final Set globals, final boolean returnInside) { + public PyCodeFragment(final Set input, + final Set output, + final Set globalWrites, + final Set nonlocalWrites, + final boolean returnInside) { super(input, output, returnInside); - myGlobals = globals; + myGlobalWrites = globalWrites; + myNonlocalWrites = nonlocalWrites; } - public Set getGlobals() { - return myGlobals; + public Set getGlobalWrites() { + return myGlobalWrites; + } + + public Set getNonlocalWrites() { + return myNonlocalWrites; } } diff --git a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java index 025b3c573d87..bf1c14952422 100644 --- a/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java +++ b/python/src/com/jetbrains/python/codeInsight/codeFragment/PyCodeFragmentUtil.java @@ -54,6 +54,7 @@ public class PyCodeFragmentUtil { } final Set globalWrites = getGlobalWrites(subGraph, owner); + final Set nonlocalWrites = getNonlocalWrites(subGraph, owner); final Set inputNames = new HashSet(); for (PsiElement element : filterElementsInScope(getInputElements(subGraph, graph), owner)) { @@ -63,7 +64,7 @@ public class PyCodeFragmentUtil { if (resolvesToBoundMethodParameter(element)) { continue; } - if (globalWrites.contains(name)) { + if (globalWrites.contains(name) || nonlocalWrites.contains(name)) { continue; } inputNames.add(name); @@ -74,12 +75,14 @@ public class PyCodeFragmentUtil { 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); - globalWrites.remove(name); } } - return new PyCodeFragment(inputNames, outputNames, globalWrites, subGraphAnalysis.returns > 0); + return new PyCodeFragment(inputNames, outputNames, globalWrites, nonlocalWrites, subGraphAnalysis.returns > 0); } private static boolean resolvesToBoundMethodParameter(@NotNull PsiElement element) { @@ -333,6 +336,21 @@ public class PyCodeFragmentUtil { return globalWrites; } + @NotNull + private static Set getNonlocalWrites(@NotNull List instructions, @NotNull ScopeOwner owner) { + final Scope scope = ControlFlowCache.getScope(owner); + final Set nonlocalWrites = new LinkedHashSet(); + for (Instruction instruction : getWriteInstructions(instructions)) { + if (instruction instanceof ReadWriteInstruction) { + final String name = ((ReadWriteInstruction)instruction).getName(); + if (scope.isNonlocal(name)) { + nonlocalWrites.add(name); + } + } + } + return nonlocalWrites; + } + @NotNull private static List multiResolve(@NotNull PsiReference reference) { if (reference instanceof PsiPolyVariantReference) { diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java index beb9f09177bb..3930a799cfa5 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodUtil.java @@ -44,10 +44,7 @@ import com.jetbrains.python.psi.impl.PyPsiUtils; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; -import java.util.Map; -import java.util.Set; +import java.util.*; /** * @author oleg @@ -105,8 +102,8 @@ public class PyExtractMethodUtil { final PsiElement firstElement = elementsRange.get(0); final boolean isMethod = PyPsiUtils.isMethodContext(firstElement); processParameters(project, generatedMethod, variableData, isMethod, isClassMethod, isStaticMethod); - processGlobalWrites(generatedMethod, fragment); + processNonlocalWrites(generatedMethod, fragment); // Generating call element final StringBuilder builder = new StringBuilder(); @@ -156,8 +153,8 @@ public class PyExtractMethodUtil { // Process parameters final boolean isMethod = PyPsiUtils.isMethodContext(elementsRange.get(0)); processParameters(project, generatedMethod, variableData, isMethod, isClassMethod, isStaticMethod); - processGlobalWrites(generatedMethod, fragment); + processNonlocalWrites(generatedMethod, fragment); // Generate call element builder.append(" = "); @@ -181,12 +178,19 @@ public class PyExtractMethodUtil { } private static void processGlobalWrites(@NotNull PyFunction function, @NotNull PyCodeFragment fragment) { - final Set globals = fragment.getGlobals(); - if (!globals.isEmpty()) { + final Set globalWrites = fragment.getGlobalWrites(); + final Set newGlobalNames = new LinkedHashSet(); + final Scope scope = ControlFlowCache.getScope(function); + for (String name : globalWrites) { + if (!scope.isGlobal(name)) { + newGlobalNames.add(name); + } + } + if (!newGlobalNames.isEmpty()) { final PyElementGenerator generator = PyElementGenerator.getInstance(function.getProject()); final PyGlobalStatement globalStatement = generator.createFromText(LanguageLevel.forElement(function), PyGlobalStatement.class, - "global " + StringUtil.join(globals, ", ")); + "global " + StringUtil.join(newGlobalNames, ", ")); final PyStatementList statementList = function.getStatementList(); if (statementList != null) { statementList.addBefore(globalStatement, statementList.getFirstChild()); @@ -194,6 +198,28 @@ public class PyExtractMethodUtil { } } + private static void processNonlocalWrites(@NotNull PyFunction function, @NotNull PyCodeFragment fragment) { + final Set nonlocalWrites = fragment.getNonlocalWrites(); + final Set newNonlocalNames = new LinkedHashSet(); + final Scope scope = ControlFlowCache.getScope(function); + for (String name : nonlocalWrites) { + if (!scope.isNonlocal(name)) { + newNonlocalNames.add(name); + } + } + if (!newNonlocalNames.isEmpty()) { + final PyElementGenerator generator = PyElementGenerator.getInstance(function.getProject()); + final PyNonlocalStatement nonlocalStatement = generator.createFromText(LanguageLevel.forElement(function), + PyNonlocalStatement.class, + "nonlocal " + StringUtil.join(newNonlocalNames, ", ")); + final PyStatementList statementList = function.getStatementList(); + if (statementList != null) { + statementList.addBefore(nonlocalStatement, statementList.getFirstChild()); + } + } + } + + private static void appendSelf(PsiElement firstElement, StringBuilder builder, boolean staticMethod) { if (staticMethod) { final PyClass containingClass = PsiTreeUtil.getParentOfType(firstElement, PyClass.class); diff --git a/python/testData/codeInsight/codefragment/importinsideuseafter.test b/python/testData/codeInsight/codefragment/importinsideuseafter.test index cd87f6ac6b46..18b1e4a31e1e 100644 --- a/python/testData/codeInsight/codefragment/importinsideuseafter.test +++ b/python/testData/codeInsight/codefragment/importinsideuseafter.test @@ -1,8 +1,9 @@ -print("start") - -import foo - -foo.bar +def foo(): + print("start") + + import foo + + foo.bar In: Out: diff --git a/python/testData/codeInsight/codefragment/out.test b/python/testData/codeInsight/codefragment/out.test index 7461a87dfe49..d5f3bd71cb6f 100644 --- a/python/testData/codeInsight/codefragment/out.test +++ b/python/testData/codeInsight/codefragment/out.test @@ -1,8 +1,9 @@ -print("start") - -aaa = 123 - -print(aaa) +def foo(): + print("start") + + aaa = 123 + + print(aaa) In: Out: diff --git a/python/testData/refactoring/extractmethod/GlobalToplevelAssignment.after.py b/python/testData/refactoring/extractmethod/GlobalToplevelAssignment.after.py index ff0ec5dd2939..55294f3fcf84 100644 --- a/python/testData/refactoring/extractmethod/GlobalToplevelAssignment.after.py +++ b/python/testData/refactoring/extractmethod/GlobalToplevelAssignment.after.py @@ -1,10 +1,10 @@ a = 2 +b = 3 def bar(): - global a - a = a + 1 + global a, c + a = a + b c = a * 2 - return c -c = bar() +bar() print(c) \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/GlobalToplevelAssignment.before.py b/python/testData/refactoring/extractmethod/GlobalToplevelAssignment.before.py index ba51aa3e9099..b392a007046e 100644 --- a/python/testData/refactoring/extractmethod/GlobalToplevelAssignment.before.py +++ b/python/testData/refactoring/extractmethod/GlobalToplevelAssignment.before.py @@ -1,4 +1,5 @@ a = 2 -a = a + 1 +b = 3 +a = a + b c = a * 2 print(c) \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Nonlocal.after.py b/python/testData/refactoring/extractmethod/Nonlocal.after.py new file mode 100644 index 000000000000..6d3f506118d5 --- /dev/null +++ b/python/testData/refactoring/extractmethod/Nonlocal.after.py @@ -0,0 +1,13 @@ +def foo(): + x = 1 + + def baz(): + nonlocal x + x = 2 + + def bar(): + nonlocal x + baz() + print(x) + bar() +foo() \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Nonlocal.before.py b/python/testData/refactoring/extractmethod/Nonlocal.before.py new file mode 100644 index 000000000000..c98f616e450f --- /dev/null +++ b/python/testData/refactoring/extractmethod/Nonlocal.before.py @@ -0,0 +1,8 @@ +def foo(): + x = 1 + def bar(): + nonlocal x + x = 2 + print(x) + bar() +foo() \ No newline at end of file diff --git a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java index dcd9ae850589..92f92bc08b22 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -7,12 +7,23 @@ import com.intellij.openapi.editor.ex.EditorEx; import com.intellij.refactoring.RefactoringActionHandler; import com.jetbrains.python.PythonLanguage; import com.jetbrains.python.fixtures.LightMarkedTestCase; +import com.jetbrains.python.psi.LanguageLevel; import com.jetbrains.python.refactoring.extractmethod.PyExtractMethodUtil; /** * @author oleg */ public class PyExtractMethodTest extends LightMarkedTestCase { + private void doTest(String newName, LanguageLevel level) { + setLanguageLevel(level); + try { + doTest(newName); + } + finally { + setLanguageLevel(null); + } + } + private void doTest(String newName) { final String testName = getTestName(false); final String beforeName = testName + ".before.py"; @@ -219,4 +230,9 @@ public class PyExtractMethodTest extends LightMarkedTestCase { public void testClassWithoutInit() { doTest("bar"); } + + // PY-6625 + public void testNonlocal() { + doTest("baz", LanguageLevel.PYTHON30); + } }