diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt index 7d4d871568f..db63fab55b1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt @@ -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, 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(), null) - super.renameElement(element, JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(), + super.renameElement(element.copy(), JvmAbi.getterName(newNameUnquoted).quoteIfNeeded(), refKindUsages[UsageKind.GETTER_USAGE]?.toTypedArray() ?: arrayOf(), null) diff --git a/idea/testData/refactoring/rename/renameKotlinValProperty/after/testing/GroovyClient.groovy b/idea/testData/refactoring/rename/renameKotlinValProperty/after/testing/GroovyClient.groovy index 3b5e23de5cf..98d0b15b5fb 100644 --- a/idea/testData/refactoring/rename/renameKotlinValProperty/after/testing/GroovyClient.groovy +++ b/idea/testData/refactoring/rename/renameKotlinValProperty/after/testing/GroovyClient.groovy @@ -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 } diff --git a/plugins/uast-kotlin/tests/KotlinUastReferencesTest.kt b/plugins/uast-kotlin/tests/KotlinUastReferencesTest.kt new file mode 100644 index 00000000000..a2729463164 --- /dev/null +++ b/plugins/uast-kotlin/tests/KotlinUastReferencesTest.kt @@ -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 myField: 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) { + override fun resolve(): PsiMethod? { + val psiClass = JavaPsiFacade.getInstance(element.project).findClass(className, element.resolveScope) ?: return null + val name = element.toUElementOfType()?.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 = 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(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) + }) + +} + + + +