Rename: Qualify references to members of enum companions in case of conflict with enum entries

#KT-10033 Fixed
(cherry picked from commit 3373d54)
This commit is contained in:
Alexey Sedunov
2016-05-27 18:58:33 +03:00
parent 35aa3f7646
commit 5296b61ce9
10 changed files with 93 additions and 6 deletions
+1
View File
@@ -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
@@ -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
@@ -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`"
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 }
}
@@ -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<out UsageInfo>, listener: RefactoringElementListener?) {
override tailrec fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, 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))
@@ -0,0 +1,8 @@
package test;
class Test {
{
E e = E.A;
int n = E.A;
}
}
@@ -0,0 +1,13 @@
package test
enum class E {
A;
companion object {
public const val A: Int = 1
}
}
fun main(args: Array<String>) {
val a = E.Companion.A + 2
}
@@ -0,0 +1,8 @@
package test;
class Test {
{
E e = E.A;
int n = E.B;
}
}
@@ -0,0 +1,13 @@
package test
enum class E {
A;
companion object {
public const val /*rename*/B : Int = 1
}
}
fun main(args: Array<String>) {
val a = E.B + 2
}
@@ -0,0 +1,6 @@
{
"type": "MARKED_ELEMENT",
"mainFile": "test/test.kt",
"newName": "A",
"withRuntime": "true"
}
@@ -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");