Move: Process internal usages after all declarations are moved

This commit is contained in:
Alexey Sedunov
2017-04-04 19:45:06 +03:00
parent a9e7d3d5cf
commit 4a168ff780
8 changed files with 171 additions and 136 deletions
@@ -24,7 +24,6 @@ import com.intellij.util.IncorrectOperationException
import com.intellij.util.SmartList import com.intellij.util.SmartList
import org.jetbrains.kotlin.descriptors.DeclarationDescriptor import org.jetbrains.kotlin.descriptors.DeclarationDescriptor
import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet import org.jetbrains.kotlin.idea.codeInsight.shorten.addToShorteningWaitSet
import org.jetbrains.kotlin.idea.codeInsight.shorten.isToBeShortened
import org.jetbrains.kotlin.idea.core.ShortenReferences import org.jetbrains.kotlin.idea.core.ShortenReferences
import org.jetbrains.kotlin.idea.core.copied import org.jetbrains.kotlin.idea.core.copied
import org.jetbrains.kotlin.idea.core.quoteIfNeeded import org.jetbrains.kotlin.idea.core.quoteIfNeeded
@@ -38,7 +37,10 @@ import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.name.isOneSegmentFQN import org.jetbrains.kotlin.name.isOneSegmentFQN
import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension import org.jetbrains.kotlin.plugin.references.SimpleNameReferenceExtension
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementOrCallableRef
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector
import org.jetbrains.kotlin.psi.psiUtil.startOffset
import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver import org.jetbrains.kotlin.resolve.DataClassDescriptorResolver
import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.types.expressions.OperatorConventions
@@ -130,7 +132,6 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
enum class ShorteningMode { enum class ShorteningMode {
NO_SHORTENING, NO_SHORTENING,
DELAYED_SHORTENING, DELAYED_SHORTENING,
DELAYED_SHORTENING_VIA_USER_DATA,
FORCED_SHORTENING FORCED_SHORTENING
} }
@@ -159,7 +160,6 @@ class KtSimpleNameReference(expression: KtSimpleNameExpression) : KtSimpleRefere
if (needToShorten) { if (needToShorten) {
when (shorteningMode) { when (shorteningMode) {
ShorteningMode.FORCED_SHORTENING -> ShortenReferences.DEFAULT.process(newQualifiedElement) ShorteningMode.FORCED_SHORTENING -> ShortenReferences.DEFAULT.process(newQualifiedElement)
ShorteningMode.DELAYED_SHORTENING_VIA_USER_DATA -> newQualifiedElement.isToBeShortened = true
else -> newQualifiedElement.addToShorteningWaitSet() else -> newQualifiedElement.addToShorteningWaitSet()
} }
} }
@@ -18,6 +18,7 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations
import com.intellij.ide.util.EditorHelper import com.intellij.ide.util.EditorHelper
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Key
import com.intellij.openapi.util.Ref import com.intellij.openapi.util.Ref
import com.intellij.openapi.util.text.StringUtil import com.intellij.openapi.util.text.StringUtil
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
@@ -29,6 +30,7 @@ import com.intellij.refactoring.move.MoveCallback
import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor
import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler
import com.intellij.refactoring.rename.RenameUtil import com.intellij.refactoring.rename.RenameUtil
import com.intellij.refactoring.util.MoveRenameUsageInfo
import com.intellij.refactoring.util.NonCodeUsageInfo import com.intellij.refactoring.util.NonCodeUsageInfo
import com.intellij.refactoring.util.RefactoringUIUtil import com.intellij.refactoring.util.RefactoringUIUtil
import com.intellij.refactoring.util.TextOccurrencesUtil import com.intellij.refactoring.util.TextOccurrencesUtil
@@ -47,9 +49,9 @@ import org.jetbrains.kotlin.idea.core.deleteSingle
import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName import org.jetbrains.kotlin.idea.refactoring.fqName.getKotlinFqName
import org.jetbrains.kotlin.idea.refactoring.move.* import org.jetbrains.kotlin.idea.refactoring.move.*
import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler import org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories.MoveKotlinClassHandler
import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode
import org.jetbrains.kotlin.idea.search.projectScope import org.jetbrains.kotlin.idea.search.projectScope
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext
import org.jetbrains.kotlin.psi.psiUtil.isAncestor import org.jetbrains.kotlin.psi.psiUtil.isAncestor
@@ -81,6 +83,8 @@ interface Mover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration {
} }
} }
internal var KtSimpleNameExpression.internalUsageInfo: UsageInfo? by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFO"))
class MoveDeclarationsDescriptor( class MoveDeclarationsDescriptor(
val elementsToMove: Collection<KtNamedDeclaration>, val elementsToMove: Collection<KtNamedDeclaration>,
val moveTarget: KotlinMoveTarget, val moveTarget: KotlinMoveTarget,
@@ -122,12 +126,6 @@ class MoveKotlinDeclarationsProcessor(
fun getConflictsAsUsages(): List<UsageInfo> = conflicts.entrySet().map { ConflictUsageInfo(it.key, it.value) } fun getConflictsAsUsages(): List<UsageInfo> = conflicts.entrySet().map { ConflictUsageInfo(it.key, it.value) }
class UsageToProcessBeforeMoveWrapper private constructor (element: PsiElement, val originalUsage: UsageInfo): UsageInfo(element) {
companion object {
fun create(originalUsage: UsageInfo) = originalUsage.element?.let { UsageToProcessBeforeMoveWrapper(it, originalUsage) }
}
}
public override fun findUsages(): Array<UsageInfo> { public override fun findUsages(): Array<UsageInfo> {
if (elementsToMove.isEmpty()) return UsageInfo.EMPTY_ARRAY if (elementsToMove.isEmpty()) return UsageInfo.EMPTY_ARRAY
@@ -194,12 +192,8 @@ class MoveKotlinDeclarationsProcessor(
conflictChecker.checkAllConflicts(externalUsages, internalUsages, conflicts) conflictChecker.checkAllConflicts(externalUsages, internalUsages, conflicts)
descriptor.delegate.collectConflicts(descriptor, internalUsages, conflicts) descriptor.delegate.collectConflicts(descriptor, internalUsages, conflicts)
usages += internalUsages
usages += externalUsages usages += externalUsages
val (usagesToProcessLater, usagesToProcessEarly) = internalUsages.partition { it is UnqualifiableMoveRenameUsageInfo }
usages += usagesToProcessLater
usagesToProcessBeforeMove += usagesToProcessEarly
usages += usagesToProcessBeforeMove.mapNotNull { UsageToProcessBeforeMoveWrapper.create(it) }
} }
descriptor.delegate.collectConflicts(descriptor, usagesToProcessBeforeMove, conflicts) descriptor.delegate.collectConflicts(descriptor, usagesToProcessBeforeMove, conflicts)
@@ -212,32 +206,25 @@ class MoveKotlinDeclarationsProcessor(
} }
override fun performRefactoring(usages: Array<out UsageInfo>) { override fun performRefactoring(usages: Array<out UsageInfo>) {
fun moveDeclaration(declaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): KtNamedDeclaration? { fun moveDeclaration(declaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): KtNamedDeclaration {
val file = declaration.containingFile as? KtFile
assert(file != null) { "${declaration::class.java}: ${declaration.text}" }
val targetContainer = moveTarget.getOrCreateTargetPsi(declaration) val targetContainer = moveTarget.getOrCreateTargetPsi(declaration)
?: throw AssertionError("Couldn't create Kotlin file for: ${declaration::class.java}: ${declaration.text}") ?: throw AssertionError("Couldn't create Kotlin file for: ${declaration::class.java}: ${declaration.text}")
descriptor.delegate.preprocessDeclaration(descriptor, declaration) descriptor.delegate.preprocessDeclaration(descriptor, declaration)
val newElement = mover(declaration, targetContainer) return mover(declaration, targetContainer).apply {
addToBeShortenedDescendantsToWaitingSet()
newElement.addToBeShortenedDescendantsToWaitingSet() }
return newElement
} }
val usageList = usages.toList()
val (oldInternalUsages, externalUsages) = usageList.partition { (it as? InternableUsage)?.scope != null }
val newInternalUsages = ArrayList<UsageInfo>()
oldInternalUsages.forEach { (it.element as? KtSimpleNameExpression)?.internalUsageInfo = it }
val usagesToProcess = ArrayList(externalUsages)
try { try {
val usageList = usages.toList() descriptor.delegate.preprocessUsages(project, descriptor, usageList)
val (usagesToProcessBeforeMoveWrappers, usagesToProcessAfterMove) =
usageList.partition { it is UsageToProcessBeforeMoveWrapper }
val usagesToProcessBeforeMove = usagesToProcessBeforeMoveWrappers.map { (it as UsageToProcessBeforeMoveWrapper).originalUsage }
descriptor.delegate.preprocessUsages(project, descriptor, usagesToProcessBeforeMove)
descriptor.delegate.preprocessUsages(project, descriptor, usagesToProcessAfterMove)
postProcessMoveUsages(usagesToProcessBeforeMove, shorteningMode = ShorteningMode.DELAYED_SHORTENING_VIA_USER_DATA)
val oldToNewElementsMapping = THashMap<PsiElement, PsiElement>( val oldToNewElementsMapping = THashMap<PsiElement, PsiElement>(
object: TObjectHashingStrategy<PsiElement> { object: TObjectHashingStrategy<PsiElement> {
@@ -259,18 +246,17 @@ class MoveKotlinDeclarationsProcessor(
} }
} }
) )
val newDeclarations = ArrayList<KtNamedDeclaration>()
for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) { for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) {
for ((oldDeclaration, oldLightElements) in kotlinToLightElements) { for ((oldDeclaration, oldLightElements) in kotlinToLightElements) {
val elementListener = transaction!!.getElementListener(oldDeclaration) val elementListener = transaction!!.getElementListener(oldDeclaration)
val newDeclaration = moveDeclaration(oldDeclaration, descriptor.moveTarget) val newDeclaration = moveDeclaration(oldDeclaration, descriptor.moveTarget)
if (newDeclaration == null) { newDeclarations += newDeclaration
for (oldElement in oldLightElements) {
oldToNewElementsMapping[oldElement] = oldElement
}
continue
}
oldToNewElementsMapping[oldDeclaration] = newDeclaration
oldToNewElementsMapping[sourceFile] = newDeclaration.containingKtFile oldToNewElementsMapping[sourceFile] = newDeclaration.containingKtFile
elementListener.elementMoved(newDeclaration) elementListener.elementMoved(newDeclaration)
@@ -288,12 +274,33 @@ class MoveKotlinDeclarationsProcessor(
} }
} }
nonCodeUsages = postProcessMoveUsages(usagesToProcessAfterMove, oldToNewElementsMapping).toTypedArray() newDeclarations.forEach { newDeclaration ->
newInternalUsages += newDeclaration.collectDescendantsOfType<KtSimpleNameExpression>().mapNotNull {
val usageInfo = it.internalUsageInfo
if (usageInfo?.element != null) return@mapNotNull usageInfo
val referencedElement = (usageInfo as? MoveRenameUsageInfo)?.referencedElement ?: return@mapNotNull null
val newReferencedElement = oldToNewElementsMapping[referencedElement] ?: referencedElement
if (!newReferencedElement.isValid) return@mapNotNull null
(usageInfo as? InternableUsage)?.refresh(it, newReferencedElement)
}
}
usagesToProcess += newInternalUsages
nonCodeUsages = postProcessMoveUsages(usagesToProcess, oldToNewElementsMapping).toTypedArray()
} }
catch (e: IncorrectOperationException) { catch (e: IncorrectOperationException) {
nonCodeUsages = null nonCodeUsages = null
RefactoringUIUtil.processIncorrectOperation(myProject, e) RefactoringUIUtil.processIncorrectOperation(myProject, e)
} }
finally {
(newInternalUsages + oldInternalUsages).forEach {
val element = it.element as? KtSimpleNameExpression ?: return@forEach
if (element.isValid) {
element.internalUsageInfo = null
}
}
}
} }
override fun performPsiSpoilingRefactoring() { override fun performPsiSpoilingRefactoring() {
@@ -22,6 +22,7 @@ import com.intellij.openapi.ui.DialogWrapper
import com.intellij.openapi.util.Comparing import com.intellij.openapi.util.Comparing
import com.intellij.openapi.util.Key import com.intellij.openapi.util.Key
import com.intellij.psi.* import com.intellij.psi.*
import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference
import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.RefactoringBundle
import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler
import com.intellij.refactoring.move.MoveCallback import com.intellij.refactoring.move.MoveCallback
@@ -123,11 +124,9 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
val descriptor = bindingContext[BindingContext.REFERENCE_TARGET, refExpr]?.getImportableDescriptor() ?: return null val descriptor = bindingContext[BindingContext.REFERENCE_TARGET, refExpr]?.getImportableDescriptor() ?: return null
val containingDescriptor = descriptor.containingDeclaration ?: return null val containingDescriptor = descriptor.containingDeclaration ?: return null
val declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null var declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null
val callableKind = (descriptor as? CallableMemberDescriptor)?.kind val callableKind = (descriptor as? CallableMemberDescriptor)?.kind
val containingDeclarationForNonRealMember = if (callableKind != CallableMemberDescriptor.Kind.DECLARATION) { if (callableKind != null && callableKind != CallableMemberDescriptor.Kind.DECLARATION) return null
DescriptorToSourceUtilsIde.getAnyDeclaration(project, containingDescriptor) as? KtClassOrObject
} else null
// Special case for enum entry superclass references (they have empty text and don't need to be processed by the refactoring) // Special case for enum entry superclass references (they have empty text and don't need to be processed by the refactoring)
if (refExpr.textRange.isEmpty) return null if (refExpr.textRange.isEmpty) return null
@@ -156,13 +155,13 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
} }
} }
val fqName = DescriptorUtils.getFqName(descriptor).let { if (declaration is KtObjectDeclaration
if (DescriptorUtils.isCompanionObject(descriptor) && declaration.isCompanion()
&& bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, refExpr] != null) { && bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, refExpr] != null) {
it.parent() declaration = declaration.containingClassOrObject ?: declaration
}
else it
} }
val fqName = DescriptorUtils.getFqName(descriptor)
if (!fqName.isSafe) return null if (!fqName.isSafe) return null
val (oldContainer, newContainer) = containerChangeInfo val (oldContainer, newContainer) = containerChangeInfo
@@ -178,30 +177,10 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
} }
.firstOrNull() .firstOrNull()
fun doCreateUsageInfo(refExpr: KtSimpleNameExpression): UsageInfo? { if (isExtension || containerFqName != null || isImported(descriptor)) return {
// Check container instead of declaration itself as the latter may be not real (e.g. fake override) createMoveUsageInfoIfPossible(it.mainReference, declaration, false, this@lazilyProcessInternalReferencesToUpdateOnPackageNameChange)
if (isAncestor(containingDeclarationForNonRealMember ?: declaration, false)) {
if (descriptor.importableFqName == null) return null
if (getReferenceKind(refExpr.mainReference, declaration) != ReferenceKind.QUALIFIABLE) return null
if (containerFqName == null || newContainer is ContainerInfo.UnknownPackage) return null
return fqName.asString().let {
val prefix = containerFqName.asString()
val prefixOffset = it.indexOf(prefix)
val newFqName = if (prefix.isEmpty()) {
val newContainerFqName = newContainer.fqName!!
FqName(if (newContainerFqName.isRoot) it else "${newContainer.fqName!!.asString()}.$it")
}
else {
FqName(it.replaceRange(prefixOffset..prefixOffset + prefix.length - 1, newContainer.fqName!!.asString()))
}
MoveRenameSelfUsageInfo(refExpr.mainReference, declaration, newFqName)
}
}
return createMoveUsageInfoIfPossible(refExpr.mainReference, declaration, false, fqName.toSafe())
} }
if (isExtension || containerFqName != null || isImported(descriptor)) return ::doCreateUsageInfo
return null return null
} }
@@ -216,49 +195,52 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange(
class ImplicitCompanionAsDispatchReceiverUsageInfo(callee: KtSimpleNameExpression) : UsageInfo(callee) class ImplicitCompanionAsDispatchReceiverUsageInfo(callee: KtSimpleNameExpression) : UsageInfo(callee)
interface InternableUsage {
val scope: PsiElement?
fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo?
}
class UnqualifiableMoveRenameUsageInfo( class UnqualifiableMoveRenameUsageInfo(
element: PsiElement, element: PsiElement,
reference: PsiReference, reference: PsiReference,
startOffset: Int,
endOffset: Int,
referencedElement: PsiElement, referencedElement: PsiElement,
val originalFile: PsiFile, val originalFile: PsiFile,
val addImportToOriginalFile: Boolean val addImportToOriginalFile: Boolean,
): MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false) override val scope: PsiElement?
): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), InternableUsage {
class MoveRenameSelfUsageInfo(ref: KtSimpleNameReference, refTarget: PsiElement, val newFqName: FqName): override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? {
MoveRenameUsageInfo(ref.element, ref, ref.rangeInElement.startOffset, ref.rangeInElement.endOffset, refTarget, false) { return UnqualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, originalFile, addImportToOriginalFile, scope)
override fun getReference() = super.getReference() as? KtSimpleNameReference }
} }
class QualifiableMoveRenameUsageInfo( class QualifiableMoveRenameUsageInfo(
element: PsiElement, element: PsiElement,
reference: PsiReference, reference: PsiReference,
startOffset: Int,
endOffset: Int,
referencedElement: PsiElement, referencedElement: PsiElement,
val newFqName: FqName? override val scope: PsiElement?
): MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false) ): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false),
InternableUsage {
override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? {
return QualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, scope)
}
}
fun createMoveUsageInfoIfPossible( fun createMoveUsageInfoIfPossible(
reference: PsiReference, reference: PsiReference,
referencedElement: PsiElement, referencedElement: PsiElement,
addImportToOriginalFile: Boolean, addImportToOriginalFile: Boolean,
newFqName: FqName? scope: PsiElement?
): UsageInfo? { ): UsageInfo? {
val element = reference.element val element = reference.element
if (element.getStrictParentOfType<KtSuperExpression>() != null) return null if (element.getStrictParentOfType<KtSuperExpression>() != null) return null
val range = reference.rangeInElement!!
val startOffset = range.startOffset
val endOffset = range.endOffset
return when (getReferenceKind(reference, referencedElement)) { return when (getReferenceKind(reference, referencedElement)) {
ReferenceKind.QUALIFIABLE -> QualifiableMoveRenameUsageInfo( ReferenceKind.QUALIFIABLE -> QualifiableMoveRenameUsageInfo(
element, reference, startOffset, endOffset, referencedElement, newFqName element, reference, referencedElement, scope
) )
ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo( ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo(
element, reference, startOffset, endOffset, referencedElement, element.containingFile!!, addImportToOriginalFile element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, scope
) )
else -> null else -> null
} }
@@ -332,6 +314,48 @@ private fun updateJavaReference(reference: PsiReferenceExpression, oldElement: P
return false return false
} }
private fun mapToNewOrThis(e: PsiElement, oldToNewElementsMapping: Map<PsiElement, PsiElement>) = oldToNewElementsMapping[e] ?: e
private fun postProcessMoveUsage(
usage: UsageInfo,
oldToNewElementsMapping: Map<PsiElement, PsiElement>,
nonCodeUsages: ArrayList<NonCodeUsageInfo>,
shorteningMode: ShorteningMode
) {
when (usage) {
is NonCodeUsageInfo -> {
nonCodeUsages.add(usage)
}
is UnqualifiableMoveRenameUsageInfo -> {
val file = with(usage) { if (addImportToOriginalFile) originalFile else mapToNewOrThis(originalFile, oldToNewElementsMapping) } as KtFile
val declaration = mapToNewOrThis(usage.referencedElement!!, oldToNewElementsMapping)
addDelayedImportRequest(declaration, file)
}
is MoveRenameUsageInfo -> {
val oldElement = usage.referencedElement!!
val newElement = mapToNewOrThis(oldElement, oldToNewElementsMapping)
val reference = usage.reference ?: (usage.element as? KtSimpleNameExpression)?.mainReference
processReference(reference, newElement, shorteningMode, oldElement)
}
}
}
private fun processReference(reference: PsiReference?, newElement: PsiElement, shorteningMode: ShorteningMode, oldElement: PsiElement) {
try {
when {
reference is PsiMultiReference -> reference.references.forEach { processReference(it, newElement, shorteningMode, oldElement) }
reference is KtSimpleNameReference -> reference.bindToElement(newElement, shorteningMode)
reference is PsiReferenceExpression && updateJavaReference(reference, oldElement, newElement) -> return
else -> reference?.bindToElement(newElement)
}
}
catch (e: IncorrectOperationException) {
// Suppress exception if bindToElement is not implemented
}
}
/** /**
* Perform usage postprocessing and return non-code usages * Perform usage postprocessing and return non-code usages
*/ */
@@ -339,9 +363,7 @@ fun postProcessMoveUsages(usages: Collection<UsageInfo>,
oldToNewElementsMapping: Map<PsiElement, PsiElement> = Collections.emptyMap(), oldToNewElementsMapping: Map<PsiElement, PsiElement> = Collections.emptyMap(),
shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING
): List<NonCodeUsageInfo> { ): List<NonCodeUsageInfo> {
fun counterpart(e: PsiElement) = oldToNewElementsMapping[e] ?: e val sortedUsages = usages.sortedWith(
val sortedUsages = usages.sortedWith(
Comparator<UsageInfo> { o1, o2 -> Comparator<UsageInfo> { o1, o2 ->
val file1 = o1.virtualFile val file1 = o1.virtualFile
val file2 = o2.virtualFile val file2 = o2.virtualFile
@@ -362,45 +384,7 @@ fun postProcessMoveUsages(usages: Collection<UsageInfo>,
val nonCodeUsages = ArrayList<NonCodeUsageInfo>() val nonCodeUsages = ArrayList<NonCodeUsageInfo>()
usageLoop@ for (usage in sortedUsages) { usageLoop@ for (usage in sortedUsages) {
when (usage) { postProcessMoveUsage(usage, oldToNewElementsMapping, nonCodeUsages, shorteningMode)
is NonCodeUsageInfo -> {
nonCodeUsages.add(usage)
}
is MoveRenameSelfUsageInfo -> {
usage.reference?.bindToFqName(usage.newFqName, shorteningMode)
}
is UnqualifiableMoveRenameUsageInfo -> {
val file = with(usage) { if (addImportToOriginalFile) originalFile else counterpart(originalFile) } as KtFile
val declaration = counterpart(usage.referencedElement!!)
addDelayedImportRequest(declaration, file)
}
is MoveRenameUsageInfo -> {
val oldElement = usage.referencedElement!!
val newElement = counterpart(oldElement)
val reference = usage.reference ?: (usage.element as? KtSimpleNameExpression)?.mainReference
try {
when {
reference is KtSimpleNameReference -> {
val newFqName = (usage as? QualifiableMoveRenameUsageInfo)?.newFqName
if (newFqName != null) {
reference.bindToFqName(newFqName, shorteningMode)
}
else {
reference.bindToElement(newElement, shorteningMode)
}
}
reference is PsiReferenceExpression && updateJavaReference(reference, oldElement, newElement) -> continue@usageLoop
else -> reference?.bindToElement(newElement)
}
}
catch (e: IncorrectOperationException) {
// Suppress exception if bindToElement is not implemented
}
}
}
} }
return nonCodeUsages return nonCodeUsages
@@ -0,0 +1,3 @@
package pack
class C
@@ -0,0 +1,15 @@
package pack2
import pack.C
class A {
val a = A()
val b = B()
val c = C()
}
class B {
val a = A()
val b = B()
val c = C()
}
@@ -0,0 +1,15 @@
package pack
class <caret>A {
val a = A()
val b = B()
val c = C()
}
class <caret>B {
val a = A()
val b = B()
val c = C()
}
class C
@@ -0,0 +1,5 @@
{
"mainFile": "pack/test.kt",
"type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS",
"targetPackage": "pack2"
}
@@ -534,6 +534,12 @@ public class MoveTestGenerated extends AbstractMoveTest {
doTest(fileName); doTest(fileName);
} }
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/mutualDependency/mutualDependency.test")
public void testKotlin_moveTopLevelDeclarations_misc_mutualDependency_MutualDependency() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/mutualDependency/mutualDependency.test");
doTest(fileName);
}
@TestMetadata("kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test") @TestMetadata("kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test")
public void testKotlin_moveTopLevelDeclarations_misc_selfReferences_SelfReferences() throws Exception { public void testKotlin_moveTopLevelDeclarations_misc_selfReferences_SelfReferences() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test"); String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test");