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:
@@ -226,6 +226,7 @@
|
|||||||
- [`KT-6363`](https://youtrack.jetbrains.com/issue/KT-6363) Do not rename ambiguous references in import directives
|
- [`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-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-8544`](https://youtrack.jetbrains.com/issue/KT-8544) Show more detailed description in Rename dialog
|
||||||
|
- [`KT-8732`](https://youtrack.jetbrains.com/issue/KT-8732) Implement Rename conflict analysis and fixes for properties/parameters
|
||||||
- [`KT-8860`](https://youtrack.jetbrains.com/issue/KT-8860) Allow renaming class by constructor delegation call referencing primary constructor
|
- [`KT-8860`](https://youtrack.jetbrains.com/issue/KT-8860) Allow renaming class by constructor delegation call referencing primary constructor
|
||||||
- [`KT-8892`](https://youtrack.jetbrains.com/issue/KT-8892) Suggest renaming base declarations on overriding members in object literals
|
- [`KT-8892`](https://youtrack.jetbrains.com/issue/KT-8892) Suggest renaming base declarations on overriding members in object literals
|
||||||
- [`KT-9156`](https://youtrack.jetbrains.com/issue/KT-9156) Quote non-identifier names in Kotlin references
|
- [`KT-9156`](https://youtrack.jetbrains.com/issue/KT-9156) Quote non-identifier names in Kotlin references
|
||||||
@@ -234,6 +235,7 @@
|
|||||||
- [`KT-9444`](https://youtrack.jetbrains.com/issue/KT-9444) Rename dialog: Allow typing any identifier without backquotes
|
- [`KT-9444`](https://youtrack.jetbrains.com/issue/KT-9444) Rename dialog: Allow typing any identifier without backquotes
|
||||||
- [`KT-9446`](https://youtrack.jetbrains.com/issue/KT-9446) Warn about calls with default arguments if function to be renamed inherits default values from some base function which is excluded from rename
|
- [`KT-9446`](https://youtrack.jetbrains.com/issue/KT-9446) Warn about calls with default arguments if function to be renamed inherits default values from some base function which is excluded from rename
|
||||||
- [`KT-10713`](https://youtrack.jetbrains.com/issue/KT-10713) Skip read-only declarations when renaming parameters
|
- [`KT-10713`](https://youtrack.jetbrains.com/issue/KT-10713) Skip read-only declarations when renaming parameters
|
||||||
|
- [`KT-12543`](https://youtrack.jetbrains.com/issue/KT-12543) Qualify property references with 'this' to avoid renaming conflicts
|
||||||
|
|
||||||
#### Java to Kotlin converter
|
#### Java to Kotlin converter
|
||||||
|
|
||||||
|
|||||||
@@ -459,3 +459,8 @@ fun KtElement.nonStaticOuterClasses(): Sequence<KtClass> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun KtElement.containingClass(): KtClass? = getStrictParentOfType<KtClass>()
|
fun KtElement.containingClass(): KtClass? = getStrictParentOfType<KtClass>()
|
||||||
|
|
||||||
|
fun KtClassOrObject.findPropertyByName(name: String): KtNamedDeclaration? {
|
||||||
|
return declarations.firstOrNull { it is KtProperty && it.name == name } as KtNamedDeclaration?
|
||||||
|
?: getPrimaryConstructorParameters().firstOrNull { it.hasValOrVar() && it.name == name }
|
||||||
|
}
|
||||||
@@ -69,13 +69,13 @@ fun ResolvedCall<*>.getExplicitReceiverValue(): ReceiverValue? {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun ResolvedCall<*>.getImplicitReceiverValue(): ReceiverValue? {
|
fun ResolvedCall<*>.getImplicitReceiverValue(): ImplicitReceiver? {
|
||||||
return when (explicitReceiverKind) {
|
return when (explicitReceiverKind) {
|
||||||
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER -> extensionReceiver ?: dispatchReceiver
|
ExplicitReceiverKind.NO_EXPLICIT_RECEIVER -> extensionReceiver ?: dispatchReceiver
|
||||||
ExplicitReceiverKind.DISPATCH_RECEIVER -> extensionReceiver
|
ExplicitReceiverKind.DISPATCH_RECEIVER -> extensionReceiver
|
||||||
ExplicitReceiverKind.EXTENSION_RECEIVER -> dispatchReceiver
|
ExplicitReceiverKind.EXTENSION_RECEIVER -> dispatchReceiver
|
||||||
else -> null
|
else -> null
|
||||||
}
|
} as? ImplicitReceiver
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun ResolvedCall<*>.hasSafeNullableReceiver(context: CallResolutionContext<*>): Boolean {
|
private fun ResolvedCall<*>.hasSafeNullableReceiver(context: CallResolutionContext<*>): Boolean {
|
||||||
|
|||||||
@@ -163,8 +163,17 @@ fun propertyNameByAccessor(name: String, accessor: KtLightMethod): String? {
|
|||||||
val methodName = Name.guessByFirstCharacter(name)
|
val methodName = Name.guessByFirstCharacter(name)
|
||||||
val propertyName = toRename.name ?: ""
|
val propertyName = toRename.name ?: ""
|
||||||
return when {
|
return when {
|
||||||
name.startsWith("get") -> propertyNameByGetMethodName(methodName)
|
JvmAbi.isGetterName(name) -> propertyNameByGetMethodName(methodName)
|
||||||
name.startsWith("set") -> propertyNameBySetMethodName(methodName, propertyName.startsWith("is"))
|
JvmAbi.isSetterName(name) -> propertyNameBySetMethodName(methodName, propertyName.startsWith("is"))
|
||||||
else -> null
|
else -> null
|
||||||
}?.asString()
|
}?.asString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun accessorNameByPropertyName(name: String, accessor: KtLightMethod): String? {
|
||||||
|
val methodName = accessor.name
|
||||||
|
return when {
|
||||||
|
JvmAbi.isGetterName(methodName) -> JvmAbi.getterName(name)
|
||||||
|
JvmAbi.isSetterName(methodName) -> JvmAbi.setterName(name)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,7 @@ import com.intellij.psi.PsiElement
|
|||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
import org.jetbrains.kotlin.descriptors.ClassDescriptorWithResolutionScopes
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
import org.jetbrains.kotlin.descriptors.VariableDescriptor
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
import org.jetbrains.kotlin.idea.resolve.ResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.resolve.frontendService
|
import org.jetbrains.kotlin.idea.resolve.frontendService
|
||||||
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
import org.jetbrains.kotlin.incremental.components.NoLookupLocation
|
||||||
@@ -31,6 +32,7 @@ import org.jetbrains.kotlin.psi.KtElement
|
|||||||
import org.jetbrains.kotlin.psi.KtFile
|
import org.jetbrains.kotlin.psi.KtFile
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
import org.jetbrains.kotlin.psi.psiUtil.parentsWithSelf
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.lazy.FileScopeProvider
|
import org.jetbrains.kotlin.resolve.lazy.FileScopeProvider
|
||||||
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
import org.jetbrains.kotlin.resolve.scopes.LexicalScope
|
||||||
import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions
|
import org.jetbrains.kotlin.resolve.scopes.utils.collectFunctions
|
||||||
@@ -78,6 +80,12 @@ fun PsiElement.getResolutionScope(bindingContext: BindingContext, resolutionFaca
|
|||||||
error("Not in KtFile")
|
error("Not in KtFile")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun KtElement.getResolutionScope(): LexicalScope {
|
||||||
|
val resolutionFacade = getResolutionFacade()
|
||||||
|
val context = resolutionFacade.analyze(this, BodyResolveMode.PARTIAL)
|
||||||
|
return getResolutionScope(context, resolutionFacade)
|
||||||
|
}
|
||||||
|
|
||||||
fun ResolutionFacade.getFileResolutionScope(file: KtFile): LexicalScope {
|
fun ResolutionFacade.getFileResolutionScope(file: KtFile): LexicalScope {
|
||||||
return frontendService<FileScopeProvider>().getFileResolutionScope(file)
|
return frontendService<FileScopeProvider>().getFileResolutionScope(file)
|
||||||
}
|
}
|
||||||
@@ -445,6 +445,9 @@
|
|||||||
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.KotlinAutomaticTestRenamerFactory"/>
|
<automaticRenamerFactory implementation="org.jetbrains.kotlin.idea.refactoring.rename.KotlinAutomaticTestRenamerFactory"/>
|
||||||
<vetoRenameCondition implementation="org.jetbrains.kotlin.idea.refactoring.KotlinVetoRenameCondition"/>
|
<vetoRenameCondition implementation="org.jetbrains.kotlin.idea.refactoring.KotlinVetoRenameCondition"/>
|
||||||
<renameInputValidator implementation="org.jetbrains.kotlin.idea.refactoring.rename.KotlinDeclarationRenameInputValidator"/>
|
<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"/>
|
<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.KtLightMethod
|
||||||
import org.jetbrains.kotlin.asJava.LightClassUtil
|
import org.jetbrains.kotlin.asJava.LightClassUtil
|
||||||
import org.jetbrains.kotlin.asJava.toLightClass
|
import org.jetbrains.kotlin.asJava.toLightClass
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ClassKind
|
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
|
||||||
import org.jetbrains.kotlin.diagnostics.Errors
|
import org.jetbrains.kotlin.diagnostics.Errors
|
||||||
import org.jetbrains.kotlin.idea.KotlinFileType
|
import org.jetbrains.kotlin.idea.KotlinFileType
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.getCallWithAssert
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
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.io.File
|
||||||
import java.lang.annotation.Retention
|
import java.lang.annotation.Retention
|
||||||
import java.util.*
|
import java.util.*
|
||||||
@@ -765,4 +765,25 @@ fun addTypeArgumentsIfNeeded(expression: KtExpression, typeArgumentList: KtTypeA
|
|||||||
|
|
||||||
callElement.addAfter(typeArgumentList, callElement.calleeExpression)
|
callElement.addAfter(typeArgumentList, callElement.calleeExpression)
|
||||||
ShortenReferences.DEFAULT.process(callElement.typeArgumentList!!)
|
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()
|
||||||
|
}
|
||||||
|
|||||||
+27
@@ -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
|
||||||
|
}
|
||||||
+81
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+57
@@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
+113
@@ -21,14 +21,18 @@ import com.intellij.openapi.editor.Editor
|
|||||||
import com.intellij.openapi.ui.Messages
|
import com.intellij.openapi.ui.Messages
|
||||||
import com.intellij.psi.*
|
import com.intellij.psi.*
|
||||||
import com.intellij.psi.search.SearchScope
|
import com.intellij.psi.search.SearchScope
|
||||||
|
import com.intellij.psi.search.searches.DirectClassInheritorsSearch
|
||||||
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
import com.intellij.psi.search.searches.OverridingMethodsSearch
|
||||||
import com.intellij.refactoring.listeners.RefactoringElementListener
|
import com.intellij.refactoring.listeners.RefactoringElementListener
|
||||||
import com.intellij.refactoring.rename.RenameProcessor
|
import com.intellij.refactoring.rename.RenameProcessor
|
||||||
import com.intellij.refactoring.util.RefactoringUtil
|
import com.intellij.refactoring.util.RefactoringUtil
|
||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
|
import com.intellij.usageView.UsageViewUtil
|
||||||
import org.jetbrains.kotlin.asJava.*
|
import org.jetbrains.kotlin.asJava.*
|
||||||
|
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
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.analyze
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
|
||||||
import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary
|
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.load.java.JvmAbi
|
||||||
import org.jetbrains.kotlin.name.Name
|
import org.jetbrains.kotlin.name.Name
|
||||||
import org.jetbrains.kotlin.psi.*
|
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.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.OverrideResolver
|
import org.jetbrains.kotlin.resolve.OverrideResolver
|
||||||
import org.jetbrains.kotlin.resolve.dataClassUtils.isComponentLike
|
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() {
|
class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
||||||
override fun canProcessElement(element: PsiElement): Boolean {
|
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? {
|
private fun chooseCallableToRename(callableDeclaration: KtCallableDeclaration): KtCallableDeclaration? {
|
||||||
val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration)
|
val deepestSuperDeclaration = findDeepestOverriddenDeclaration(callableDeclaration)
|
||||||
if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) {
|
if (deepestSuperDeclaration == null || deepestSuperDeclaration == callableDeclaration) {
|
||||||
@@ -200,6 +311,8 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
|||||||
refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
refKindUsages[UsageKind.SIMPLE_PROPERTY_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
||||||
null)
|
null)
|
||||||
|
|
||||||
|
usages.forEach { (it as? UsageInfoWithReplacement)?.apply() }
|
||||||
|
|
||||||
dropOverrideKeywordIfNecessary(element)
|
dropOverrideKeywordIfNecessary(element)
|
||||||
|
|
||||||
listener?.elementRenamed(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)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class ShadeKotlin {
|
||||||
|
val name1 = 1;
|
||||||
|
fun inner() {
|
||||||
|
val <caret>name2 = 2;
|
||||||
|
print(name1)
|
||||||
|
print(name2)
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class ShadeKotlin {
|
||||||
|
val name1 = 1;
|
||||||
|
fun inner() {
|
||||||
|
val name1 = 2;
|
||||||
|
print(this.name1)
|
||||||
|
print(name1)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class A {
|
||||||
|
val foo = 1
|
||||||
|
val /*rename*/bar = 2
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "foo",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"hint": "Property 'foo' is already declared in class 'A'"
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class ShadeKotlin {
|
||||||
|
val name2 = 1;
|
||||||
|
fun inner() {
|
||||||
|
val name2 = 2;
|
||||||
|
print(this.name2)
|
||||||
|
print(name2)
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class ShadeKotlin {
|
||||||
|
val /*rename*/name1 = 1;
|
||||||
|
fun inner() {
|
||||||
|
val name2 = 2;
|
||||||
|
print(name1)
|
||||||
|
print(name2)
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "name2",
|
||||||
|
"withRuntime": "true"
|
||||||
|
}
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class X {
|
||||||
|
val x: Int get() = 1
|
||||||
|
|
||||||
|
inner class A {
|
||||||
|
val xx: Int get() = 1
|
||||||
|
|
||||||
|
inner class XX {
|
||||||
|
val xx: Int get() = 1
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
this@A.xx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class X {
|
||||||
|
val x: Int get() = 1
|
||||||
|
|
||||||
|
inner class A {
|
||||||
|
val /*rename*/a: Int get() = 1
|
||||||
|
|
||||||
|
inner class XX {
|
||||||
|
val xx: Int get() = 1
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "xx",
|
||||||
|
"withRuntime": "true"
|
||||||
|
}
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class X {
|
||||||
|
val x: Int get() = 1
|
||||||
|
|
||||||
|
inner class A {
|
||||||
|
val x: Int get() = 1
|
||||||
|
|
||||||
|
inner class XX {
|
||||||
|
val xx: Int get() = 1
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
x
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class X {
|
||||||
|
val x: Int get() = 1
|
||||||
|
|
||||||
|
inner class A {
|
||||||
|
val /*rename*/a: Int get() = 1
|
||||||
|
|
||||||
|
inner class XX {
|
||||||
|
val xx: Int get() = 1
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
a
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+6
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "x",
|
||||||
|
"withRuntime": "true"
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
val /*rename*/foo = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
open class B : A() {
|
||||||
|
val bar = 2
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "bar",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"hint": "Property after rename will clash with existing property 'bar' in class 'B'"
|
||||||
|
}
|
||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
open class A {
|
||||||
|
val foo = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
open class B : A() {
|
||||||
|
val /*rename*/bar = 2
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "foo",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"hint": "Property after rename will clash with existing property 'foo' in class 'A'"
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
open class A(val /*rename*/foo: Int)
|
||||||
|
|
||||||
|
open class B : A() {
|
||||||
|
val bar = 2
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "bar",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"hint": "Parameter after rename will clash with existing property 'bar' in class 'B'"
|
||||||
|
}
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
open class A(val foo: Int)
|
||||||
|
|
||||||
|
open class B : A() {
|
||||||
|
val /*rename*/bar = 2
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "foo",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"hint": "Property after rename will clash with existing parameter 'foo' in class 'A'"
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Foo(val /*rename*/a: Int) {
|
||||||
|
val b = 1
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "b",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"hint": "Property 'b' is already declared in class 'Foo'"
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
class Foo(val a: Int) {
|
||||||
|
val /*rename*/b = 1
|
||||||
|
}
|
||||||
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "a",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"hint": "Parameter 'a' is already declared in class 'Foo'"
|
||||||
|
}
|
||||||
+4
@@ -0,0 +1,4 @@
|
|||||||
|
package test
|
||||||
|
|
||||||
|
val foo = 1
|
||||||
|
val /*rename*/bar = 2
|
||||||
Vendored
+7
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"type": "MARKED_ELEMENT",
|
||||||
|
"mainFile": "test/test.kt",
|
||||||
|
"newName": "foo",
|
||||||
|
"withRuntime": "true",
|
||||||
|
"hint": "Property 'foo' is already declared in package 'test'"
|
||||||
|
}
|
||||||
@@ -128,6 +128,10 @@ class InplaceRenameTest : LightPlatformCodeInsightTestCase() {
|
|||||||
doTestInplaceRename("foo")
|
doTestInplaceRename("foo")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun testLocalVarShadowingMemberProperty() {
|
||||||
|
doTestInplaceRename("name1")
|
||||||
|
}
|
||||||
|
|
||||||
private fun doTestInplaceRename(newName: String?) {
|
private fun doTestInplaceRename(newName: String?) {
|
||||||
configureByFile(getTestName(false) + ".kt")
|
configureByFile(getTestName(false) + ".kt")
|
||||||
val element = TargetElementUtilBase.findTargetElement(
|
val element = TargetElementUtilBase.findTargetElement(
|
||||||
|
|||||||
@@ -131,6 +131,66 @@ public class RenameTestGenerated extends AbstractRenameTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("memberPropertyRedeclaration/memberPropertyRedeclaration.test")
|
||||||
|
public void testMemberPropertyRedeclaration_MemberPropertyRedeclaration() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/memberPropertyRedeclaration/memberPropertyRedeclaration.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("memberPropertyShadowedByLocalVar/memberPropertyShadowedByLocalVar.test")
|
||||||
|
public void testMemberPropertyShadowedByLocalVar_MemberPropertyShadowedByLocalVar() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/memberPropertyShadowedByLocalVar/memberPropertyShadowedByLocalVar.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("memberPropertyWithInnerInstanceProperty/memberPropertyWithInnerInstanceProperty.test")
|
||||||
|
public void testMemberPropertyWithInnerInstanceProperty_MemberPropertyWithInnerInstanceProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/memberPropertyWithInnerInstanceProperty/memberPropertyWithInnerInstanceProperty.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("memberPropertyWithOuterInstanceProperty/memberPropertyWithOuterInstanceProperty.test")
|
||||||
|
public void testMemberPropertyWithOuterInstanceProperty_MemberPropertyWithOuterInstanceProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/memberPropertyWithOuterInstanceProperty/memberPropertyWithOuterInstanceProperty.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyAccidentalOverrideSubclass/propertyAccidentalOverrideSubclass.test")
|
||||||
|
public void testPropertyAccidentalOverrideSubclass_PropertyAccidentalOverrideSubclass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/propertyAccidentalOverrideSubclass/propertyAccidentalOverrideSubclass.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyAccidentalOverrideSuperclass/propertyAccidentalOverrideSuperclass.test")
|
||||||
|
public void testPropertyAccidentalOverrideSuperclass_PropertyAccidentalOverrideSuperclass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/propertyAccidentalOverrideSuperclass/propertyAccidentalOverrideSuperclass.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyParameterAccidentalOverrideSubclass/propertyParameterAccidentalOverrideSubclass.test")
|
||||||
|
public void testPropertyParameterAccidentalOverrideSubclass_PropertyParameterAccidentalOverrideSubclass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/propertyParameterAccidentalOverrideSubclass/propertyParameterAccidentalOverrideSubclass.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyParameterAccidentalOverrideSuperclass/propertyParameterAccidentalOverrideSuperclass.test")
|
||||||
|
public void testPropertyParameterAccidentalOverrideSuperclass_PropertyParameterAccidentalOverrideSuperclass() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/propertyParameterAccidentalOverrideSuperclass/propertyParameterAccidentalOverrideSuperclass.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyParameterRedeclaredByProperty/propertyParameterRedeclaredByProperty.test")
|
||||||
|
public void testPropertyParameterRedeclaredByProperty_PropertyParameterRedeclaredByProperty() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/propertyParameterRedeclaredByProperty/propertyParameterRedeclaredByProperty.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("propertyRedeclaredByPropertyParameter/propertyRedeclaredByPropertyParameter.test")
|
||||||
|
public void testPropertyRedeclaredByPropertyParameter_PropertyRedeclaredByPropertyParameter() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/propertyRedeclaredByPropertyParameter/propertyRedeclaredByPropertyParameter.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("renameArgumentsWhenParameterRenamed/parameter.test")
|
@TestMetadata("renameArgumentsWhenParameterRenamed/parameter.test")
|
||||||
public void testRenameArgumentsWhenParameterRenamed_Parameter() throws Exception {
|
public void testRenameArgumentsWhenParameterRenamed_Parameter() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameArgumentsWhenParameterRenamed/parameter.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameArgumentsWhenParameterRenamed/parameter.test");
|
||||||
@@ -784,4 +844,10 @@ public class RenameTestGenerated extends AbstractRenameTest {
|
|||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/syntheticPropertyUsages3/renameSetMethod.test");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/syntheticPropertyUsages3/renameSetMethod.test");
|
||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("topLevelPropertyRedeclaration/topLevelPropertyRedeclaration.test")
|
||||||
|
public void testTopLevelPropertyRedeclaration_TopLevelPropertyRedeclaration() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/topLevelPropertyRedeclaration/topLevelPropertyRedeclaration.test");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user