From 5296b61ce993f9562262ecf6df2da1a43d142410 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Fri, 27 May 2016 18:58:33 +0300 Subject: [PATCH] Rename: Qualify references to members of enum companions in case of conflict with enum entries #KT-10033 Fixed (cherry picked from commit 3373d54) --- ChangeLog.md | 1 + .../kotlin/idea/core/ShortenReferences.kt | 11 +++++++---- .../src/org/jetbrains/kotlin/idea/core/Utils.kt | 16 +++++++++++++++- .../rename/RenameKotlinPropertyProcessor.kt | 17 ++++++++++++++++- .../after/test/JavaClient.java | 8 ++++++++ .../after/test/test.kt | 13 +++++++++++++ .../before/test/JavaClient.java | 8 ++++++++ .../before/test/test.kt | 13 +++++++++++++ ...ropertyInEnumCompanionWithEntryConflict.test | 6 ++++++ .../refactoring/rename/RenameTestGenerated.java | 6 ++++++ 10 files changed, 93 insertions(+), 6 deletions(-) create mode 100644 idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/after/test/JavaClient.java create mode 100644 idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/after/test/test.kt create mode 100644 idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/before/test/JavaClient.java create mode 100644 idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/before/test/test.kt create mode 100644 idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/renameKotlinPropertyInEnumCompanionWithEntryConflict.test diff --git a/ChangeLog.md b/ChangeLog.md index 046a5da7ead..5d3ba62de43 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -242,6 +242,7 @@ - [`KT-9435`](https://youtrack.jetbrains.com/issue/KT-9435) Process property accesor usages (Java) in comments and string literals - [`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) Copy default parameter values to overriding function which is renamed while its base function is not +- [`KT-10033`](https://youtrack.jetbrains.com/issue/KT-10033) Qualify references to members of enum companions in case of conflict with enum entries - [`KT-10713`](https://youtrack.jetbrains.com/issue/KT-10713) Skip read-only declarations when renaming parameters - [`KT-10687`](https://youtrack.jetbrains.com/issue/KT-10687) Qualify property references to avoid shadowing by parameters - [`KT-12543`](https://youtrack.jetbrains.com/issue/KT-12543) Qualify property references with 'this' to avoid renaming conflicts diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt index c0045ca6d23..2a9586574bb 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/ShortenReferences.kt @@ -21,10 +21,7 @@ import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElement import com.intellij.psi.impl.source.PostprocessReformattingAspect import com.intellij.psi.util.PsiTreeUtil -import org.jetbrains.kotlin.descriptors.ClassDescriptor -import org.jetbrains.kotlin.descriptors.ClassifierDescriptor -import org.jetbrains.kotlin.descriptors.DeclarationDescriptor -import org.jetbrains.kotlin.descriptors.PackageViewDescriptor +import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.analysis.analyzeInContext import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade import org.jetbrains.kotlin.idea.core.ShortenReferences.Options @@ -46,6 +43,7 @@ import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver import org.jetbrains.kotlin.resolve.scopes.utils.findClassifier import org.jetbrains.kotlin.resolve.scopes.utils.findPackage +import org.jetbrains.kotlin.resolve.source.getPsi import org.jetbrains.kotlin.utils.singletonOrEmptyList import java.util.* @@ -469,6 +467,11 @@ class ShortenReferences(val options: (KtElement) -> Options = { Options.DEFAULT val selectorsSelectorTarget = selectorsSelector.singleTarget(bindingContext) ?: return false if (selectorsSelectorTarget is ClassDescriptor) return false + // TODO: More generic solution may be possible + if (selectorsSelectorTarget is PropertyDescriptor) { + val source = selectorsSelectorTarget.source.getPsi() as? KtProperty + if (source != null && isEnumCompanionPropertyWithEntryConflict(source, source.name ?: "")) return false + } addElementToShorten(qualifiedExpression) return true diff --git a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt index a322bd81d85..acabdfde600 100644 --- a/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt +++ b/idea/idea-core/src/org/jetbrains/kotlin/idea/core/Utils.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.core +import com.intellij.psi.PsiElement import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.idea.analysis.computeTypeInContext import org.jetbrains.kotlin.idea.caches.resolve.analyze @@ -27,6 +28,7 @@ import org.jetbrains.kotlin.idea.resolve.frontendService import org.jetbrains.kotlin.idea.util.getImplicitReceiversWithInstanceToExpression import org.jetbrains.kotlin.idea.util.getResolutionScope import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelectorOrThis import org.jetbrains.kotlin.resolve.BindingContext @@ -159,4 +161,16 @@ fun KtCallableDeclaration.canOmitDeclaredType(initializerOrBodyExpression: KtExp return canChangeTypeToSubtype && expressionType.isSubtypeOf(declaredType) } -fun String.quoteIfNeeded(): String = if (KotlinNameSuggester.isIdentifier(this)) this else "`$this`" \ No newline at end of file +fun String.quoteIfNeeded(): String = if (KotlinNameSuggester.isIdentifier(this)) this else "`$this`" + +fun isEnumCompanionPropertyWithEntryConflict(element: PsiElement, expectedName: String): Boolean { + if (element !is KtProperty) return false + + val propertyClass = element.containingClassOrObject as? KtObjectDeclaration ?: return false + if (!propertyClass.isCompanion()) return false + + val outerClass = propertyClass.containingClassOrObject as? KtClass ?: return false + if (!outerClass.isEnum()) return false + + return outerClass.declarations.any { it is KtEnumEntry && it.name == expectedName } +} \ No newline at end of file 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 53d513758e2..e43e5093b59 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/rename/RenameKotlinPropertyProcessor.kt @@ -26,6 +26,7 @@ import com.intellij.psi.search.searches.OverridingMethodsSearch import com.intellij.refactoring.JavaRefactoringSettings import com.intellij.refactoring.listeners.RefactoringElementListener import com.intellij.refactoring.rename.RenameProcessor +import com.intellij.refactoring.util.MoveRenameUsageInfo import com.intellij.refactoring.util.RefactoringUtil import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageViewUtil @@ -36,9 +37,11 @@ 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.resolveToDescriptor +import org.jetbrains.kotlin.idea.core.isEnumCompanionPropertyWithEntryConflict import org.jetbrains.kotlin.idea.refactoring.dropOverrideKeywordIfNecessary import org.jetbrains.kotlin.idea.references.KtReference import org.jetbrains.kotlin.idea.references.KtSimpleNameReference +import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.util.application.runReadAction import org.jetbrains.kotlin.lexer.KtTokens import org.jetbrains.kotlin.load.java.JvmAbi @@ -283,7 +286,7 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { SETTER_USAGE } - override tailrec fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { + override tailrec fun renameElement(element: PsiElement, newName: String, usages: Array, listener: RefactoringElementListener?) { if (element is KtLightMethod) { if (element.modifierList.findAnnotation(DescriptorUtils.JVM_NAME.asString()) != null) { return super.renameElement(element, newName, usages, listener) @@ -309,6 +312,18 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() { val oldGetterName = JvmAbi.getterName(name) val oldSetterName = JvmAbi.setterName(name) + if (isEnumCompanionPropertyWithEntryConflict(element, newName)) { + for ((i, usage) in usages.withIndex()) { + if (usage !is MoveRenameUsageInfo) continue + val ref = usage.reference ?: continue + // TODO: Enum value can't be accessed from Java in case of conflict with companion member + if (ref is KtReference) { + val newRef = (ref.bindToElement(element) as? KtSimpleNameExpression)?.mainReference ?: continue + usages[i] = MoveRenameUsageInfo(newRef, usage.referencedElement) + } + } + } + val adjustedUsages = if (element is KtParameter) usages.filterNot { val refTarget = it.reference?.resolve() refTarget is KtLightMethod && isComponentLike(Name.guessByFirstCharacter(refTarget.name)) diff --git a/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/after/test/JavaClient.java b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/after/test/JavaClient.java new file mode 100644 index 00000000000..dd2873cf5e3 --- /dev/null +++ b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/after/test/JavaClient.java @@ -0,0 +1,8 @@ +package test; + +class Test { + { + E e = E.A; + int n = E.A; + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/after/test/test.kt b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/after/test/test.kt new file mode 100644 index 00000000000..f130e51674c --- /dev/null +++ b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/after/test/test.kt @@ -0,0 +1,13 @@ +package test + +enum class E { + A; + + companion object { + public const val A: Int = 1 + } +} + +fun main(args: Array) { + val a = E.Companion.A + 2 +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/before/test/JavaClient.java b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/before/test/JavaClient.java new file mode 100644 index 00000000000..c2f22c62b7b --- /dev/null +++ b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/before/test/JavaClient.java @@ -0,0 +1,8 @@ +package test; + +class Test { + { + E e = E.A; + int n = E.B; + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/before/test/test.kt b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/before/test/test.kt new file mode 100644 index 00000000000..3f60ab5cdb2 --- /dev/null +++ b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/before/test/test.kt @@ -0,0 +1,13 @@ +package test + +enum class E { + A; + + companion object { + public const val /*rename*/B : Int = 1 + } +} + +fun main(args: Array) { + val a = E.B + 2 +} \ No newline at end of file diff --git a/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/renameKotlinPropertyInEnumCompanionWithEntryConflict.test b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/renameKotlinPropertyInEnumCompanionWithEntryConflict.test new file mode 100644 index 00000000000..b92066d2c49 --- /dev/null +++ b/idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/renameKotlinPropertyInEnumCompanionWithEntryConflict.test @@ -0,0 +1,6 @@ +{ + "type": "MARKED_ELEMENT", + "mainFile": "test/test.kt", + "newName": "A", + "withRuntime": "true" +} \ No newline at end of file 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 29d7703a2cc..5548f5cd46d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/rename/RenameTestGenerated.java @@ -893,6 +893,12 @@ public class RenameTestGenerated extends AbstractRenameTest { doTest(fileName); } + @TestMetadata("renamePropertyInEnumCompanionWithEntryConflict/renameKotlinPropertyInEnumCompanionWithEntryConflict.test") + public void testRenamePropertyInEnumCompanionWithEntryConflict_RenameKotlinPropertyInEnumCompanionWithEntryConflict() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renamePropertyInEnumCompanionWithEntryConflict/renameKotlinPropertyInEnumCompanionWithEntryConflict.test"); + doTest(fileName); + } + @TestMetadata("renameSet/set.test") public void testRenameSet_Set() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/rename/renameSet/set.test");