Rename: Do not rename ambiguous references in import directives
#KT-6363 Fixed
This commit is contained in:
@@ -185,6 +185,7 @@
|
||||
- [`KT-8512`](https://youtrack.jetbrains.com/issue/KT-8512) Support "Rename tests" options in Rename dialog
|
||||
|
||||
###### Issues fixed
|
||||
- [`KT-6363`](https://youtrack.jetbrains.com/issue/KT-6363) Do not rename ambiguous references in import directives
|
||||
- [`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-8860`](https://youtrack.jetbrains.com/issue/KT-8860) Allow renaming class by constructor delegation call referencing primary constructor
|
||||
|
||||
@@ -16,12 +16,14 @@
|
||||
|
||||
package org.jetbrains.kotlin.idea.refactoring.rename
|
||||
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.refactoring.JavaRefactoringSettings
|
||||
import com.intellij.refactoring.RefactoringBundle
|
||||
import com.intellij.refactoring.rename.naming.AutomaticRenamer
|
||||
import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.annotations.TestOnly
|
||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||
@@ -30,13 +32,22 @@ import org.jetbrains.kotlin.idea.util.getAllAccessibleFunctions
|
||||
import org.jetbrains.kotlin.idea.util.getResolutionScope
|
||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.UserDataProperty
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
|
||||
class AutomaticOverloadsRenamer(function: KtNamedFunction, newName: String) : AutomaticRenamer() {
|
||||
companion object {
|
||||
@get:TestOnly
|
||||
@set:TestOnly
|
||||
internal var PsiElement.elementFilter: ((PsiElement) -> Boolean)? by UserDataProperty(Key.create("ELEMENT_FILTER"))
|
||||
}
|
||||
|
||||
init {
|
||||
val filter = function.elementFilter
|
||||
function.getOverloads().mapNotNullTo(myElements) {
|
||||
val candidate = it.source.getPsi() as? KtNamedFunction ?: return@mapNotNullTo null
|
||||
if (filter != null && !filter(candidate)) return@mapNotNullTo null
|
||||
if (candidate != function) candidate else null
|
||||
}
|
||||
suggestAllNames(function.name, newName)
|
||||
|
||||
+47
-5
@@ -17,13 +17,14 @@
|
||||
package org.jetbrains.kotlin.idea.refactoring.rename
|
||||
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.PsiReference
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||
import com.intellij.refactoring.rename.RenameJavaMethodProcessor
|
||||
import com.intellij.refactoring.rename.RenameUtil
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import com.intellij.util.SmartList
|
||||
import org.jetbrains.kotlin.asJava.KtLightElement
|
||||
import org.jetbrains.kotlin.asJava.KtLightMethod
|
||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||
@@ -33,8 +34,12 @@ 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() {
|
||||
@@ -89,18 +94,55 @@ 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>()
|
||||
for (usage in usages) {
|
||||
if (usage is LostDefaultValuesInOverridingFunctionUsageInfo) {
|
||||
usage.apply()
|
||||
continue
|
||||
}
|
||||
|
||||
simpleUsages += usage
|
||||
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) {
|
||||
ambiguousImportUsages += usage
|
||||
}
|
||||
else {
|
||||
simpleUsages += usage
|
||||
}
|
||||
}
|
||||
element.ambiguousImportUsages = ambiguousImportUsages
|
||||
|
||||
super.renameElement(element, newName, usages, listener)
|
||||
RenameUtil.doRenameGenericNamedElement(element, newName, simpleUsages.toTypedArray(), listener)
|
||||
|
||||
(element.unwrapped as? KtNamedDeclaration)?.let { dropOverrideKeywordIfNecessary(it) }
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
package lib
|
||||
|
||||
fun bar(n: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun foo(n: Int, s: String) {
|
||||
|
||||
}
|
||||
Vendored
+12
@@ -0,0 +1,12 @@
|
||||
package usage;
|
||||
|
||||
import lib.LibKt;
|
||||
|
||||
import static lib.LibKt.foo;
|
||||
|
||||
public class JUsage {
|
||||
void test() {
|
||||
LibKt.bar(1);
|
||||
foo(1, "2");
|
||||
}
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
package usage
|
||||
|
||||
import lib.foo
|
||||
import lib.bar
|
||||
|
||||
fun test() {
|
||||
bar(1)
|
||||
foo(1, "2")
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
{
|
||||
"type": "MARKED_ELEMENT",
|
||||
"mainFile": "lib/lib.kt",
|
||||
"newName": "bar",
|
||||
"withRuntime": "true",
|
||||
"overloadRenamer.onlyPrimaryElement": "true"
|
||||
}
|
||||
Vendored
+9
@@ -0,0 +1,9 @@
|
||||
package lib
|
||||
|
||||
fun /*rename*/foo(n: Int) {
|
||||
|
||||
}
|
||||
|
||||
fun foo(n: Int, s: String) {
|
||||
|
||||
}
|
||||
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
package usage;
|
||||
|
||||
import static lib.LibKt.foo;
|
||||
|
||||
public class JUsage {
|
||||
void test() {
|
||||
foo(1);
|
||||
foo(1, "2");
|
||||
}
|
||||
}
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
package usage
|
||||
|
||||
import lib.foo
|
||||
|
||||
fun test() {
|
||||
foo(1)
|
||||
foo(1, "2")
|
||||
}
|
||||
@@ -199,7 +199,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
|
||||
val searchInComments = renameParamsObject["searchInComments"]?.asBoolean ?: true
|
||||
val searchInTextOccurrences = renameParamsObject["searchInTextOccurrences"]?.asBoolean ?: true
|
||||
runRenameProcessor(context, newName, substitution, searchInComments, searchInTextOccurrences)
|
||||
runRenameProcessor(context, newName, substitution, renameParamsObject, searchInComments, searchInTextOccurrences)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -211,7 +211,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
val aClass = context.javaFacade.findClass(classFQN, context.project.allScope())!!
|
||||
val substitution = RenamePsiElementProcessor.forElement(aClass).substituteElementToRename(aClass, null)
|
||||
|
||||
runRenameProcessor(context, newName, substitution, true, true)
|
||||
runRenameProcessor(context, newName, substitution, renameParamsObject, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +229,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
if (method == null) throw IllegalStateException("Method with signature '$methodSignature' wasn't found in class $classFQN")
|
||||
|
||||
val substitution = RenamePsiElementProcessor.forElement(method).substituteElementToRename(method, null)
|
||||
runRenameProcessor(context, newName, substitution, false, false)
|
||||
runRenameProcessor(context, newName, substitution, renameParamsObject, false, false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -272,7 +272,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
val psiElement = segmentReference.resolve()!!
|
||||
|
||||
val substitution = RenamePsiElementProcessor.forElement(psiElement).substituteElementToRename(psiElement, null)
|
||||
runRenameProcessor(context, newName, substitution, true, true)
|
||||
runRenameProcessor(context, newName, substitution, renameParamsObject, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -284,7 +284,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
val mainFile = rootDir.findChild(file)!!
|
||||
val psiFile = PsiManager.getInstance(context.project).findFile(mainFile)
|
||||
|
||||
runRenameProcessor(context, newName, psiFile, true, true)
|
||||
runRenameProcessor(context, newName, psiFile, renameParamsObject, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -298,7 +298,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
val psiFile = PsiManager.getInstance(context.project).findFile(mainFile) as PropertiesFile
|
||||
val property = psiFile.findPropertyByKey(oldName) as Property
|
||||
|
||||
runRenameProcessor(context, newName, property, true, true)
|
||||
runRenameProcessor(context, newName, property, renameParamsObject, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -334,7 +334,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
|
||||
val substitution = RenamePsiElementProcessor.forElement(psiElement).substituteElementToRename(psiElement, null)
|
||||
|
||||
runRenameProcessor(context, newName, substitution, true, true)
|
||||
runRenameProcessor(context, newName, substitution, renameParamsObject, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -358,7 +358,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
|
||||
val substitution = RenamePsiElementProcessor.forElement(propertyWrapper).substituteElementToRename(propertyWrapper, null)
|
||||
|
||||
runRenameProcessor(context, newName, substitution, true, true)
|
||||
runRenameProcessor(context, newName, substitution, renameParamsObject, true, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -366,10 +366,14 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() {
|
||||
context: TestContext,
|
||||
newName: String,
|
||||
substitution: PsiElement?,
|
||||
renameParamsObject: JsonObject,
|
||||
isSearchInComments: Boolean,
|
||||
isSearchTextOccurrences: Boolean
|
||||
) {
|
||||
val renameProcessor = RenameProcessor(context.project, substitution, newName, isSearchInComments, isSearchTextOccurrences)
|
||||
if (renameParamsObject["overloadRenamer.onlyPrimaryElement"]?.asBoolean ?: false) {
|
||||
with(AutomaticOverloadsRenamer) { substitution?.elementFilter = { false } }
|
||||
}
|
||||
Extensions.getExtensions(AutomaticRenamerFactory.EP_NAME).forEach { renameProcessor.addRenamerFactory(it) }
|
||||
renameProcessor.run()
|
||||
}
|
||||
|
||||
@@ -65,6 +65,12 @@ public class RenameTestGenerated extends AbstractRenameTest {
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("automaticRenamerOverloadsAmbiguousImport/automaticRenamerOverloadsAmbiguousImport.test")
|
||||
public void testAutomaticRenamerOverloadsAmbiguousImport_AutomaticRenamerOverloadsAmbiguousImport() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/automaticRenamerOverloadsAmbiguousImport/automaticRenamerOverloadsAmbiguousImport.test");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("automaticRenamerOverloadsClass/class.test")
|
||||
public void testAutomaticRenamerOverloadsClass_Class() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/automaticRenamerOverloadsClass/class.test");
|
||||
|
||||
Reference in New Issue
Block a user