RenameKotlinPropertyProcessor made not update the property before all references are updated (KT-27602)
because otherwise language independent bean property references couldn't properly handle rename in some cases. Groovy references were also affected by this bug because they weren't able to resolve to original property
This commit is contained in:
+5
-7
@@ -36,9 +36,7 @@ import org.jetbrains.kotlin.asJava.*
|
||||
import org.jetbrains.kotlin.asJava.classes.KtLightClass
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightDeclaration
|
||||
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.demangleInternalName
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.getModuleNameSuffix
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.mangleInternalName
|
||||
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.*
|
||||
import org.jetbrains.kotlin.descriptors.ClassDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||
@@ -350,6 +348,7 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
||||
SETTER_USAGE
|
||||
}
|
||||
|
||||
//TODO: a very long and complicated method, even recursive. mb refactor it somehow? at least split by PsiElement types?
|
||||
override tailrec fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
|
||||
val newNameUnquoted = newName.unquote()
|
||||
if (element is KtLightMethod) {
|
||||
@@ -405,17 +404,16 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
|
||||
oldSetterName -> UsageKind.SETTER_USAGE
|
||||
else -> UsageKind.SIMPLE_PROPERTY_USAGE
|
||||
}
|
||||
}
|
||||
else {
|
||||
} else {
|
||||
UsageKind.SIMPLE_PROPERTY_USAGE
|
||||
}
|
||||
}
|
||||
|
||||
super.renameElement(element, JvmAbi.setterName(newNameUnquoted).quoteIfNeeded(),
|
||||
super.renameElement(element.copy(), JvmAbi.setterName(newNameUnquoted).quoteIfNeeded(),
|
||||
refKindUsages[UsageKind.SETTER_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
||||
null)
|
||||
|
||||
super.renameElement(element, JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(),
|
||||
super.renameElement(element.copy(), JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(),
|
||||
refKindUsages[UsageKind.GETTER_USAGE]?.toTypedArray() ?: arrayOf<UsageInfo>(),
|
||||
null)
|
||||
|
||||
|
||||
+4
-4
@@ -4,11 +4,11 @@ import testing.rename.*;
|
||||
|
||||
class GroovyClient {
|
||||
public void foo(AP ap, DP dp) {
|
||||
ap.getSecond
|
||||
new BP().getSecond
|
||||
new CP().getSecond
|
||||
ap.second
|
||||
new BP().second
|
||||
new CP().second
|
||||
|
||||
dp.getSecond
|
||||
dp.second
|
||||
new EP().second
|
||||
new FP().second
|
||||
}
|
||||
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2010-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||
* that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.uast.test.kotlin
|
||||
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.extensions.Extensions
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.patterns.uast.literalExpression
|
||||
import com.intellij.psi.*
|
||||
import com.intellij.psi.impl.source.resolve.reference.PsiReferenceContributorEP
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistry
|
||||
import com.intellij.psi.impl.source.resolve.reference.ReferenceProvidersRegistryImpl
|
||||
import com.intellij.psi.util.PropertyUtil
|
||||
import com.intellij.testFramework.LightProjectDescriptor
|
||||
import com.intellij.testFramework.registerServiceInstance
|
||||
import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase
|
||||
import org.jetbrains.kotlin.idea.test.KotlinWithJdkAndRuntimeLightProjectDescriptor
|
||||
import org.jetbrains.uast.UExpression
|
||||
import org.jetbrains.uast.evaluateString
|
||||
import org.jetbrains.uast.toUElementOfType
|
||||
import org.junit.Test
|
||||
import kotlin.test.fail
|
||||
|
||||
|
||||
class KotlinUastReferencesTest : KotlinLightCodeInsightFixtureTestCase() {
|
||||
|
||||
override fun getProjectDescriptor(): LightProjectDescriptor = KotlinWithJdkAndRuntimeLightProjectDescriptor.INSTANCE
|
||||
|
||||
|
||||
@Test
|
||||
fun `test original getter is visible when reference is under renaming`() {
|
||||
|
||||
registerReferenceProviders(testRootDisposable) {
|
||||
registerUastReferenceProvider(literalExpression(), uastLiteralReferenceProvider { _, psiLanguageInjectionHost ->
|
||||
arrayOf(GetterReference("KotlinBean", psiLanguageInjectionHost))
|
||||
})
|
||||
}
|
||||
|
||||
myFixture.configureByText(
|
||||
"KotlinBean.kt", """
|
||||
data class KotlinBean(val myF<caret>ield: String)
|
||||
|
||||
val reference = "myField"
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
myFixture.renameElementAtCaret("myRenamedField")
|
||||
|
||||
myFixture.checkResult(
|
||||
"""
|
||||
data class KotlinBean(val myRenamedField: String)
|
||||
|
||||
val reference = "myRenamedField"
|
||||
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private class GetterReference(
|
||||
val className: String,
|
||||
psiElement: PsiElement
|
||||
) : PsiReferenceBase<PsiElement>(psiElement) {
|
||||
override fun resolve(): PsiMethod? {
|
||||
val psiClass = JavaPsiFacade.getInstance(element.project).findClass(className, element.resolveScope) ?: return null
|
||||
val name = element.toUElementOfType<UExpression>()?.evaluateString() ?: return null
|
||||
return PropertyUtil.getGetters(psiClass, name).firstOrNull()
|
||||
}
|
||||
|
||||
override fun handleElementRename(newElementName: String): PsiElement {
|
||||
val resolve = resolve()
|
||||
?: fail("can't resolve during rename, looks like someone renamed or removed the source element before updating references")
|
||||
|
||||
val newName =
|
||||
if (PropertyUtil.getPropertyName(resolve) != null)
|
||||
PropertyUtil.getPropertyName(newElementName) ?: newElementName
|
||||
else newElementName
|
||||
|
||||
return super.handleElementRename(newName)
|
||||
}
|
||||
|
||||
override fun getVariants(): Array<Any> = emptyArray()
|
||||
}
|
||||
|
||||
|
||||
fun registerReferenceProviders(disposable: Disposable, registerContributors: PsiReferenceRegistrar.() -> Unit) {
|
||||
registerReferenceContributor(object : PsiReferenceContributor() {
|
||||
override fun registerReferenceProviders(registrar: PsiReferenceRegistrar) = registrar.registerContributors()
|
||||
}, disposable)
|
||||
}
|
||||
|
||||
fun registerReferenceContributor(contributor: PsiReferenceContributor, disposable: Disposable) {
|
||||
val referenceContributorEp = Extensions.getArea(null).getExtensionPoint<PsiReferenceContributorEP>(PsiReferenceContributor.EP_NAME.name)
|
||||
|
||||
val contributorEp = object : PsiReferenceContributorEP() {
|
||||
override fun getInstance(): PsiReferenceContributor = contributor
|
||||
}
|
||||
|
||||
referenceContributorEp.registerExtension(contributorEp)
|
||||
|
||||
val application = ApplicationManager.getApplication()
|
||||
|
||||
//we need a fresh ReferenceProvidersRegistry after updating ReferenceContributors
|
||||
val oldReferenceProviderRegistry =
|
||||
application.picoContainer.getComponentInstance(ReferenceProvidersRegistry::class.java) as ReferenceProvidersRegistry
|
||||
application.registerServiceInstance(ReferenceProvidersRegistry::class.java, ReferenceProvidersRegistryImpl())
|
||||
|
||||
Disposer.register(disposable, Disposable {
|
||||
referenceContributorEp.unregisterExtension(contributorEp)
|
||||
application.registerServiceInstance(ReferenceProvidersRegistry::class.java, oldReferenceProviderRegistry)
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user