Rename: Fix processing of references to synthetic Java properties

#KT-17742 Fixed
This commit is contained in:
Alexey Sedunov
2018-03-05 13:54:14 +03:00
parent 862dc76ed9
commit 8978c4b9e8
19 changed files with 256 additions and 65 deletions
+3
View File
@@ -512,6 +512,9 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio.
<psi.treeChangePreprocessor implementation="org.jetbrains.kotlin.idea.caches.KotlinPackageStatementPsiTreeChangePreprocessor"/>
<renamePsiElementProcessor id="KotlinAwareJavaGetter"
implementation="org.jetbrains.kotlin.idea.refactoring.rename.KotlinAwareJavaGetterRenameProcessor"
order="first"/>
<renamePsiElementProcessor id="KotlinClass"
implementation="org.jetbrains.kotlin.idea.refactoring.rename.RenameKotlinClassifierProcessor"
order="first"/>
@@ -0,0 +1,39 @@
/*
* Copyright 2000-2018 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.kotlin.idea.refactoring.rename
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.PsiReference
import com.intellij.psi.PsiType
import com.intellij.refactoring.rename.RenameJavaMethodProcessor
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.idea.references.SyntheticPropertyAccessorReference
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.synthetic.SyntheticJavaPropertyDescriptor
import org.jetbrains.kotlin.utils.ifEmpty
class KotlinAwareJavaGetterRenameProcessor : RenameJavaMethodProcessor() {
override fun canProcessElement(element: PsiElement) = element is PsiMethod && element !is KtLightMethod && JvmAbi.isGetterName(element.name)
override fun findReferences(element: PsiElement): MutableCollection<PsiReference> {
val getterReferences = super.findReferences(element)
val getter = element as? PsiMethod ?: return getterReferences
val propertyName = SyntheticJavaPropertyDescriptor.propertyNameByGetMethodName(Name.identifier(getter.name)) ?: return getterReferences
val setterName = JvmAbi.setterName(propertyName.asString())
val containingClass = getter.containingClass ?: return getterReferences
val setterReferences = containingClass
.findMethodsByName(setterName, true)
.filter { it.parameters.size == 1 && it.returnType == PsiType.VOID }
.flatMap { super.findReferences(it).filterIsInstance<SyntheticPropertyAccessorReference.Setter>() }
.ifEmpty { return getterReferences }
return ArrayList<PsiReference>(getterReferences.size + setterReferences.size).apply {
addAll(getterReferences)
setterReferences.mapTo(this) { SyntheticPropertyAccessorReference.Getter(it.expression) }
}
}
}
@@ -35,8 +35,7 @@ import org.jetbrains.kotlin.asJava.elements.KtLightElement
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.unwrapped
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.*
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.Pass
@@ -183,19 +182,20 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
override fun prepareRenaming(element: PsiElement, newName: String, allRenames: MutableMap<PsiElement, String>, scope: SearchScope) {
super.prepareRenaming(element, newName, allRenames, scope)
val originalName = (element.unwrapped as? KtNamedFunction)?.name ?: ""
if (element is KtLightMethod && getJvmName(element) == null) {
(element.kotlinOrigin as? KtNamedFunction)?.let { allRenames[it] = newName }
}
if (element is FunctionWithSupersWrapper) {
allRenames.remove(element)
}
val originalName = (element.unwrapped as? KtNamedFunction)?.name ?: return
for (declaration in ((element as? FunctionWithSupersWrapper)?.supers ?: listOf(element))) {
val psiMethod = wrapPsiMethod(declaration) ?: continue
allRenames[declaration] = newName
val baseName = psiMethod.name
val newBaseName = if (demangleInternalName(baseName) == originalName) "$newName$${getModuleNameSuffix(baseName)}" else newName
val newBaseName = if (demangleInternalName(baseName) == originalName) {
mangleInternalName(newName, getModuleNameSuffix(baseName)!!)
} else newName
if (psiMethod.containingClass != null) {
psiMethod.forEachOverridingMethod(scope) { it ->
val overrider = (it as? PsiMirrorElement)?.prototype as? PsiMethod ?: it
@@ -227,7 +227,7 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
ambiguousImportUsages += usage
}
else {
if (!renameUsageIfPossible(usage, element, newName)) {
if (!renameMangledUsageIfPossible(usage, element, newName)) {
simpleUsages += usage
}
}
@@ -46,19 +46,7 @@ import org.jetbrains.kotlin.psi.psiUtil.parents
import org.jetbrains.kotlin.psi.psiUtil.quoteIfNeeded
import org.jetbrains.kotlin.resolve.ImportPath
import java.util.ArrayList
import kotlin.collections.Collection
import kotlin.collections.HashSet
import kotlin.collections.List
import kotlin.collections.MutableMap
import kotlin.collections.flatMapTo
import kotlin.collections.forEach
import kotlin.collections.isEmpty
import kotlin.collections.mapNotNullTo
import kotlin.collections.none
import kotlin.collections.plusAssign
import kotlin.collections.set
import kotlin.collections.toMutableList
import kotlin.collections.toTypedArray
import kotlin.collections.*
abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
class MangledJavaRefUsageInfo(
@@ -151,15 +139,16 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
&& ref.multiResolve(false).mapNotNullTo(HashSet()) { it.element?.unwrapped }.size > 1
}
protected fun renameUsageIfPossible(usage: UsageInfo, element: PsiElement, newName: String): Boolean {
var chosenName: String? = null
if (usage is MangledJavaRefUsageInfo) {
chosenName = KotlinTypeMapper.InternalNameMapper.mangleInternalName(newName, usage.manglingSuffix)
protected fun renameMangledUsageIfPossible(usage: UsageInfo, element: PsiElement, newName: String): Boolean {
val chosenName = if (usage is MangledJavaRefUsageInfo) {
KotlinTypeMapper.InternalNameMapper.mangleInternalName(newName, usage.manglingSuffix)
} else {
val reference = usage.reference
if (reference is KtReference) {
chosenName = (if (element is KtLightMethod && element.isMangled) KotlinTypeMapper.InternalNameMapper.demangleInternalName(newName) else null) ?: newName
}
if (element is KtLightMethod && element.isMangled) {
KotlinTypeMapper.InternalNameMapper.demangleInternalName(newName)
} else null
} else null
}
if (chosenName == null) return false
usage.reference?.handleElementRename(chosenName)
@@ -174,7 +163,7 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
for (usage in usages) {
if (renameUsageIfPossible(usage, element, newName)) continue
if (renameMangledUsageIfPossible(usage, element, newName)) continue
simpleUsages += usage
}
@@ -186,7 +175,7 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
element.ambiguousImportUsages?.forEach {
val ref = it.reference as? PsiPolyVariantReference ?: return@forEach
if (ref.multiResolve(false).isEmpty()) {
if (!renameUsageIfPossible(it, element, newName)) {
if (!renameMangledUsageIfPossible(it, element, newName)) {
ref.handleElementRename(newName)
}
}
@@ -24,7 +24,6 @@ import com.intellij.psi.search.UsageSearchContext
import com.intellij.psi.search.searches.MethodReferencesSearch
import com.intellij.psi.util.MethodSignatureUtil
import com.intellij.psi.util.TypeConversionUtil
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.compatibility.ExecutorProcessor
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference
@@ -32,10 +31,8 @@ import org.jetbrains.kotlin.idea.references.SyntheticPropertyAccessorReference
import org.jetbrains.kotlin.idea.references.readWriteAccess
import org.jetbrains.kotlin.idea.search.restrictToKotlinSources
import org.jetbrains.kotlin.idea.util.runReadActionInSmartMode
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.load.java.JvmAbi
import org.jetbrains.kotlin.load.java.getPropertyNamesCandidatesByAccessorName
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper.InternalNameMapper.demangleInternalName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.psi.KtCallableDeclaration
import org.jetbrains.kotlin.psi.KtNamedFunction
@@ -50,30 +47,10 @@ class KotlinOverridingMethodReferenceSearcher : MethodUsagesSearcher() {
return
}
if (method is KtLightMethod) {
method.kotlinOrigin?.let { ktElement ->
val (mayBeMangled, name) = p.project.runReadActionInSmartMode {
ktElement.hasModifier(KtTokens.PRIVATE_KEYWORD) || ktElement.hasModifier(KtTokens.INTERNAL_KEYWORD)
} to method.name
if (mayBeMangled && name != null) {
val demangledName = demangleInternalName(name)
if (demangledName != null) {
val wrappedMethod = object : KtLightMethod by method {
override fun getName(): String = demangledName
}
processQuery(
MethodReferencesSearch.SearchParameters(wrappedMethod, p.scopeDeterminedByUser, p.isStrictSignatureSearch, p.optimizer),
consumer
)
}
}
}
}
val searchScope = p.project.runReadActionInSmartMode {
p.effectiveSearchScope
.intersectWith(method.useScope)
.restrictToKotlinSources()
.intersectWith(method.useScope)
.restrictToKotlinSources()
}
if (searchScope === GlobalSearchScope.EMPTY_SCOPE) return
@@ -133,8 +110,8 @@ class KotlinOverridingMethodReferenceSearcher : MethodUsagesSearcher() {
}
fun countNonFinalLightMethods() = refElement
.toLightMethods()
.filterNot { it.hasModifierProperty(PsiModifier.FINAL) }
.toLightMethods()
.filterNot { it.hasModifierProperty(PsiModifier.FINAL) }
val lightMethods = when (refElement) {
is KtProperty, is KtParameter -> {