Rename: Fix processing of overriding methods with mangled names

#KT-24460 Fixed
This commit is contained in:
Alexey Sedunov
2018-05-30 13:47:40 +03:00
parent eea9113d1c
commit 862dc76ed9
21 changed files with 320 additions and 25 deletions
@@ -121,7 +121,7 @@ class RenameKotlinClassifierProcessor : RenameKotlinPsiProcessor() {
else -> null
}
override fun renameElement(element: PsiElement, newName: String, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
override fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
val ambiguousImportUsages = com.intellij.util.SmartList<UsageInfo>()
for (usage in usages) {
@@ -35,6 +35,8 @@ 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.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.unsafeResolveToDescriptor
import org.jetbrains.kotlin.idea.refactoring.Pass
@@ -181,6 +183,8 @@ 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 }
}
@@ -190,22 +194,22 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
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
if (psiMethod.containingClass != null) {
psiMethod.forEachOverridingMethod { it ->
psiMethod.forEachOverridingMethod(scope) { it ->
val overrider = (it as? PsiMirrorElement)?.prototype as? PsiMethod ?: it
if (overrider is SyntheticElement) return@forEachOverridingMethod true
val overriderName = overrider.name
val baseName = psiMethod.name
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newName)
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, baseName, newBaseName)
if (newOverriderName != null) {
RenameProcessor.assertNonCompileElement(overrider)
allRenames.put(overrider, newOverriderName)
}
return@forEachOverridingMethod true
}
javaMethodProcessorInstance.prepareRenaming(psiMethod, newName, allRenames, scope)
}
}
}
@@ -223,7 +227,9 @@ class RenameKotlinFunctionProcessor : RenameKotlinPsiProcessor() {
ambiguousImportUsages += usage
}
else {
simpleUsages += usage
if (!renameUsageIfPossible(usage, element, newName)) {
simpleUsages += usage
}
}
}
element.ambiguousImportUsages = ambiguousImportUsages
@@ -53,7 +53,7 @@ class RenameKotlinParameterProcessor : RenameKotlinPsiProcessor() {
result += collisions
}
override fun renameElement(element: PsiElement, newName: String, usages: Array<out UsageInfo>, listener: RefactoringElementListener?) {
override fun renameElement(element: PsiElement, newName: String, usages: Array<UsageInfo>, listener: RefactoringElementListener?) {
super.renameElement(element, newName, usages, listener)
usages.forEach { (it as? KtResolvableCollisionUsageInfo)?.apply() }
@@ -36,6 +36,9 @@ 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.descriptors.ClassDescriptor
import org.jetbrains.kotlin.descriptors.PropertyDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
@@ -323,16 +326,21 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
}
for (propertyMethod in propertyMethods) {
if (element is KtDeclaration && newPropertyName != null) {
val mangledPropertyName = if (propertyMethod is KtLightMethod && propertyMethod.isMangled) {
val suffix = getModuleNameSuffix(propertyMethod.name)
if (suffix != null && newPropertyName != null) mangleInternalName(newPropertyName, suffix) else null
} else null
val adjustedPropertyName = mangledPropertyName ?: newPropertyName
if (element is KtDeclaration && adjustedPropertyName != null) {
val wrapper = PropertyMethodWrapper(propertyMethod)
when {
JvmAbi.isGetterName(propertyMethod.name) && getterJvmName == null ->
allRenames[wrapper] = JvmAbi.getterName(newPropertyName)
allRenames[wrapper] = JvmAbi.getterName(adjustedPropertyName)
JvmAbi.isSetterName(propertyMethod.name) && setterJvmName == null ->
allRenames[wrapper] = JvmAbi.setterName(newPropertyName)
allRenames[wrapper] = JvmAbi.setterName(adjustedPropertyName)
}
}
addRenameElements(propertyMethod, (element as PsiNamedElement).name, newPropertyName, allRenames, scope)
addRenameElements(propertyMethod, (element as PsiNamedElement).name, adjustedPropertyName, allRenames, scope)
}
}
@@ -389,7 +397,10 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
val refKindUsages = adjustedUsages.groupBy { usage: UsageInfo ->
val refElement = usage.reference?.resolve()
if (refElement is PsiMethod) {
when (refElement.name) {
val refElementName = refElement.name
val refElementNameToCheck =
(if (usage is MangledJavaRefUsageInfo) demangleInternalName(refElementName) else null) ?: refElementName
when (refElementNameToCheck) {
oldGetterName -> UsageKind.GETTER_USAGE
oldSetterName -> UsageKind.SETTER_USAGE
else -> UsageKind.SIMPLE_PROPERTY_USAGE
@@ -441,7 +452,9 @@ class RenameKotlinPropertyProcessor : RenameKotlinPsiProcessor() {
}
}
else {
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, newName)
val demangledName = if (newName != null && overrider is KtLightMethod && overrider.isMangled) demangleInternalName(newName) else null
val adjustedName = demangledName ?: newName
val newOverriderName = RefactoringUtil.suggestNewOverriderName(overriderName, oldName, adjustedName)
if (newOverriderName != null) {
allRenames[overriderElement] = newOverriderName
}
@@ -24,10 +24,15 @@ import com.intellij.psi.search.searches.ReferencesSearch
import com.intellij.psi.util.PsiUtilCore
import com.intellij.refactoring.listeners.RefactoringElementListener
import com.intellij.refactoring.rename.RenamePsiElementProcessor
import com.intellij.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.asJava.elements.KtLightMethod
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
import org.jetbrains.kotlin.asJava.toLightMethods
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.idea.references.KtReference
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchOptions
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReferencesSearchParameters
import org.jetbrains.kotlin.idea.search.projectScope
@@ -40,15 +45,43 @@ import org.jetbrains.kotlin.psi.psiUtil.isIdentifier
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
abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
class MangledJavaRefUsageInfo(
val manglingSuffix: String,
element: PsiElement,
ref: PsiReference,
referenceElement: PsiElement
) : MoveRenameUsageInfo(
referenceElement,
ref,
ref.getRangeInElement().getStartOffset(),
ref.getRangeInElement().getEndOffset(),
element,
false
)
override fun canProcessElement(element: PsiElement): Boolean = element is KtNamedDeclaration
override fun findReferences(element: PsiElement): Collection<PsiReference> {
val searchParameters = KotlinReferencesSearchParameters(
element,
element.project.projectScope(),
kotlinOptions = KotlinReferencesSearchOptions(searchForComponentConventions = false)
element,
element.project.projectScope(),
kotlinOptions = KotlinReferencesSearchOptions(searchForComponentConventions = false)
)
val references = ReferencesSearch.search(searchParameters).toMutableList()
if (element is KtNamedFunction
@@ -59,6 +92,23 @@ abstract class RenameKotlinPsiProcessor : RenamePsiElementProcessor() {
return references
}
override fun createUsageInfo(element: PsiElement, ref: PsiReference, referenceElement: PsiElement): UsageInfo {
if (ref !is KtReference) {
val targetElement = ref.resolve()
if (targetElement is KtLightMethod && targetElement.isMangled) {
KotlinTypeMapper.InternalNameMapper.getModuleNameSuffix(targetElement.name)?.let {
return MangledJavaRefUsageInfo(
it,
element,
ref,
referenceElement
)
}
}
}
return super.createUsageInfo(element, ref, referenceElement)
}
override fun getElementToSearchInStringsAndComments(element: PsiElement): PsiElement? {
val unwrapped = element?.unwrapped ?: return null
if ((unwrapped is KtDeclaration) && KtPsiUtil.isLocal(unwrapped as KtDeclaration)) return null
@@ -101,12 +151,44 @@ 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)
} else {
val reference = usage.reference
if (reference is KtReference) {
chosenName = (if (element is KtLightMethod && element.isMangled) KotlinTypeMapper.InternalNameMapper.demangleInternalName(newName) else null) ?: newName
}
}
if (chosenName == null) return false
usage.reference?.handleElementRename(chosenName)
return true
}
override fun renameElement(
element: PsiElement,
newName: String,
usages: Array<UsageInfo>,
listener: RefactoringElementListener?
) {
val simpleUsages = ArrayList<UsageInfo>(usages.size)
for (usage in usages) {
if (renameUsageIfPossible(usage, element, newName)) continue
simpleUsages += usage
}
RenameUtil.doRenameGenericNamedElement(element, newName, simpleUsages.toTypedArray(), listener)
}
override fun getPostRenameCallback(element: PsiElement, newName: String, elementListener: RefactoringElementListener): Runnable? {
return Runnable {
element.ambiguousImportUsages?.forEach {
val ref = it.reference as? PsiPolyVariantReference ?: return@forEach
if (ref.multiResolve(false).isEmpty()) {
ref.handleElementRename(newName)
if (!renameUsageIfPossible(it, element, newName)) {
ref.handleElementRename(newName)
}
}
else {
ref.element?.getStrictParentOfType<KtImportDirective>()?.let { importDirective ->
@@ -24,6 +24,7 @@ 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
@@ -31,8 +32,10 @@ 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
@@ -47,6 +50,26 @@ 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)