PY-6637 Generate unique name for extracted qualifier
authorMikhail Golubev <mikhail.golubev@jetbrains.com>
Mon, 5 Oct 2015 14:34:25 +0000 (17:34 +0300)
committerMikhail Golubev <mikhail.golubev@jetbrains.com>
Mon, 5 Oct 2015 15:52:41 +0000 (18:52 +0300)
python/src/com/jetbrains/python/refactoring/makeFunctionTopLevel/PyMakeMethodTopLevelProcessor.java
python/testData/refactoring/convertTopLevel/methodCalledViaClass.after.py
python/testData/refactoring/convertTopLevel/methodMultipleAttributesConstructorQualifier.after.py
python/testData/refactoring/convertTopLevel/methodUniqueNameOfExtractedQualifier.after.py [new file with mode: 0644]
python/testData/refactoring/convertTopLevel/methodUniqueNameOfExtractedQualifier.py [new file with mode: 0644]
python/testSrc/com/jetbrains/python/refactoring/PyMakeFunctionTopLevelTest.java

index 85d86d8c32bb1a4e96f70c14898717bd5906afec..821781e255327f8907f4de497087133b789fb4b7 100644 (file)
@@ -15,6 +15,7 @@
  */
 package com.jetbrains.python.refactoring.makeFunctionTopLevel;
 
+import com.google.common.collect.Iterables;
 import com.intellij.openapi.application.ApplicationManager;
 import com.intellij.openapi.editor.Editor;
 import com.intellij.psi.PsiElement;
@@ -25,11 +26,14 @@ import com.intellij.util.Function;
 import com.intellij.util.IncorrectOperationException;
 import com.intellij.util.containers.ContainerUtil;
 import com.jetbrains.python.PyBundle;
+import com.jetbrains.python.PyNames;
 import com.jetbrains.python.codeInsight.controlflow.ScopeOwner;
 import com.jetbrains.python.codeInsight.imports.AddImportHelper;
 import com.jetbrains.python.codeInsight.imports.AddImportHelper.ImportPriority;
 import com.jetbrains.python.psi.*;
 import com.jetbrains.python.psi.resolve.QualifiedNameFinder;
+import com.jetbrains.python.refactoring.NameSuggesterUtil;
+import com.jetbrains.python.refactoring.introduce.IntroduceValidator;
 import org.jetbrains.annotations.NotNull;
 
 import java.util.*;
@@ -96,8 +100,7 @@ public class PyMakeMethodTopLevelProcessor extends PyBaseMakeFunctionTopLevelPro
           // Class().method() -> inst = Class(); method(inst.foo, inst.bar)
           else if (!newParamNames.isEmpty()) {
             final PyStatement anchor = PsiTreeUtil.getParentOfType(callExpr, PyStatement.class);
-            // TODO meaningful unique target name
-            final String targetName = "foo";
+            final String targetName = selectUniqueName(usageElem);
             final String assignmentText = targetName + " = " + instanceExpr.getText();
             final PyAssignmentStatement assignment = myGenerator.createFromText(LanguageLevel.forElement(callExpr),
                                                                                 PyAssignmentStatement.class,
@@ -129,6 +132,33 @@ public class PyMakeMethodTopLevelProcessor extends PyBaseMakeFunctionTopLevelPro
     }
   }
 
+  @NotNull
+  private String selectUniqueName(@NotNull PsiElement anchor) {
+    final PyClass pyClass = myFunction.getContainingClass();
+    assert pyClass != null;
+    final Collection<String> suggestions;
+    if (pyClass.getName() != null) {
+      suggestions = NameSuggesterUtil.generateNamesByType(pyClass.getName());
+    }
+    else {
+      suggestions = Collections.singleton("inst");
+    }
+    for (String name : suggestions) {
+      if (!(IntroduceValidator.isDefinedInScope(name, anchor) || PyNames.isReserved(name))) {
+        return name;
+      }
+    }
+    final String shortestName = Iterables.getLast(suggestions);
+    int counter = 1;
+    String name = shortestName;
+    while (IntroduceValidator.isDefinedInScope(name, anchor) || PyNames.isReserved(name)) {
+      name = shortestName + counter;
+      counter++;
+    }
+    //noinspection ConstantConditions
+    return name;
+  }
+
   private boolean resolvesToClass(PyExpression qualifier) {
     for (PsiElement element : PyUtil.multiResolveTopPriority(qualifier, myResolveContext)) {
       if (element == myFunction.getContainingClass()) {
index 44d0a52dad6c159bb444ac7204afa9c04bc0de45..82d5ce7b48430391e61d268c12a2541189da79f4 100644 (file)
@@ -6,5 +6,5 @@ def method(foo, bar, x):
     print(foo, bar, x)
 
 
-foo = C()
-method(foo.foo, foo.bar, 42)
\ No newline at end of file
+c = C()
+method(c.foo, c.bar, 42)
\ No newline at end of file
index 8bfabce04219444804431f5e7e45e81f9ba9893f..5cfeff14ebac51c2ba97cd20ad68437ea61ec31d 100644 (file)
@@ -8,5 +8,5 @@ def method(foo, bar, x):
     print(foo, bar)
 
 
-foo = C()
-method(foo.foo, foo.bar, 1)
\ No newline at end of file
+c = C()
+method(c.foo, c.bar, 1)
\ No newline at end of file
diff --git a/python/testData/refactoring/convertTopLevel/methodUniqueNameOfExtractedQualifier.after.py b/python/testData/refactoring/convertTopLevel/methodUniqueNameOfExtractedQualifier.after.py
new file mode 100644 (file)
index 0000000..9dbb95a
--- /dev/null
@@ -0,0 +1,12 @@
+class AbstractBaseResponseHandler:
+    pass
+
+
+def method(response, code):
+    if response:
+       return code
+
+
+def func(abstract_base_response_handler, a):
+    a1 = AbstractBaseResponseHandler()
+    method(a1.response, a1.code)
diff --git a/python/testData/refactoring/convertTopLevel/methodUniqueNameOfExtractedQualifier.py b/python/testData/refactoring/convertTopLevel/methodUniqueNameOfExtractedQualifier.py
new file mode 100644 (file)
index 0000000..b85b016
--- /dev/null
@@ -0,0 +1,8 @@
+class AbstractBaseResponseHandler:
+    def me<caret>thod(self):
+        if self.response:
+           return self.code
+        
+
+def func(abstract_base_response_handler, a):
+    AbstractBaseResponseHandler().method()
index 6c026f157164aeb51bf8ebb4d2b8fd21cb0f065f..835d7f29c63352ffc9eb3dd3d81a2e3cd1fb03ce 100644 (file)
@@ -189,6 +189,11 @@ public class PyMakeFunctionTopLevelTest extends PyTestCase {
     doTestSuccess();
   }
 
+
+  public void testMethodUniqueNameOfExtractedQualifier() {
+    doTestSuccess();
+  }
+
   @Override
   protected String getTestDataPath() {
     return super.getTestDataPath() + "/refactoring/convertTopLevel/";