提交 862dc76e 编写于 作者: A Alexey Sedunov

Rename: Fix processing of overriding methods with mangled names

 #KT-24460 Fixed
上级 eea9113d
......@@ -1676,13 +1676,21 @@ public class KotlinTypeMapper {
}
@Nullable
public static String internalNameWithoutModuleSuffix(@NotNull String name) {
public static String demangleInternalName(@NotNull String name) {
int indexOfDollar = name.indexOf('$');
if (indexOfDollar == -1) {
return null;
}
return indexOfDollar >= 0 ? name.substring(0, indexOfDollar) : null;
}
return name.substring(0, indexOfDollar) + '$';
@Nullable
public static String getModuleNameSuffix(@NotNull String name) {
int indexOfDollar = name.indexOf('$');
return indexOfDollar >=0 ? name.substring(indexOfDollar + 1) : null;
}
@Nullable
public static String internalNameWithoutModuleSuffix(@NotNull String name) {
String demangledName = demangleInternalName(name);
return demangledName != null ? demangledName + '$' : null;
}
}
}
......@@ -44,4 +44,6 @@ interface KtLightMethod : PsiAnnotationMethod, KtLightMember<PsiMethod> {
val isDelegated: Boolean
get() = lightMemberOrigin?.originKind == JvmDeclarationOriginKind.DELEGATION
|| lightMemberOrigin?.originKind == JvmDeclarationOriginKind.CLASS_MEMBER_DELEGATION_TO_DEFAULT_IMPL
val isMangled: Boolean
}
\ No newline at end of file
......@@ -30,6 +30,7 @@ import org.jetbrains.kotlin.asJava.classes.cannotModify
import org.jetbrains.kotlin.asJava.classes.lazyPub
import org.jetbrains.kotlin.asJava.propertyNameByAccessor
import org.jetbrains.kotlin.asJava.unwrapped
import org.jetbrains.kotlin.codegen.state.KotlinTypeMapper
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.resolve.DescriptorUtils
......@@ -84,9 +85,17 @@ class KtLightMethodImpl private constructor(
}
}
override val isMangled: Boolean
get() {
val demangledName = KotlinTypeMapper.InternalNameMapper.demangleInternalName(name) ?: return false
val originalName = propertyNameByAccessor(demangledName, this) ?: demangledName
return originalName == kotlinOrigin?.name
}
override fun setName(name: String): PsiElement? {
val jvmNameAnnotation = modifierList.findAnnotation(DescriptorUtils.JVM_NAME.asString())
val newNameForOrigin = propertyNameByAccessor(name, this) ?: name
val demangledName = (if (isMangled) KotlinTypeMapper.InternalNameMapper.demangleInternalName(name) else null) ?: name
val newNameForOrigin = propertyNameByAccessor(demangledName, this) ?: demangledName
if (newNameForOrigin == kotlinOrigin?.name) {
jvmNameAnnotation?.delete()
return this
......
......@@ -227,6 +227,9 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
}
else if (declaration is KtFunction) {
processStaticsFromCompanionObject(declaration)
if (element.isMangled) {
searchNamedElement(declaration) { it.restrictToKotlinSources() }
}
}
}
......@@ -297,9 +300,14 @@ class KotlinReferencesSearcher : QueryExecutorBase<PsiReference, ReferencesSearc
return allMethods.filter { it is KtLightMethod && it.kotlinOrigin == declaration }
}
private fun searchNamedElement(element: PsiNamedElement?, name: String? = element?.name) {
private fun searchNamedElement(
element: PsiNamedElement?,
name: String? = element?.name,
modifyScope: ((SearchScope) -> SearchScope)? = null
) {
if (name != null && element != null) {
val scope = queryParameters.effectiveSearchScope(element)
val baseScope = queryParameters.effectiveSearchScope(element)
val scope = if (modifyScope != null) modifyScope(baseScope) else baseScope
val context = UsageSearchContext.IN_CODE + UsageSearchContext.IN_FOREIGN_LANGUAGES + UsageSearchContext.IN_COMMENTS
val resultProcessor = KotlinRequestResultProcessor(element,
queryParameters.elementToSearch.namedUnwrappedElement ?: element,
......
......@@ -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)
......
class J extends X.Y {
@Override
public int newOverridableMethod$production_sources_for_module_light_idea_test_case(int x) {
return super.newOverridableMethod$production_sources_for_module_light_idea_test_case(x);
}
}
\ No newline at end of file
sealed class X {
internal abstract fun newOverridableMethod(x: Int): Int
abstract class Y : X() {
override fun newOverridableMethod(x: Int): Int = 1
}
class Z : Y() {
override fun newOverridableMethod(x: Int): Int =
if (x > 0) x
else super.newOverridableMethod(x)
}
}
fun get() : X? {
return X.Z()
}
fun test() {
get()?.newOverridableMethod(2)
}
class J extends X.Y {
@Override
public int overridableMethod$production_sources_for_module_light_idea_test_case(int x) {
return super.overridableMethod$production_sources_for_module_light_idea_test_case(x);
}
}
\ No newline at end of file
sealed class X {
internal abstract fun /*rename*/overridableMethod(x: Int): Int
abstract class Y : X() {
override fun overridableMethod(x: Int): Int = 1
}
class Z : Y() {
override fun overridableMethod(x: Int): Int =
if (x > 0) x
else super.overridableMethod(x)
}
}
fun get() : X? {
return X.Z()
}
fun test() {
get()?.overridableMethod(2)
}
{
"type": "MARKED_ELEMENT",
"mainFile": "test.kt",
"newName": "newOverridableMethod"
}
\ No newline at end of file
class J extends X.Y {
@Override
public int getNewOverridableVar$production_sources_for_module_light_idea_test_case() {
return super.getNewOverridableVar$production_sources_for_module_light_idea_test_case();
}
@Override
public void setNewOverridableVar$production_sources_for_module_light_idea_test_case(int value) {
super.setNewOverridableVar$production_sources_for_module_light_idea_test_case(value);
}
}
\ No newline at end of file
sealed class X {
internal abstract var newOverridableVar: Int
abstract class Y : X() {
override var newOverridableVar: Int
get() = 1
set(value) {}
}
class Z : Y() {
override var newOverridableVar: Int
get() = super.newOverridableVar
set(value) { super.newOverridableVar = value }
}
}
fun get() : X? {
return X.Z()
}
fun test() {
get()?.newOverridableVar
get()?.newOverridableVar = 3
}
class J extends X.Y {
@Override
public int getOverridableVar$production_sources_for_module_light_idea_test_case() {
return super.getOverridableVar$production_sources_for_module_light_idea_test_case();
}
@Override
public void setOverridableVar$production_sources_for_module_light_idea_test_case(int value) {
super.setOverridableVar$production_sources_for_module_light_idea_test_case(value);
}
}
\ No newline at end of file
sealed class X {
internal abstract var /*rename*/overridableVar: Int
abstract class Y : X() {
override var overridableVar: Int
get() = 1
set(value) {}
}
class Z : Y() {
override var overridableVar: Int
get() = super.overridableVar
set(value) { super.overridableVar = value }
}
}
fun get() : X? {
return X.Z()
}
fun test() {
get()?.overridableVar
get()?.overridableVar = 3
}
{
"type": "MARKED_ELEMENT",
"mainFile": "test.kt",
"newName": "newOverridableVar"
}
\ No newline at end of file
......@@ -214,6 +214,16 @@ public class RenameTestGenerated extends AbstractRenameTest {
runTest("idea/testData/refactoring/rename/importAliasByRef/importAliasByRef.test");
}
@TestMetadata("internalFunWithOverrides/internalFunWithOverrides.test")
public void testInternalFunWithOverrides_InternalFunWithOverrides() throws Exception {
runTest("idea/testData/refactoring/rename/internalFunWithOverrides/internalFunWithOverrides.test");
}
@TestMetadata("internalVarWithOverrides/internalFunWithOverrides.test")
public void testInternalVarWithOverrides_InternalFunWithOverrides() throws Exception {
runTest("idea/testData/refactoring/rename/internalVarWithOverrides/internalFunWithOverrides.test");
}
@TestMetadata("javaClassImportAliasByRef/javaClassImportAliasByRef.test")
public void testJavaClassImportAliasByRef_JavaClassImportAliasByRef() throws Exception {
runTest("idea/testData/refactoring/rename/javaClassImportAliasByRef/javaClassImportAliasByRef.test");
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册