diff --git a/python/src/com/jetbrains/python/PyBundle.properties b/python/src/com/jetbrains/python/PyBundle.properties index fbd9d64acd91..2e44a53bdadf 100644 --- a/python/src/com/jetbrains/python/PyBundle.properties +++ b/python/src/com/jetbrains/python/PyBundle.properties @@ -604,6 +604,7 @@ refactoring.extract.method.error.local.variable.modifications.and.returns=Cannot refactoring.extract.method.error.empty.fragment=Cannot perform refactoring from empty code fragment refactoring.extract.method.error.undetermined.execution.flow=Cannot determine execution flow for the code fragment refactoring.extract.method.error.yield=Cannot perform refactoring with 'yield' statement inside code block +refactoring.extract.method.error.class.level=Cannot perform refactoring at class level # extract superclass diff --git a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodHandler.java b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodHandler.java index 708a27700fb6..ac92a559d076 100644 --- a/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodHandler.java +++ b/python/src/com/jetbrains/python/refactoring/extractmethod/PyExtractMethodHandler.java @@ -31,6 +31,7 @@ import com.jetbrains.python.PyBundle; import com.jetbrains.python.codeInsight.codeFragment.PyCodeFragment; import com.jetbrains.python.codeInsight.codeFragment.PyCodeFragmentUtil; import com.jetbrains.python.codeInsight.controlflow.ScopeOwner; +import com.jetbrains.python.psi.PyClass; import com.jetbrains.python.psi.PyElement; import com.jetbrains.python.psi.impl.PyPsiUtils; import com.jetbrains.python.refactoring.PyRefactoringUtil; @@ -82,6 +83,11 @@ public class PyExtractMethodHandler implements RefactoringActionHandler { RefactoringBundle.message("extract.method.title"), "refactoring.extractMethod"); return; } + if (rangeBelongsToSameClassBody(element1, element2)) { + CommonRefactoringUtil.showErrorHint(project, editor, PyBundle.message("refactoring.extract.method.error.class.level"), + RefactoringBundle.message("extract.method.title"), "refactoring.extractMethod"); + return; + } final Couple statements = getStatementsRange(element1, element2); if (statements != null) { @@ -126,6 +132,12 @@ public class PyExtractMethodHandler implements RefactoringActionHandler { RefactoringBundle.message("extract.method.title"), "refactoring.extractMethod"); } + private static boolean rangeBelongsToSameClassBody(@NotNull PsiElement element1, @NotNull PsiElement element2) { + final PyClass firstScopeOwner = PsiTreeUtil.getParentOfType(element1, PyClass.class, false, ScopeOwner.class); + final PyClass secondScopeOwner = PsiTreeUtil.getParentOfType(element2, PyClass.class, false, ScopeOwner.class); + return firstScopeOwner != null && firstScopeOwner == secondScopeOwner; + } + @Nullable private static Couple getStatementsRange(final PsiElement element1, final PsiElement element2) { final PsiElement parent = PsiTreeUtil.findCommonParent(element1, element2); diff --git a/python/testData/refactoring/extractmethod/ClassContext.after.py b/python/testData/refactoring/extractmethod/ClassContext.after.py deleted file mode 100644 index d8d43cddfcb9..000000000000 --- a/python/testData/refactoring/extractmethod/ClassContext.after.py +++ /dev/null @@ -1,5 +0,0 @@ -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 deleted file mode 100644 index b33d3dea282a..000000000000 --- a/python/testData/refactoring/extractmethod/ClassContext.before.py +++ /dev/null @@ -1,2 +0,0 @@ -class PyCharm: - print("Hello Pycharm!") \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Comment2.after.py b/python/testData/refactoring/extractmethod/Comment2.after.py deleted file mode 100644 index c5cc3a7ff224..000000000000 --- a/python/testData/refactoring/extractmethod/Comment2.after.py +++ /dev/null @@ -1,8 +0,0 @@ -class Foo(): - def baz(): - tmp = "!" # try to extract this assignment, either with or without this comment - - baz() - - def bar(self): - pass \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/Comment2.before.py b/python/testData/refactoring/extractmethod/Comment2.before.py deleted file mode 100644 index 23ba13de61f7..000000000000 --- a/python/testData/refactoring/extractmethod/Comment2.before.py +++ /dev/null @@ -1,5 +0,0 @@ -class Foo(): - tmp = "!" #try to extract this assignment, either with or without this comment - - def bar(self): - pass \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/CommentIncluded.after.py b/python/testData/refactoring/extractmethod/CommentIncluded.after.py new file mode 100644 index 000000000000..6bfc159140a0 --- /dev/null +++ b/python/testData/refactoring/extractmethod/CommentIncluded.after.py @@ -0,0 +1,8 @@ +def baz(): + tmp = "!" # try to extract this assignment, either with or without this comment + + +baz() + +def bar(self): + pass \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/CommentIncluded.before.py b/python/testData/refactoring/extractmethod/CommentIncluded.before.py new file mode 100644 index 000000000000..0b5dcef55ec4 --- /dev/null +++ b/python/testData/refactoring/extractmethod/CommentIncluded.before.py @@ -0,0 +1,4 @@ +tmp = "!" #try to extract this assignment, either with or without this comment + +def bar(self): + pass \ No newline at end of file diff --git a/python/testData/refactoring/extractmethod/NameCollisionClass.before.py b/python/testData/refactoring/extractmethod/NameCollisionClass.before.py deleted file mode 100644 index 5aa02294eab8..000000000000 --- a/python/testData/refactoring/extractmethod/NameCollisionClass.before.py +++ /dev/null @@ -1,5 +0,0 @@ -class A: - def hello(): - pass - - print("Hello") diff --git a/python/testData/refactoring/extractmethod/ProhibitedAtClassLevel.before.py b/python/testData/refactoring/extractmethod/ProhibitedAtClassLevel.before.py new file mode 100644 index 000000000000..0630f2dfcfaa --- /dev/null +++ b/python/testData/refactoring/extractmethod/ProhibitedAtClassLevel.before.py @@ -0,0 +1,2 @@ +class C(): + some_class_field = 1 \ 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 bfa342969b5f..1ebebf000efe 100644 --- a/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java +++ b/python/testSrc/com/jetbrains/python/refactoring/PyExtractMethodTest.java @@ -101,10 +101,6 @@ public class PyExtractMethodTest extends LightMarkedTestCase { doTest("bar"); } - public void testNameCollisionClass() { - doFail("hello", "Method name clashes with already existing name"); - } - public void testNameCollisionFile() { doFail("hello", "Method name clashes with already existing name"); } @@ -149,10 +145,6 @@ public class PyExtractMethodTest extends LightMarkedTestCase { doTest("bar"); } - public void testClassContext() { - doTest("bar"); - } - public void testConditionalReturn() { doFail("bar", "Cannot perform refactoring when execution flow is interrupted"); } @@ -161,7 +153,7 @@ public class PyExtractMethodTest extends LightMarkedTestCase { doTest("bar"); } - public void testComment2() { + public void testCommentIncluded() { doTest("baz"); } @@ -281,4 +273,9 @@ public class PyExtractMethodTest extends LightMarkedTestCase { public void testRedundantGlobalInTopLevelFunction() { doTest("foo"); } + + // PY-6620 + public void testProhibitedAtClassLevel() { + doFail("foo", "Cannot perform refactoring at class level"); + } }