diff --git a/ChangeLog.md b/ChangeLog.md index af54e085bee..b8b3b5be0f0 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassProcessor.kt index c1b4abb8564..08987352971 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinClassProcessor.kt @@ -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, listener: RefactoringElementListener?) { - super.renameElement(element, newName, usages, listener) + val simpleUsages = ArrayList(usages.size) + val ambiguousImportUsages = com.intellij.util.SmartList() + 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() } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt index 3b6fc835ea8..60e4ba74d75 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinFunctionProcessor.kt @@ -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? 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()?.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, listener: RefactoringElementListener?) { val simpleUsages = ArrayList(usages.size) val ambiguousImportUsages = SmartList() @@ -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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt index d2107f381c5..11a43de30a0 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPsiProcessor.kt @@ -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? 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()?.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 + } + } } diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/after/lib/lib.kt b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/after/lib/lib.kt new file mode 100644 index 00000000000..c41a0b3c9c4 --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/after/lib/lib.kt @@ -0,0 +1,5 @@ +package lib + +class Foo2 + +fun Foo(p: Int): Foo2 = Foo2() \ No newline at end of file diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/after/usage/usage.kt b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/after/usage/usage.kt new file mode 100644 index 00000000000..0f2dbc96f9c --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/after/usage/usage.kt @@ -0,0 +1,9 @@ +package usage + +import lib.Foo +import lib.Foo2 + +fun test() { + val v: Foo2 = Foo(1) +} + diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/ambiguousClassFunImportRenameClass.test b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/ambiguousClassFunImportRenameClass.test new file mode 100644 index 00000000000..545e20846e9 --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/ambiguousClassFunImportRenameClass.test @@ -0,0 +1,6 @@ +{ + "type": "MARKED_ELEMENT", + "mainFile": "lib/lib.kt", + "newName": "Foo2", + "withRuntime": "true" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/before/lib/lib.kt b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/before/lib/lib.kt new file mode 100644 index 00000000000..9975f146dd4 --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/before/lib/lib.kt @@ -0,0 +1,5 @@ +package lib + +class /*rename*/Foo + +fun Foo(p: Int): Foo = Foo() \ No newline at end of file diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/before/usage/usage.kt b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/before/usage/usage.kt new file mode 100644 index 00000000000..d5a58ee214b --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameClass/before/usage/usage.kt @@ -0,0 +1,8 @@ +package usage + +import lib.Foo + +fun test() { + val v: Foo = Foo(1) +} + diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/after/lib/lib.kt b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/after/lib/lib.kt new file mode 100644 index 00000000000..6bce41019bd --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/after/lib/lib.kt @@ -0,0 +1,5 @@ +package lib + +class Foo + +fun Foo2(p: Int): Foo = Foo() \ No newline at end of file diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/after/usage/usage.kt b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/after/usage/usage.kt new file mode 100644 index 00000000000..6bfb36723ae --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/after/usage/usage.kt @@ -0,0 +1,9 @@ +package usage + +import lib.Foo +import lib.Foo2 + +fun test() { + val v: Foo = Foo2(1) +} + diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/ambiguousClassFunImportRenameFun.test b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/ambiguousClassFunImportRenameFun.test new file mode 100644 index 00000000000..545e20846e9 --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/ambiguousClassFunImportRenameFun.test @@ -0,0 +1,6 @@ +{ + "type": "MARKED_ELEMENT", + "mainFile": "lib/lib.kt", + "newName": "Foo2", + "withRuntime": "true" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/before/lib/lib.kt b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/before/lib/lib.kt new file mode 100644 index 00000000000..ad101a59d48 --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/before/lib/lib.kt @@ -0,0 +1,5 @@ +package lib + +class Foo + +fun /*rename*/Foo(p: Int): Foo = Foo() \ No newline at end of file diff --git a/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/before/usage/usage.kt b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/before/usage/usage.kt new file mode 100644 index 00000000000..d5a58ee214b --- /dev/null +++ b/idea/testData/refactoring/rename/ambiguousClassFunImportRenameFun/before/usage/usage.kt @@ -0,0 +1,8 @@ +package usage + +import lib.Foo + +fun test() { + val v: Foo = Foo(1) +} + diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java index 979d644b6d5..1b953bf159b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java @@ -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");