Rename: Fix rename of ambiguous import reference to class/function when some referenced declarations are not changed

#KT-6663 Fixed
(cherry picked from commit 9ba8ecd)
This commit is contained in:
Alexey Sedunov
2016-06-14 16:14:26 +03:00
parent b87d8e29e4
commit 4c447d0580
15 changed files with 141 additions and 42 deletions
+1
View File
@@ -224,6 +224,7 @@
###### Issues fixed
- [`KT-6363`](https://youtrack.jetbrains.com/issue/KT-6363) Do not rename ambiguous references in import directives
- [`KT-6663`](https://youtrack.jetbrains.com/issue/KT-6663) Fixed rename of ambiguous import reference to class/function when some referenced declarations are not changed
- [`KT-8541`](https://youtrack.jetbrains.com/issue/KT-8541), [`KT-8786`](https://youtrack.jetbrains.com/issue/KT-8786) Do now show 'Rename overloads' options if target function has no overloads
- [`KT-8544`](https://youtrack.jetbrains.com/issue/KT-8544) Show more detailed description in Rename dialog
- [`KT-8562`](https://youtrack.jetbrains.com/issue/KT-8562) Show conflicts dialog on attempt of redeclaration
@@ -36,6 +36,7 @@ import org.jetbrains.kotlin.psi.KtObjectDeclaration
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.utils.SmartList
import java.util.*
class RenameKotlinClassProcessor : RenameKotlinPsiProcessor() {
override fun canProcessElement(element: PsiElement): Boolean {
@@ -108,7 +109,19 @@ class RenameKotlinClassProcessor : RenameKotlinPsiProcessor() {
}
override fun renameElement(element: PsiElement, newName: String?, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
super.renameElement(element, newName, usages, listener)
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = com.intellij.util.SmartList<UsageInfo>()
for (usage in usages) {
if (usage.isAmbiguousImportUsage()) {
ambiguousImportUsages += usage
}
else {
simpleUsages += usage
}
}
element.ambiguousImportUsages = ambiguousImportUsages
super.renameElement(element, newName, simpleUsages.toTypedArray(), listener)
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
}
@@ -17,8 +17,9 @@
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.editor.Editor
import com.intellij.openapi.util.Key
import com.intellij.psi.*
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.search.SearchScope
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenameJavaMethodProcessor
@@ -34,12 +35,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.util.application.runReadAction
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.ImportPath
import java.util.*
class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
@@ -94,33 +91,6 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
}
}
private var PsiElement.ambiguousImportUsages: List<UsageInfo>? by UserDataProperty(Key.create("AMBIGUOUS_IMPORT_USAGES"))
override fun getPostRenameCallback(element: PsiElement, newName: String?, elementListener: RefactoringElementListener?): Runnable? {
if (newName == null) return null
return Runnable {
element.ambiguousImportUsages?.forEach {
val ref = it.reference as? PsiPolyVariantReference ?: return@forEach
if (ref.multiResolve(false).isEmpty()) {
ref.handleElementRename(newName)
}
else {
ref.element?.getStrictParentOfType<KtImportDirective>()?.let { importDirective ->
val fqName = importDirective.importedFqName!!
val newFqName = fqName.parent().child(Name.identifier(newName))
val importList = importDirective.parent as KtImportList
if (importList.imports.none { it.importedFqName == newFqName }) {
val newImportDirective = KtPsiFactory(element).createImportDirective(ImportPath(newFqName, false))
importDirective.parent.addAfter(newImportDirective, importDirective)
}
}
}
}
element.ambiguousImportUsages = null
}
}
override fun renameElement(element: PsiElement, newName: String?, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = SmartList<UsageInfo>()
@@ -130,10 +100,7 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
continue
}
val ref = usage.reference as? PsiPolyVariantReference ?: continue
val refElement = ref.element
if (refElement.parents.any { (it is KtImportDirective && !it.isAllUnder) || (it is PsiImportStaticStatement && !it.isOnDemand) }
&& ref.multiResolve(false).size > 1) {
if (usage.isAmbiguousImportUsage()) {
ambiguousImportUsages += usage
}
else {
@@ -16,18 +16,24 @@
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.openapi.util.Key
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiImportStaticStatement
import com.intellij.psi.PsiPolyVariantReference
import com.intellij.psi.PsiReference
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenamePsiElementProcessor
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.idea.core.KotlinNameSuggester
import org.jetbrains.kotlin.idea.core.quoteIfNeeded
import org.jetbrains.kotlin.psi.KtNamedDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
import org.jetbrains.kotlin.psi.KtParameter
import org.jetbrains.kotlin.psi.KtProperty
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.resolve.ImportPath
import org.jetbrains.kotlin.utils.SmartList
abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
@@ -48,4 +54,38 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
allRenames[element] = newName.quoteIfNeeded()
}
}
protected var PsiElement.ambiguousImportUsages: List<UsageInfo>? by UserDataProperty(Key.create("AMBIGUOUS_IMPORT_USAGES"))
protected fun UsageInfo.isAmbiguousImportUsage(): Boolean {
val ref = reference as? PsiPolyVariantReference ?: return false
val refElement = ref.element
return refElement.parents.any { (it is KtImportDirective && !it.isAllUnder) || (it is PsiImportStaticStatement && !it.isOnDemand) }
&& ref.multiResolve(false).size > 1
}
override fun getPostRenameCallback(element: PsiElement, newName: String?, elementListener: RefactoringElementListener?): Runnable? {
if (newName == null) return null
return Runnable {
element.ambiguousImportUsages?.forEach {
val ref = it.reference as? PsiPolyVariantReference ?: return@forEach
if (ref.multiResolve(false).isEmpty()) {
ref.handleElementRename(newName)
}
else {
ref.element?.getStrictParentOfType<KtImportDirective>()?.let { importDirective ->
val fqName = importDirective.importedFqName!!
val newFqName = fqName.parent().child(Name.identifier(newName))
val importList = importDirective.parent as KtImportList
if (importList.imports.none { it.importedFqName == newFqName }) {
val newImportDirective = KtPsiFactory(element).createImportDirective(ImportPath(newFqName, false))
importDirective.parent.addAfter(newImportDirective, importDirective)
}
}
}
}
element.ambiguousImportUsages = null
}
}
}
@@ -0,0 +1,5 @@
package lib
class Foo2
fun Foo(p: Int): Foo2 = Foo2()
@@ -0,0 +1,9 @@
package usage
import lib.Foo
import lib.Foo2
fun test() {
val v: Foo2 = Foo(1)
}
@@ -0,0 +1,6 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "lib/lib.kt",
"newName": "Foo2",
"withRuntime": "true"
}
@@ -0,0 +1,5 @@
package lib
class /*rename*/Foo
fun Foo(p: Int): Foo = Foo()
@@ -0,0 +1,8 @@
package usage
import lib.Foo
fun test() {
val v: Foo = Foo(1)
}
@@ -0,0 +1,5 @@
package lib
class Foo
fun Foo2(p: Int): Foo = Foo()
@@ -0,0 +1,9 @@
package usage
import lib.Foo
import lib.Foo2
fun test() {
val v: Foo = Foo2(1)
}
@@ -0,0 +1,6 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "lib/lib.kt",
"newName": "Foo2",
"withRuntime": "true"
}
@@ -0,0 +1,5 @@
package lib
class Foo
fun /*rename*/Foo(p: Int): Foo = Foo()
@@ -0,0 +1,8 @@
package usage
import lib.Foo
fun test() {
val v: Foo = Foo(1)
}
@@ -35,6 +35,18 @@ public class RenameTestGenerated extends AbstractRenameTest {
KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/refactoring/rename"), Pattern.compile("^(.+)\\.test$"));
}
@TestMetadata("ambiguousClassFunImportRenameClass/ambiguousClassFunImportRenameClass.test")
public void testAmbiguousClassFunImportRenameClass_AmbiguousClassFunImportRenameClass() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/ambiguousClassFunImportRenameClass.test");
doTest(fileName);
}
@TestMetadata("ambiguousClassFunImportRenameFun/ambiguousClassFunImportRenameFun.test")
public void testAmbiguousClassFunImportRenameFun_AmbiguousClassFunImportRenameFun() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/ambiguousClassFunImportRenameFun.test");
doTest(fileName);
}
@TestMetadata("automaticRenamer/simple.test")
public void testAutomaticRenamer_Simple() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/automaticRenamer/simple.test");