Rename: Implement Rename conflict analysis for properties. Qualify property references to resove rename conflicts when possible

#KT-8732 Fixed
 #KT-12543 Fixed
(cherry picked from commit e23029d)
This commit is contained in:
Alexey Sedunov
2016-06-14 15:07:52 +03:00
parent 43f5da0ece
commit 77b0bb9849
41 changed files with 878 additions and 9 deletions
+3
View File
@@ -445,6 +445,9 @@
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.KotlinAutomaticTestRenamerFactory"/>
<vetoRenameCondition implementation="org.jetbrains.kotlin.idea.refactoring.KotlinVetoRenameCondition"/>
<renameInputValidator implementation="org.jetbrains.kotlin.idea.refactoring.rename.KotlinDeclarationRenameInputValidator"/>
<rename.inplace.resolveSnapshotProvider
language="kotlin"
implementationClass="org.jetbrains.kotlin.idea.refactoring.rename.KotlinResolveSnapshotProvider"/>
<spellchecker.support implementationClass="org.jetbrains.kotlin.idea.KotlinSpellcheckingStrategy" language="kotlin"/>
@@ -59,10 +59,8 @@ import com.intellij.util.containers.MultiMap
import org.jetbrains.kotlin.asJava.KtLightMethod
import org.jetbrains.kotlin.asJava.LightClassUtil
import org.jetbrains.kotlin.asJava.toLightClass
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.ClassKind
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.KotlinFileType
import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -91,6 +89,8 @@ import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getCallWithAssert
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
import org.jetbrains.kotlin.resolve.source.getPsi
import java.io.File
import java.lang.annotation.Retention
import java.util.*
@@ -765,4 +765,25 @@ fun addTypeArgumentsIfNeeded(expression: KtExpression, typeArgumentList: KtTypeA
callElement.addAfter(typeArgumentList, callElement.calleeExpression)
ShortenReferences.DEFAULT.process(callElement.typeArgumentList!!)
}
}
internal fun DeclarationDescriptor.getThisLabelName(): String {
if (!name.isSpecial) return name.asString()
if (this is AnonymousFunctionDescriptor) {
val function = source.getPsi() as? KtFunction
val argument = function?.parent as? KtValueArgument
val callElement = argument?.getStrictParentOfType<KtCallElement>()
val callee = callElement?.calleeExpression as? KtSimpleNameExpression
if (callee != null) return callee.text
}
return ""
}
internal fun DeclarationDescriptor.explicateAsTextForReceiver(): String {
val labelName = getThisLabelName()
return if (labelName.isEmpty()) "this" else "this@$labelName"
}
internal fun ImplicitReceiver.explicateAsText(): String {
return declarationDescriptor.explicateAsTextForReceiver()
}
@@ -0,0 +1,27 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.refactoring.rename.UnresolvableCollisionUsageInfo
class BasicUnresolvableCollisionUsageInfo(
element: PsiElement,
referencedElement: PsiElement,
private val _description: String) : UnresolvableCollisionUsageInfo(element, referencedElement) {
override fun getDescription() = _description
}
@@ -0,0 +1,81 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiDocumentManager
import com.intellij.psi.PsiElement
import com.intellij.psi.SmartPsiElementPointer
import com.intellij.refactoring.rename.ResolveSnapshotProvider
import com.intellij.util.containers.HashMap
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.refactoring.explicateAsTextForReceiver
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtPsiFactory
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
import org.jetbrains.kotlin.psi.KtTreeVisitorVoid
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import java.util.*
class KotlinResolveSnapshotProvider : ResolveSnapshotProvider() {
override fun createSnapshot(scope: PsiElement) = object : ResolveSnapshot() {
private val project = scope.project
private val document = PsiDocumentManager.getInstance(project).getDocument(scope.containingFile)!!
private val refExpressionToDescriptor = HashMap<SmartPsiElementPointer<*>, PropertyDescriptor>()
init {
scope.accept(
object: KtTreeVisitorVoid() {
override fun visitSimpleNameExpression(expression: KtSimpleNameExpression) {
if (expression.getQualifiedExpressionForSelector() != null) return super.visitSimpleNameExpression(expression)
val context = expression.analyze(BodyResolveMode.PARTIAL)
val targetDescriptor = expression.mainReference.resolveToDescriptors(context).singleOrNull() ?: return
if (targetDescriptor !is PropertyDescriptor) return
refExpressionToDescriptor[expression.createSmartPointer()] = targetDescriptor
}
}
)
}
override fun apply(name: String) {
PsiDocumentManager.getInstance(project).commitDocument(document)
val elementsToShorten = ArrayList<KtElement>()
for ((refExprPointer, targetDescriptor) in refExpressionToDescriptor) {
val refExpr = refExprPointer.element ?: continue
if (refExpr.text != name) continue
val containingDescriptor = targetDescriptor.containingDeclaration
val qualifiedRefText = if (containingDescriptor is ClassDescriptor) {
"${containingDescriptor.explicateAsTextForReceiver()}.${targetDescriptor.name.asString()}"
}
else {
targetDescriptor.importableFqName?.asString() ?: continue
}
val qualifiedRefExpr = KtPsiFactory(project).createExpression(qualifiedRefText)
elementsToShorten += refExpr.replaced(qualifiedRefExpr)
}
ShortenReferences { ShortenReferences.Options.ALL_ENABLED }.process(elementsToShorten)
}
}
}
@@ -0,0 +1,57 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.refactoring.rename.ResolvableCollisionUsageInfo
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.psi.KtElement
import org.jetbrains.kotlin.psi.KtSimpleNameExpression
abstract class KtResolvableCollisionUsageInfo(
element: PsiElement,
referencedElement: PsiElement
) : ResolvableCollisionUsageInfo(element, referencedElement) {
// To prevent simple rename via PsiReference
override fun getReference() = null
abstract fun apply()
}
class UsageInfoWithReplacement(
element: PsiElement,
referencedElement: PsiElement,
private val replacement: KtElement
) : KtResolvableCollisionUsageInfo(element, referencedElement) {
override fun apply() {
element?.replaced(replacement)?.addToShorteningWaitSet(ShortenReferences.Options.ALL_ENABLED)
}
}
class UsageInfoWithFqNameReplacement(
element: KtSimpleNameExpression,
referencedElement: PsiElement,
private val newFqName: FqName
) : KtResolvableCollisionUsageInfo(element, referencedElement) {
override fun apply() {
(element as? KtSimpleNameExpression)?.mainReference?.bindToFqName(newFqName)
}
}
@@ -21,14 +21,18 @@ import com.intellij.openapi.editor.Editor
import com.intellij.openapi.ui.Messages
import com.intellij.psi.*
import com.intellij.psi.search.SearchScope
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
import com.intellij.psi.search.searches.OverridingMethodsSearch
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenameProcessor
import com.intellij.refactoring.util.RefactoringUtil
import com.intellij.usageView.UsageInfo
import com.intellij.usageView.UsageViewUtil
import org.jetbrains.kotlin.asJava.*
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
@@ -39,11 +43,17 @@ import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.findPropertyByName
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
import org.jetbrains.kotlin.resolve.DescriptorUtils
import org.jetbrains.kotlin.resolve.OverrideResolver
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.findCallableMemberBySignature
import org.jetbrains.kotlin.utils.DFS
import org.jetbrains.kotlin.utils.SmartList
class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
override fun canProcessElement(element: PsiElement): Boolean {
@@ -79,6 +89,107 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
}
}
private fun checkAccidentalOverrides(
declaration: KtNamedDeclaration,
newName: String,
descriptor: VariableDescriptor,
result: MutableList<UsageInfo>
) {
fun reportAccidentalOverride(candidate: PsiNamedElement) {
val what = UsageViewUtil.getType(declaration).capitalize()
val withWhat = candidate.renderDescription()
val where = candidate.representativeContainer()?.renderDescription() ?: return
val message = "$what after rename will clash with existing $withWhat in $where"
result += BasicUnresolvableCollisionUsageInfo(candidate, candidate, message)
}
if (descriptor !is PropertyDescriptor) return
val initialClass = declaration.containingClassOrObject ?: return
val initialClassDescriptor = descriptor.containingDeclaration as? ClassDescriptor ?: return
val prototype = object : PropertyDescriptor by descriptor {
override fun getName() = Name.guessByFirstCharacter(newName)
}
DFS.dfs(
listOf(initialClassDescriptor),
DFS.Neighbors<ClassDescriptor> { DescriptorUtils.getSuperclassDescriptors(it) },
object : DFS.AbstractNodeHandler<ClassDescriptor, Unit>() {
override fun beforeChildren(current: ClassDescriptor): Boolean {
if (current == initialClassDescriptor) return true
(current.findCallableMemberBySignature(prototype))?.let { candidateDescriptor ->
val candidate = candidateDescriptor.source.getPsi() as? PsiNamedElement ?: return false
reportAccidentalOverride(candidate)
return false
}
return true
}
override fun result() {
}
}
)
if (!declaration.hasModifier(KtTokens.PRIVATE_KEYWORD)) {
val initialPsiClass = initialClass.toLightClass() ?: return
val prototypes = declaration.toLightMethods().mapNotNull {
it as KtLightMethod
val methodName = accessorNameByPropertyName(newName, it) ?: return@mapNotNull null
object : KtLightMethod by it {
override fun getName() = methodName
}
}
DFS.dfs(
listOf(initialPsiClass),
DFS.Neighbors<PsiClass> { DirectClassInheritorsSearch.search(it) },
object : DFS.AbstractNodeHandler<PsiClass, Unit>() {
override fun beforeChildren(current: PsiClass): Boolean {
if (current == initialPsiClass) return true
if (current is KtLightClass) {
val property = current.kotlinOrigin?.findPropertyByName(newName) ?: return true
reportAccidentalOverride(property)
return false
}
for (psiMethod in prototypes) {
current.findMethodBySignature(psiMethod, false)?.let {
val candidate = it.unwrapped as? PsiNamedElement ?: return true
reportAccidentalOverride(candidate)
return false
}
}
return true
}
override fun result() {
}
}
)
}
}
override fun findCollisions(
element: PsiElement,
newName: String?,
allRenames: MutableMap<out PsiElement, String>,
result: MutableList<UsageInfo>
) {
if (newName == null) return
val declaration = element.namedUnwrappedElement as? KtNamedDeclaration ?: return
val descriptor = declaration.resolveToDescriptor() as VariableDescriptor
val collisions = SmartList<UsageInfo>()
checkRedeclarations(descriptor, newName, collisions)
checkAccidentalOverrides(declaration, newName, descriptor, collisions)
checkOriginalUsagesRetargeting(declaration, newName, result, collisions)
checkNewNameUsagesRetargeting(declaration, newName, collisions)
result += collisions
}
private fun chooseCallableToRename(callableDeclaration: KtCallableDeclaration): KtCallableDeclaration? {
val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration)
if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) {
@@ -200,6 +311,8 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
null)
usages.forEach { (it as? UsageInfoWithReplacement)?.apply() }
dropOverrideKeywordIfNecessary(element)
listener?.elementRenamed(element)
@@ -0,0 +1,239 @@
/*
* Copyright 2010-2016 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.idea.refactoring.rename
import com.intellij.psi.*
import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.usageView.UsageInfo
import com.intellij.usageView.UsageViewUtil
import org.jetbrains.kotlin.descriptors.*
import org.jetbrains.kotlin.idea.analysis.analyzeInContext
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde
import org.jetbrains.kotlin.idea.core.NewDeclarationNameValidator
import org.jetbrains.kotlin.idea.core.copied
import org.jetbrains.kotlin.idea.imports.importableFqName
import org.jetbrains.kotlin.idea.refactoring.explicateAsText
import org.jetbrains.kotlin.idea.refactoring.getThisLabelName
import org.jetbrains.kotlin.idea.references.mainReference
import org.jetbrains.kotlin.idea.search.and
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers
import org.jetbrains.kotlin.idea.util.getAllAccessibleVariables
import org.jetbrains.kotlin.idea.util.getResolutionScope
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.*
import org.jetbrains.kotlin.renderer.DescriptorRenderer
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getImplicitReceiverValue
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.scopes.DescriptorKindFilter
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
import org.jetbrains.kotlin.resolve.scopes.getDescriptorsFiltered
import org.jetbrains.kotlin.resolve.scopes.utils.getImplicitReceiversHierarchy
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.utils.SmartList
internal fun ResolvedCall<*>.noReceivers() = dispatchReceiver == null && extensionReceiver == null
internal fun PsiNamedElement.renderDescription() = "${UsageViewUtil.getType(this)} '$name'".trim()
internal fun PsiElement.representativeContainer(): PsiNamedElement? =
when (this) {
is KtDeclaration -> containingClassOrObject
?: getStrictParentOfType<KtNamedDeclaration>()
?: JavaPsiFacade.getInstance(project).findPackage(getContainingKtFile().packageFqName.asString())
is PsiMember -> containingClass
else -> null
}
internal fun DeclarationDescriptor.canonicalRender(): String = DescriptorRenderer.FQ_NAMES_IN_TYPES.render(this)
internal fun checkRedeclarations(
descriptor: DeclarationDescriptor,
newName: String,
result: MutableList<UsageInfo>
) {
val containingDescriptor = descriptor.containingDeclaration
val containingScope = when (containingDescriptor) {
is ClassDescriptor -> containingDescriptor.unsubstitutedMemberScope
is PackageFragmentDescriptor -> containingDescriptor.getMemberScope()
else -> return
}
containingScope.getDescriptorsFiltered(DescriptorKindFilter.VARIABLES) { it.asString() == newName }.firstOrNull()?.let { candidateDescriptor ->
val candidate = (candidateDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() as? KtNamedDeclaration ?: return
val what = candidate.renderDescription().capitalize()
val where = candidate.representativeContainer()?.renderDescription() ?: return
val message = "$what is already declared in $where"
result += BasicUnresolvableCollisionUsageInfo(candidate, candidate, message)
}
}
private fun LexicalScope.getRelevantDescriptors(
declaration: PsiNamedElement,
name: String
): Collection<DeclarationDescriptor> {
val nameAsName = Name.identifier(name)
return when (declaration) {
is KtProperty, is KtParameter, is PsiField -> getAllAccessibleVariables(nameAsName)
else -> emptyList()
}
}
fun reportShadowing(
declaration: PsiNamedElement,
elementToBindUsageInfoTo: PsiElement,
candidateDescriptor: DeclarationDescriptor,
refElement: PsiElement,
result: MutableList<UsageInfo>
) {
val candidate = DescriptorToSourceUtilsIde.getAnyDeclaration(declaration.project, candidateDescriptor) as? PsiNamedElement ?: return
val message = "${declaration.renderDescription().capitalize()} will be shadowed by ${candidate.renderDescription()}"
result += BasicUnresolvableCollisionUsageInfo(refElement, elementToBindUsageInfoTo, message)
}
private fun checkUsagesRetargeting(
elementToBindUsageInfosTo: PsiElement,
declaration: PsiNamedElement,
name: String,
isNewName: Boolean,
accessibleDescriptors: Collection<DeclarationDescriptor>,
originalUsages: MutableList<UsageInfo>,
newUsages: MutableList<UsageInfo>
) {
val usageIterator = originalUsages.listIterator()
while (usageIterator.hasNext()) {
val usage = usageIterator.next()
val refElement = usage.element as? KtSimpleNameExpression ?: continue
val context = refElement.analyze(BodyResolveMode.PARTIAL)
val scope = refElement
.parentsWithSelf
.filterIsInstance<KtElement>()
.mapNotNull { context[BindingContext.LEXICAL_SCOPE, it] }
.firstOrNull()
?: continue
if (scope.getRelevantDescriptors(declaration, name).isEmpty()) {
if (declaration !is KtProperty && declaration !is KtParameter) continue
if (NewDeclarationNameValidator(refElement.parent, refElement, NewDeclarationNameValidator.Target.VARIABLES)(name)) continue
}
val psiFactory = KtPsiFactory(declaration)
val resolvedCall = refElement.getResolvedCall(context) ?: continue
val callExpression = resolvedCall.call.callElement as? KtExpression ?: continue
val fullCallExpression = callExpression.getQualifiedExpressionForSelectorOrThis()
val qualifiedExpression = if (resolvedCall.noReceivers()) {
val resultingDescriptor = resolvedCall.resultingDescriptor
val fqName =
resultingDescriptor.importableFqName
?: (resultingDescriptor as? ClassifierDescriptor)?.let {
FqName(IdeDescriptorRenderers.SOURCE_CODE.renderClassifierName(it))
}
?: continue
if (fqName.parent().isRoot) {
callExpression.copied()
}
else {
psiFactory.createExpressionByPattern("${fqName.parent().asString()}.$0", callExpression)
}
}
else {
resolvedCall.getExplicitReceiverValue()?.let {
fullCallExpression.copied()
}
?: resolvedCall.getImplicitReceiverValue()?.let { implicitReceiver ->
val expectedLabelName = implicitReceiver.declarationDescriptor.getThisLabelName()
val implicitReceivers = scope.getImplicitReceiversHierarchy()
val receiversWithExpectedName = implicitReceivers.filter {
it.value.type.constructor.declarationDescriptor?.getThisLabelName() == expectedLabelName
}
val canQualifyThis = receiversWithExpectedName.size <= 1
if (canQualifyThis) {
psiFactory.createExpressionByPattern("${implicitReceiver.explicateAsText()}.$0", callExpression)
}
else {
val defaultReceiverClassText =
implicitReceivers.firstOrNull()?.value?.type?.constructor?.declarationDescriptor?.canonicalRender()
val canInsertUnqualifiedThis = accessibleDescriptors.any { it.canonicalRender() == defaultReceiverClassText }
if (canInsertUnqualifiedThis) {
psiFactory.createExpressionByPattern("this.$0", callExpression)
}
else {
callExpression.copied()
}
}
}
?: continue
}
val newCallee = qualifiedExpression.getQualifiedElementSelector() as? KtSimpleNameExpression ?: continue
if (isNewName) {
newCallee.getReferencedNameElement().replace(psiFactory.createNameIdentifier(name))
}
val newContext = qualifiedExpression.analyzeInContext(scope, refElement)
val newResolvedCall = newCallee.getResolvedCall(newContext)
val candidateText = newResolvedCall?.candidateDescriptor?.canonicalRender()
if (newResolvedCall != null
&& !accessibleDescriptors.any { it.canonicalRender() == candidateText }
&& resolvedCall.candidateDescriptor.canonicalRender() != candidateText) {
reportShadowing(declaration, elementToBindUsageInfosTo, newResolvedCall.candidateDescriptor, refElement, newUsages)
continue
}
usageIterator.set(UsageInfoWithReplacement(fullCallExpression, declaration, qualifiedExpression))
}
}
internal fun checkOriginalUsagesRetargeting(
declaration: KtNamedDeclaration,
newName: String,
originalUsages: MutableList<UsageInfo>,
newUsages: MutableList<UsageInfo>
) {
val accessibleDescriptors = declaration.getResolutionScope().getRelevantDescriptors(declaration, newName)
checkUsagesRetargeting(declaration, declaration, newName, true, accessibleDescriptors, originalUsages, newUsages)
}
internal fun checkNewNameUsagesRetargeting(
declaration: KtNamedDeclaration,
newName: String,
newUsages: MutableList<UsageInfo>
) {
val currentName = declaration.name ?: return
val descriptor = declaration.resolveToDescriptor()
for (candidateDescriptor in declaration.getResolutionScope().getRelevantDescriptors(declaration, newName)) {
val candidate = DescriptorToSourceUtilsIde.getAnyDeclaration(declaration.project, candidateDescriptor) as? PsiNamedElement ?: continue
val usages = ReferencesSearch
.search(candidate, candidate.useScope.restrictToKotlinSources() and declaration.useScope)
.mapTo(SmartList<UsageInfo>()) { MoveRenameUsageInfo(it, candidate) }
checkUsagesRetargeting(candidate, declaration, currentName, false, listOf(descriptor), usages, newUsages)
usages.filterIsInstanceTo<KtResolvableCollisionUsageInfo, MutableList<UsageInfo>>(newUsages)
}
}