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:
Nicolay Mitropolsky
2019-02-12 21:17:58 +03:00
parent 2bcdadf17f
commit 48619a49ed
3 changed files with 133 additions and 11 deletions
@@ -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,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)
})
}