From cc5c3c2353efdc7785a4035ba385e1969b5e807c Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Tue, 19 Apr 2016 19:01:04 +0300 Subject: [PATCH] Rename: Fixed rename of Java getters/setters through synthetic property references in Kotlin #KT-8817 Fixed --- ChangeLog.md | 1 + .../SyntheticPropertyAccessorReference.kt | 10 +- idea/src/META-INF/plugin.xml | 3 + .../KotlinElementDescriptionProvider.kt | 8 +- .../RenameJavaSyntheticPropertyHandler.kt | 99 +++++++++++++++++++ .../after/Bean.java | 10 ++ .../after/test.kt | 4 + .../before/Bean.java | 10 ++ .../before/test.kt | 4 + ...ameJavaSyntheticIsPropertyByGetterRef.test | 5 + .../after/Bean.java | 10 ++ .../after/test.kt | 5 + .../before/Bean.java | 10 ++ .../before/test.kt | 5 + ...enameJavaSyntheticPropertyByGetterRef.test | 5 + .../after/Bean.java | 10 ++ .../after/test.kt | 5 + .../before/Bean.java | 10 ++ .../before/test.kt | 5 + ...avaSyntheticPropertyByGetterSetterRef.test | 5 + .../after/Bean.java | 10 ++ .../after/test.kt | 5 + .../before/Bean.java | 10 ++ .../before/test.kt | 5 + ...enameJavaSyntheticPropertyBySetterRef.test | 5 + .../refactoring/rename/AbstractRenameTest.kt | 34 ++++++- .../rename/RenameTestGenerated.java | 24 +++++ 27 files changed, 311 insertions(+), 6 deletions(-) create mode 100644 idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameJavaSyntheticPropertyHandler.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/after/Bean.java create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/after/test.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/before/Bean.java create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/before/test.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/renameJavaSyntheticIsPropertyByGetterRef.test create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/after/Bean.java create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/after/test.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/before/Bean.java create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/before/test.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/renameJavaSyntheticPropertyByGetterRef.test create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/after/Bean.java create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/after/test.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/before/Bean.java create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/before/test.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/renameJavaSyntheticPropertyByGetterSetterRef.test create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/after/Bean.java create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/after/test.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/before/Bean.java create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/before/test.kt create mode 100644 idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/renameJavaSyntheticPropertyBySetterRef.test diff --git a/ChangeLog.md b/ChangeLog.md index 1b0d902977a..c081028d132 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -31,6 +31,7 @@ Issues fixed: - [KT-11908](https://youtrack.jetbrains.com/issue/KT-11866) Allow properties with custom setters to be used in generated equals/hashCode/toString - [KT-11845](https://youtrack.jetbrains.com/issue/KT-11845) Fixed exception on attempt to find derived classes - [KT-11736](https://youtrack.jetbrains.com/issue/KT-11736) Fixed searching of Java usages for @JvmStatic properties and @JvmStatic @JvmOverloads functions +- [KT-8817](https://youtrack.jetbrains.com/issue/KT-8817) Fixed rename of Java getters/setters through synthetic property references in Kotlin #### Debugger diff --git a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt index 53df839507c..ae8b594df4f 100644 --- a/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt +++ b/idea/idea-analysis/src/org/jetbrains/kotlin/idea/references/SyntheticPropertyAccessorReference.kt @@ -51,6 +51,12 @@ sealed class SyntheticPropertyAccessorReference(expression: KtNameReferenceExpre override fun canRename() = true + fun renameByPropertyName(newName: String): PsiElement? { + val nameIdentifier = KtPsiFactory(expression).createNameIdentifier(newName) + expression.getReferencedNameElement().replace(nameIdentifier) + return expression + } + override fun handleElementRename(newElementName: String?): PsiElement? { if (!Name.isValidIdentifier(newElementName!!)) return expression @@ -65,9 +71,7 @@ sealed class SyntheticPropertyAccessorReference(expression: KtNameReferenceExpre } if (newName == null) return expression //TODO: handle the case when get/set becomes ordinary method - val nameIdentifier = KtPsiFactory(expression).createNameIdentifier(newName.identifier) - expression.getReferencedNameElement().replace(nameIdentifier) - return expression + return renameByPropertyName(newName.identifier) } class Getter(expression: KtNameReferenceExpression) : SyntheticPropertyAccessorReference(expression, true) diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 1d9c090544d..be702947101 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -411,9 +411,12 @@ + + diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinElementDescriptionProvider.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinElementDescriptionProvider.kt index 2c14851704d..e222d5ceec2 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinElementDescriptionProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/KotlinElementDescriptionProvider.kt @@ -24,9 +24,12 @@ import com.intellij.psi.PsiNamedElement import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.refactoring.util.RefactoringDescriptionLocation import com.intellij.usageView.UsageViewLongNameLocation +import com.intellij.usageView.UsageViewTypeLocation import org.jetbrains.kotlin.asJava.unwrapped import org.jetbrains.kotlin.descriptors.ConstructorDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.refactoring.rename.RenameJavaSyntheticPropertyHandler import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.resolve.DescriptorUtils @@ -44,6 +47,7 @@ class KotlinElementDescriptionProvider : ElementDescriptionProvider { is KtTypeParameter -> "type parameter" is KtParameter -> "parameter" is KtDestructuringDeclarationEntry -> "variable" + is RenameJavaSyntheticPropertyHandler.SyntheticPropertyWrapper -> "property" else -> null } @@ -55,9 +59,9 @@ class KotlinElementDescriptionProvider : ElementDescriptionProvider { return descriptor } - if (targetElement !is PsiNamedElement || targetElement !is KtElement) return null - + if (targetElement !is PsiNamedElement || targetElement.language != KotlinLanguage.INSTANCE) return null return when(location) { + is UsageViewTypeLocation -> elementKind() is UsageViewLongNameLocation -> targetElement.getName() is RefactoringDescriptionLocation -> { val kind = elementKind() ?: return null diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameJavaSyntheticPropertyHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameJavaSyntheticPropertyHandler.kt new file mode 100644 index 00000000000..1cd77db0f9e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameJavaSyntheticPropertyHandler.kt @@ -0,0 +1,99 @@ +/* + * 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.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.actionSystem.DataContext +import com.intellij.openapi.editor.Editor +import com.intellij.openapi.project.Project +import com.intellij.psi.* +import com.intellij.psi.impl.light.LightElement +import com.intellij.psi.search.SearchScope +import com.intellij.refactoring.rename.PsiElementRenameHandler +import com.intellij.refactoring.rename.RenamePsiElementProcessor +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.caches.resolve.analyze +import org.jetbrains.kotlin.idea.util.IdeDescriptorRenderers +import org.jetbrains.kotlin.load.java.JvmAbi +import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtSimpleNameExpression +import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode +import org.jetbrains.kotlin.resolve.source.getPsi +import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor + +class RenameJavaSyntheticPropertyHandler : PsiElementRenameHandler() { + class Processor : RenamePsiElementProcessor() { + override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap, scope: SearchScope) { + val propertyWrapper = element as? SyntheticPropertyWrapper ?: return + propertyWrapper.getter?.let { allRenames[it] = JvmAbi.getterName(newName) } + propertyWrapper.setter?.let { allRenames[it] = JvmAbi.setterName(newName) } + } + + override fun canProcessElement(element: PsiElement) = element is SyntheticPropertyWrapper + } + + internal class SyntheticPropertyWrapper( + manager: PsiManager, + val descriptor: SyntheticJavaPropertyDescriptor + ): LightElement(manager, KotlinLanguage.INSTANCE), PsiNamedElement { + val getter: PsiMethod? get() = descriptor.getMethod.source.getPsi() as? PsiMethod + val setter: PsiMethod? get() = descriptor.setMethod?.source?.getPsi() as? PsiMethod + + override fun getContainingFile() = getter?.containingFile + + override fun getName() = descriptor.name.asString() + + override fun setName(name: String): PsiElement? { + getter?.name = JvmAbi.getterName(name) + setter?.name = JvmAbi.setterName(name) + return this + } + + override fun toString(): String { + val renderer = IdeDescriptorRenderers.SOURCE_CODE + return "${renderer.render(descriptor.getMethod)}|${descriptor.setMethod?.let { renderer.render(it) }}" + } + } + + private fun getTargetDescriptor(dataContext: DataContext): SyntheticJavaPropertyDescriptor? { + val caret = CommonDataKeys.CARET.getData(dataContext) ?: return null + val ktFile = CommonDataKeys.PSI_FILE.getData(dataContext) as? KtFile ?: return null + val refExpr = ktFile.findElementAt(caret.offset)?.getNonStrictParentOfType() ?: return null + return refExpr.analyze(BodyResolveMode.PARTIAL)[BindingContext.REFERENCE_TARGET, refExpr] as? SyntheticJavaPropertyDescriptor + } + + override fun isAvailableOnDataContext(dataContext: DataContext): Boolean { + return CommonDataKeys.EDITOR.getData(dataContext) != null && getTargetDescriptor(dataContext) != null + } + + override fun invoke(project: Project, editor: Editor?, file: PsiFile?, dataContext: DataContext) { + val descriptor = getTargetDescriptor(dataContext) ?: return + val wrappingContext = DataContext { id -> + if (CommonDataKeys.PSI_ELEMENT.`is`(id)) { + return@DataContext SyntheticPropertyWrapper(PsiManager.getInstance(project), descriptor) + } + dataContext.getData(id) + } + super.invoke(project, editor, file, wrappingContext) + } + + override fun invoke(project: Project, elements: Array, dataContext: DataContext) { + // Can't be invoked outside of a text editor + } +} diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/after/Bean.java b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/after/Bean.java new file mode 100644 index 00000000000..7a1fbd1022a --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/after/Bean.java @@ -0,0 +1,10 @@ +public class Bean { + private boolean prop; + public boolean isProp2() { return prop; } + public void setProp2(boolean prop) { this.prop = prop; } + + void test() { + isProp2(); + setProp2(true); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/after/test.kt b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/after/test.kt new file mode 100644 index 00000000000..46aaf7b3128 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/after/test.kt @@ -0,0 +1,4 @@ +fun test(bean: Bean) { + bean.isProp2 = true + println(bean.isProp2) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/before/Bean.java b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/before/Bean.java new file mode 100644 index 00000000000..a72cbe4720a --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/before/Bean.java @@ -0,0 +1,10 @@ +public class Bean { + private boolean prop; + public boolean isProp() { return prop; } + public void setProp(boolean prop) { this.prop = prop; } + + void test() { + isProp(); + setProp(true); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/before/test.kt b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/before/test.kt new file mode 100644 index 00000000000..e8750d1cedc --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/before/test.kt @@ -0,0 +1,4 @@ +fun test(bean: Bean) { + bean.isProp = true + println(bean./*rename*/isProp) +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/renameJavaSyntheticIsPropertyByGetterRef.test b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/renameJavaSyntheticIsPropertyByGetterRef.test new file mode 100644 index 00000000000..59e4a1e2fdc --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/renameJavaSyntheticIsPropertyByGetterRef.test @@ -0,0 +1,5 @@ +{ + "type": "SYNTHETIC_PROPERTY", + "mainFile": "test.kt", + "newName": "isProp2" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/after/Bean.java b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/after/Bean.java new file mode 100644 index 00000000000..aa64cba8f8a --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/after/Bean.java @@ -0,0 +1,10 @@ +public class Bean { + private String prop = "value"; + public String getProp2() { return prop; } + public void setProp2(String prop) { this.prop = prop; } + + void test() { + getProp2(); + setProp2(""); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/after/test.kt b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/after/test.kt new file mode 100644 index 00000000000..736b9715793 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/after/test.kt @@ -0,0 +1,5 @@ +fun test(bean: Bean) { + bean.prop2 = "a" + println(bean.prop2) + bean.prop2 += "a" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/before/Bean.java b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/before/Bean.java new file mode 100644 index 00000000000..e46678c27c6 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/before/Bean.java @@ -0,0 +1,10 @@ +public class Bean { + private String prop = "value"; + public String getProp() { return prop; } + public void setProp(String prop) { this.prop = prop; } + + void test() { + getProp(); + setProp(""); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/before/test.kt b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/before/test.kt new file mode 100644 index 00000000000..0804d93c534 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/before/test.kt @@ -0,0 +1,5 @@ +fun test(bean: Bean) { + bean.prop = "a" + println(bean./*rename*/prop) + bean.prop += "a" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/renameJavaSyntheticPropertyByGetterRef.test b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/renameJavaSyntheticPropertyByGetterRef.test new file mode 100644 index 00000000000..47db0eaef04 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/renameJavaSyntheticPropertyByGetterRef.test @@ -0,0 +1,5 @@ +{ + "type": "SYNTHETIC_PROPERTY", + "mainFile": "test.kt", + "newName": "prop2" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/after/Bean.java b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/after/Bean.java new file mode 100644 index 00000000000..aa64cba8f8a --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/after/Bean.java @@ -0,0 +1,10 @@ +public class Bean { + private String prop = "value"; + public String getProp2() { return prop; } + public void setProp2(String prop) { this.prop = prop; } + + void test() { + getProp2(); + setProp2(""); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/after/test.kt b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/after/test.kt new file mode 100644 index 00000000000..736b9715793 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/after/test.kt @@ -0,0 +1,5 @@ +fun test(bean: Bean) { + bean.prop2 = "a" + println(bean.prop2) + bean.prop2 += "a" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/before/Bean.java b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/before/Bean.java new file mode 100644 index 00000000000..e46678c27c6 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/before/Bean.java @@ -0,0 +1,10 @@ +public class Bean { + private String prop = "value"; + public String getProp() { return prop; } + public void setProp(String prop) { this.prop = prop; } + + void test() { + getProp(); + setProp(""); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/before/test.kt b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/before/test.kt new file mode 100644 index 00000000000..19f5073cf32 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/before/test.kt @@ -0,0 +1,5 @@ +fun test(bean: Bean) { + bean.prop = "a" + println(bean.prop) + bean./*rename*/prop += "a" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/renameJavaSyntheticPropertyByGetterSetterRef.test b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/renameJavaSyntheticPropertyByGetterSetterRef.test new file mode 100644 index 00000000000..47db0eaef04 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/renameJavaSyntheticPropertyByGetterSetterRef.test @@ -0,0 +1,5 @@ +{ + "type": "SYNTHETIC_PROPERTY", + "mainFile": "test.kt", + "newName": "prop2" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/after/Bean.java b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/after/Bean.java new file mode 100644 index 00000000000..aa64cba8f8a --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/after/Bean.java @@ -0,0 +1,10 @@ +public class Bean { + private String prop = "value"; + public String getProp2() { return prop; } + public void setProp2(String prop) { this.prop = prop; } + + void test() { + getProp2(); + setProp2(""); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/after/test.kt b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/after/test.kt new file mode 100644 index 00000000000..736b9715793 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/after/test.kt @@ -0,0 +1,5 @@ +fun test(bean: Bean) { + bean.prop2 = "a" + println(bean.prop2) + bean.prop2 += "a" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/before/Bean.java b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/before/Bean.java new file mode 100644 index 00000000000..e46678c27c6 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/before/Bean.java @@ -0,0 +1,10 @@ +public class Bean { + private String prop = "value"; + public String getProp() { return prop; } + public void setProp(String prop) { this.prop = prop; } + + void test() { + getProp(); + setProp(""); + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/before/test.kt b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/before/test.kt new file mode 100644 index 00000000000..c7917f6fd80 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/before/test.kt @@ -0,0 +1,5 @@ +fun test(bean: Bean) { + bean./*rename*/prop = "a" + println(bean.prop) + bean.prop += "a" +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/renameJavaSyntheticPropertyBySetterRef.test b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/renameJavaSyntheticPropertyBySetterRef.test new file mode 100644 index 00000000000..47db0eaef04 --- /dev/null +++ b/idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/renameJavaSyntheticPropertyBySetterRef.test @@ -0,0 +1,5 @@ +{ + "type": "SYNTHETIC_PROPERTY", + "mainFile": "test.kt", + "newName": "prop2" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt index 8283868bdf9..58f696e1efb 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/AbstractRenameTest.kt @@ -43,19 +43,25 @@ import com.intellij.refactoring.rename.naming.AutomaticRenamerFactory import com.intellij.refactoring.util.CommonRefactoringUtil.RefactoringErrorHintException import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor +import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult import org.jetbrains.kotlin.idea.jsonUtils.getNullableString import org.jetbrains.kotlin.idea.jsonUtils.getString +import org.jetbrains.kotlin.idea.refactoring.toPsiFile import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.search.allScope import org.jetbrains.kotlin.idea.test.* import org.jetbrains.kotlin.incremental.components.NoLookupLocation import org.jetbrains.kotlin.name.* import org.jetbrains.kotlin.psi.KtFile +import org.jetbrains.kotlin.psi.KtSimpleNameExpression import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorToSourceUtils +import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.MemberScope import org.jetbrains.kotlin.serialization.deserialization.findClassAcrossModuleDependencies +import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor import org.junit.Assert import java.io.File @@ -68,7 +74,8 @@ private enum class RenameType { KOTLIN_PACKAGE, MARKED_ELEMENT, FILE, - BUNDLE_PROPERTY + BUNDLE_PROPERTY, + SYNTHETIC_PROPERTY } abstract class AbstractRenameTest : KotlinMultiFileTestCase() { @@ -109,6 +116,7 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() { RenameType.MARKED_ELEMENT -> renameMarkedElement(renameObject, context) RenameType.FILE -> renameFile(renameObject, context) RenameType.BUNDLE_PROPERTY -> renameBundleProperty(renameObject, context) + RenameType.SYNTHETIC_PROPERTY -> renameSyntheticProperty(renameObject, context) } if (hintDirective != null) { @@ -323,6 +331,30 @@ abstract class AbstractRenameTest : KotlinMultiFileTestCase() { } } + private fun renameSyntheticProperty(renameParamsObject: JsonObject, context: TestContext) { + val mainFilePath = renameParamsObject.getString("mainFile") + val newName = renameParamsObject.getString("newName") + + doTestCommittingDocuments { rootDir, rootAfter -> + configExtra(rootDir, renameParamsObject) + + val psiFile = rootDir.findFileByRelativePath(mainFilePath)!!.toPsiFile(project)!! + + val doc = PsiDocumentManager.getInstance(project).getDocument(psiFile)!! + val marker = doc.extractMarkerOffset(project, "/*rename*/") + assert(marker != -1) + + val refExpr = psiFile.findElementAt(marker)!!.getNonStrictParentOfType()!! + val descriptor = refExpr.analyze(BodyResolveMode.PARTIAL)[BindingContext.REFERENCE_TARGET, refExpr] + as SyntheticJavaPropertyDescriptor + val propertyWrapper = RenameJavaSyntheticPropertyHandler.SyntheticPropertyWrapper(psiFile.manager, descriptor) + + val substitution = RenamePsiElementProcessor.forElement(propertyWrapper).substituteElementToRename(propertyWrapper, null) + + runRenameProcessor(context, newName, substitution, true, true) + } + } + private fun runRenameProcessor( context: TestContext, newName: String, diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java index 3bcacebaf87..967c1e67e35 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java @@ -215,6 +215,30 @@ public class RenameTestGenerated extends AbstractRenameTest { doTest(fileName); } + @TestMetadata("renameJavaSyntheticIsPropertyByGetterRef/renameJavaSyntheticIsPropertyByGetterRef.test") + public void testRenameJavaSyntheticIsPropertyByGetterRef_RenameJavaSyntheticIsPropertyByGetterRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameJavaSyntheticIsPropertyByGetterRef/renameJavaSyntheticIsPropertyByGetterRef.test"); + doTest(fileName); + } + + @TestMetadata("renameJavaSyntheticPropertyByGetterRef/renameJavaSyntheticPropertyByGetterRef.test") + public void testRenameJavaSyntheticPropertyByGetterRef_RenameJavaSyntheticPropertyByGetterRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterRef/renameJavaSyntheticPropertyByGetterRef.test"); + doTest(fileName); + } + + @TestMetadata("renameJavaSyntheticPropertyByGetterSetterRef/renameJavaSyntheticPropertyByGetterSetterRef.test") + public void testRenameJavaSyntheticPropertyByGetterSetterRef_RenameJavaSyntheticPropertyByGetterSetterRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameJavaSyntheticPropertyByGetterSetterRef/renameJavaSyntheticPropertyByGetterSetterRef.test"); + doTest(fileName); + } + + @TestMetadata("renameJavaSyntheticPropertyBySetterRef/renameJavaSyntheticPropertyBySetterRef.test") + public void testRenameJavaSyntheticPropertyBySetterRef_RenameJavaSyntheticPropertyBySetterRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameJavaSyntheticPropertyBySetterRef/renameJavaSyntheticPropertyBySetterRef.test"); + doTest(fileName); + } + @TestMetadata("renameKotlinBaseMethod/javaWrapperForBaseFunction.test") public void testRenameKotlinBaseMethod_JavaWrapperForBaseFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameKotlinBaseMethod/javaWrapperForBaseFunction.test");