From 735fd6c626b4f7fd57a7d47856397a3d8e6b2086 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 17 Sep 2014 20:45:06 +0400 Subject: [PATCH] Move: Fix detection of imported declarations and processing of extension usages #KT-5787 Fixed #KT-4959 Fixed --- .../moveClassesOrPackages/annotations.xml | 6 + .../codeInsight/JetFileReferencesResolver.kt | 44 +++-- .../plugin/codeInsight/ShortenReferences.kt | 2 +- .../MoveKotlinFileHandler.kt | 11 +- ...MoveKotlinTopLevelDeclarationsProcessor.kt | 87 +++------ .../jet/plugin/refactoring/move/moveUtils.kt | 166 +++++++++++++++--- .../references/JetSimpleNameReference.kt | 8 +- .../internalReferences/after/library/misc.kt | 7 + .../internalReferences/after/source/some.kt | 5 + .../internalReferences/after/target/A.kt | 21 +++ .../internalReferences/after/target/some.kt | 3 + .../internalReferences/before/library/misc.kt | 7 + .../internalReferences/before/source/A.kt | 21 +++ .../internalReferences/before/source/some.kt | 3 + .../internalReferences/before/target/some.kt | 3 + .../internalReferences.test | 5 + .../internalReferences/after/library/some.kt | 7 + .../internalReferences/after/source/some.kt | 5 + .../misc/internalReferences/after/target/A.kt | 16 ++ .../internalReferences/after/target/some.kt | 3 + .../internalReferences/before/library/some.kt | 7 + .../internalReferences/before/source/A.kt | 16 ++ .../internalReferences/before/source/some.kt | 3 + .../internalReferences/before/target/some.kt | 3 + .../internalReferences.test | 5 + .../move/JetMoveTestGenerated.java | 12 ++ 26 files changed, 366 insertions(+), 110 deletions(-) create mode 100644 annotations/com/intellij/refactoring/move/moveClassesOrPackages/annotations.xml create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/library/misc.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/source/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/target/A.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/target/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/library/misc.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/source/A.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/source/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/target/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveFile/internalReferences/internalReferences.test create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/library/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/source/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/target/A.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/target/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/library/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/source/A.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/source/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/target/some.kt create mode 100644 idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/internalReferences.test diff --git a/annotations/com/intellij/refactoring/move/moveClassesOrPackages/annotations.xml b/annotations/com/intellij/refactoring/move/moveClassesOrPackages/annotations.xml new file mode 100644 index 00000000000..e14982837b4 --- /dev/null +++ b/annotations/com/intellij/refactoring/move/moveClassesOrPackages/annotations.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/JetFileReferencesResolver.kt b/idea/src/org/jetbrains/jet/plugin/codeInsight/JetFileReferencesResolver.kt index 78ccb2902bb..6a6669111a5 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/JetFileReferencesResolver.kt +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/JetFileReferencesResolver.kt @@ -29,25 +29,28 @@ import org.jetbrains.jet.lang.psi.JetCallExpression import org.jetbrains.jet.lang.psi.JetSimpleNameExpression import java.util.Collections import org.jetbrains.jet.plugin.caches.resolve.getLazyResolveSession +import org.jetbrains.jet.lang.psi.JetQualifiedExpression +import org.jetbrains.jet.lang.descriptors.ClassDescriptor +import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor object JetFileReferencesResolver { fun resolve( element: JetElement, - visitReceivers: Boolean = true, - visitShortNames: Boolean = true + resolveQualifiers: Boolean = true, + resolveShortNames: Boolean = true ): Map { return (element.getContainingFile() as? JetFile)?.let { file -> - resolve(file, listOf(element), visitReceivers, visitShortNames) + resolve(file, listOf(element), resolveQualifiers, resolveShortNames) } ?: Collections.emptyMap() } fun resolve( file: JetFile, elements: Iterable? = null, - visitReceivers: Boolean = true, - visitShortNames: Boolean = true + resolveQualifiers: Boolean = true, + resolveShortNames: Boolean = true ): Map { - val visitor = ResolveAllReferencesVisitor(file, visitReceivers, visitShortNames) + val visitor = ResolveAllReferencesVisitor(file, resolveQualifiers, resolveShortNames) if (elements != null) { elements.forEach { it.accept(visitor) } } @@ -57,18 +60,18 @@ object JetFileReferencesResolver { return visitor.result } - private class ResolveAllReferencesVisitor(file: JetFile, val visitReceivers: Boolean, val visitShortNames: Boolean) : JetTreeVisitorVoid() { + private class ResolveAllReferencesVisitor(file: JetFile, val resolveQualifiers: Boolean, val resolveShortNames: Boolean) : JetTreeVisitorVoid() { private val resolveSession = file.getLazyResolveSession() private val resolveMap = HashMap() public val result: Map = resolveMap override fun visitUserType(userType: JetUserType) { - if (visitReceivers) { + if (resolveQualifiers) { userType.acceptChildren(this) } - if (visitShortNames || userType.getQualifier() != null) { + if (resolveShortNames || userType.getQualifier() != null) { val referenceExpression = userType.getReferenceExpression() if (referenceExpression != null) { resolveMap[referenceExpression] = resolveSession.resolveToElement(referenceExpression) @@ -76,21 +79,24 @@ object JetFileReferencesResolver { } } - override fun visitDotQualifiedExpression(expression: JetDotQualifiedExpression) { - if (visitReceivers) { - expression.acceptChildren(this) - } + private fun JetExpression.isReceiver(): Boolean { + val parent = getParent() + if (parent !is JetQualifiedExpression) return false + if (parent.getReceiverExpression() == this) return true - val referenceExpression = expression.getSelectorExpression()?.referenceExpression() - if (referenceExpression != null) { - resolveMap[referenceExpression] = resolveSession.resolveToElement(referenceExpression) - } + val parentParent = parent.getParent() + return parentParent is JetQualifiedExpression && parentParent.getReceiverExpression() == parent } override fun visitSimpleNameExpression(expression: JetSimpleNameExpression) { - if (visitShortNames) { - resolveMap[expression] = resolveSession.resolveToElement(expression) + val context = resolveSession.resolveToElement(expression) + val descriptor = context[BindingContext.REFERENCE_TARGET, expression] + if ((descriptor is ClassDescriptor || descriptor is PackageViewDescriptor) && expression.isReceiver()) { + if (!resolveQualifiers) return } + if (!resolveShortNames) return + + resolveMap[expression] = context } } } diff --git a/idea/src/org/jetbrains/jet/plugin/codeInsight/ShortenReferences.kt b/idea/src/org/jetbrains/jet/plugin/codeInsight/ShortenReferences.kt index 29dc3cbf432..2652ef40dae 100644 --- a/idea/src/org/jetbrains/jet/plugin/codeInsight/ShortenReferences.kt +++ b/idea/src/org/jetbrains/jet/plugin/codeInsight/ShortenReferences.kt @@ -101,7 +101,7 @@ public object ShortenReferences { ImportInsertHelper.optimizeImportsIfNeeded(file) // first resolve all qualified references - optimization - val referenceToContext = JetFileReferencesResolver.resolve(file, fileElements, visitShortNames = false) + val referenceToContext = JetFileReferencesResolver.resolve(file, fileElements, resolveShortNames = false) val shortenTypesVisitor = ShortenTypesVisitor(file, elementFilter, referenceToContext) processElements(fileElements, shortenTypesVisitor) diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt index 3f0da644222..31593e32b71 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt @@ -25,7 +25,6 @@ import com.intellij.openapi.roots.JavaProjectRootsUtil import com.intellij.psi.PsiCompiledElement import org.jetbrains.jet.lang.psi.JetFile import java.util.ArrayList -import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.util.MoveRenameUsageInfo import org.jetbrains.jet.lang.psi.JetNamedDeclaration @@ -39,7 +38,6 @@ import org.jetbrains.jet.lang.psi.psiUtil.getPackage import org.jetbrains.jet.plugin.references.JetReference import org.jetbrains.jet.asJava.toLightElements import org.jetbrains.jet.lang.psi.JetDeclaration -import org.jetbrains.jet.lang.psi.JetPsiUtil import com.intellij.openapi.util.TextRange import com.intellij.openapi.diagnostic.Logger import org.jetbrains.jet.lang.psi.JetClassOrObject @@ -48,12 +46,12 @@ import org.jetbrains.jet.lang.resolve.DescriptorUtils import com.intellij.refactoring.util.TextOccurrencesUtil import java.util.Collections import org.jetbrains.jet.plugin.refactoring.move.PackageNameInfo -import org.jetbrains.jet.plugin.refactoring.move.updateInternalReferencesOnPackageNameChange import org.jetbrains.jet.plugin.search.projectScope import org.jetbrains.jet.plugin.search.fileScope -import org.jetbrains.jet.plugin.search.not -import org.jetbrains.jet.plugin.search.and import org.jetbrains.jet.plugin.search.minus +import org.jetbrains.jet.plugin.refactoring.move.getInternalReferencesToUpdateOnPackageNameChange +import org.jetbrains.jet.plugin.refactoring.move.postProcessMoveUsages +import org.jetbrains.jet.plugin.codeInsight.addToShorteningWaitSet public class MoveKotlinFileHandler : MoveFileHandler() { class object { @@ -181,7 +179,8 @@ public class MoveKotlinFileHandler : MoveFileHandler() { val packageNameInfo = file.getAndRemoveCopyableUserData(PACKAGE_NAME_INFO_KEY) if (packageNameInfo == null) return - file.updateInternalReferencesOnPackageNameChange(packageNameInfo, updateImportedReferences = false) + val usages = file.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) + postProcessMoveUsages(usages) val packageRef = file.getPackageDirective()?.getLastReferenceExpression()?.getReference() as? JetSimpleNameReference packageRef?.bindToFqName(packageNameInfo.newPackageName) diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt index 0e7bbce687b..0276a5330d0 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveTopLevelDeclarations/MoveKotlinTopLevelDeclarationsProcessor.kt @@ -27,7 +27,6 @@ import com.intellij.usageView.UsageViewUtil import com.intellij.openapi.diagnostic.Logger import com.intellij.psi.PsiElement import com.intellij.refactoring.util.NonCodeUsageInfo -import com.intellij.refactoring.move.moveClassesOrPackages.CommonMoveUtil import com.intellij.util.IncorrectOperationException import com.intellij.refactoring.util.RefactoringUIUtil import org.jetbrains.jet.utils.keysToMap @@ -39,7 +38,6 @@ import org.jetbrains.jet.lang.psi.psiUtil.getPackage import org.jetbrains.jet.lang.psi.JetFile import org.jetbrains.jet.plugin.refactoring.move.PackageNameInfo import org.jetbrains.jet.plugin.refactoring.createKotlinFile -import org.jetbrains.jet.plugin.refactoring.move.updateInternalReferencesOnPackageNameChange import org.jetbrains.jet.plugin.codeInsight.addToShorteningWaitSet import org.jetbrains.jet.plugin.refactoring.move.getFileNameAfterMove import org.jetbrains.jet.lang.psi.JetNamedDeclaration @@ -67,13 +65,13 @@ import com.intellij.psi.PsiModifier import com.intellij.util.VisibilityUtil import com.intellij.openapi.util.Ref import org.jetbrains.jet.plugin.refactoring.getKotlinFqName -import org.jetbrains.jet.plugin.references.JetSimpleNameReference.ShorteningMode import org.jetbrains.jet.plugin.search.projectScope -import org.jetbrains.jet.lang.psi.psiUtil.isExtensionDeclaration -import org.jetbrains.jet.plugin.references.JetReference -import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper -import org.jetbrains.jet.lang.psi.psiUtil.getParentByType -import org.jetbrains.jet.lang.psi.JetImportDirective +import org.jetbrains.jet.plugin.refactoring.move.getInternalReferencesToUpdateOnPackageNameChange +import org.jetbrains.jet.plugin.refactoring.move.createMoveUsageInfo +import org.jetbrains.jet.plugin.refactoring.move.postProcessMoveUsages +import org.jetbrains.jet.plugin.references.JetSimpleNameReference.ShorteningMode +import org.jetbrains.jet.lang.psi.psiUtil.isAncestor +import org.jetbrains.jet.plugin.refactoring.move.MoveRenameUsageInfoForExtension public class MoveKotlinTopLevelDeclarationsOptions( val elementsToMove: Collection, @@ -83,14 +81,6 @@ public class MoveKotlinTopLevelDeclarationsOptions( val moveCallback: MoveCallback? = null ) -class MoveRenameUsageInfoForExtension( - element: PsiElement, - reference: PsiReference, - startOffset: Int, - endOffset: Int, - referencedElement: PsiElement -): MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false) - public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val options: MoveKotlinTopLevelDeclarationsOptions) : BaseRefactoringProcessor(project) { class object { private val LOG: Logger = Logger.getInstance(javaClass()) @@ -111,21 +101,6 @@ public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val optio } override fun findUsages(): Array { - fun createUsageInfo( - element: PsiElement, - reference: PsiReference, - startOffset: Int, - endOffset: Int, - referencedElement: PsiElement - ): UsageInfo { - if (reference is JetReference - && referencedElement.namedUnwrappedElement!!.isExtensionDeclaration() - && element.getParentByType(javaClass()) == null) { - return MoveRenameUsageInfoForExtension(element, reference, startOffset, endOffset, referencedElement) - } - return MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false) - } - val newPackageName = options.moveTarget.packageWrapper?.getQualifiedName() ?: "" fun collectUsages(): List { @@ -137,9 +112,8 @@ public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val optio val results = ReferencesSearch .search(lightElement, projectScope, false) .mapTo(ArrayList()) { ref -> - if (foundReferences.add(ref)) { - val range = ref.getRangeInElement()!! - createUsageInfo(ref.getElement(), ref, range.getStartOffset(), range.getEndOffset(), lightElement) + if (foundReferences.add(ref) && elementsToMove.all { !it.isAncestor(ref.getElement())}) { + createMoveUsageInfo(ref, lightElement, true) } else null } @@ -201,7 +175,7 @@ public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val optio fun collectConflictsInDeclarations() { val declarationToReferenceTargets = HashMap>() for (declaration in elementsToMove) { - val referenceToContext = JetFileReferencesResolver.resolve(element = declaration, visitReceivers = false) + val referenceToContext = JetFileReferencesResolver.resolve(element = declaration, resolveQualifiers = false) for ((refExpr, bindingContext) in referenceToContext) { val refTarget = bindingContext[BindingContext.REFERENCE_TARGET, refExpr]?.let { descriptor -> DescriptorToDeclarationUtil.getDeclaration(declaration.getProject(), descriptor) @@ -246,7 +220,11 @@ public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val optio } override fun performRefactoring(usages: Array?) { - fun moveDeclaration(declaration: JetNamedDeclaration, moveTarget: KotlinMoveTarget): JetNamedDeclaration? { + fun moveDeclaration( + declaration: JetNamedDeclaration, + moveTarget: KotlinMoveTarget, + usagesToProcessAfterMove: MutableList + ): JetNamedDeclaration? { val file = declaration.getContainingFile() as? JetFile assert (file != null, "${declaration.javaClass}: ${declaration.getText()}") @@ -265,14 +243,12 @@ public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val optio assert(targetFile is JetFile, "Couldn't create Koltin file for: ${declaration.javaClass}: ${declaration.getText()}") - val newPackageFqName = (targetFile as JetFile).getPackageFqName() - - val packageNameInfo = PackageNameInfo(file!!.getPackageFqName(), newPackageFqName) - declaration.updateInternalReferencesOnPackageNameChange( - packageNameInfo, - updateImportedReferences = true, - shorteningMode = ShorteningMode.NO_SHORTENING - ) + val packageNameInfo = PackageNameInfo(file!!.getPackageFqName(), (targetFile as JetFile).getPackageFqName()) + val (usagesToProcessLater, usagesToProcessNow) = declaration + .getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) + .partition { it is MoveRenameUsageInfoForExtension } + postProcessMoveUsages(usagesToProcessNow, shorteningMode = ShorteningMode.NO_SHORTENING) + usagesToProcessAfterMove.addAll(usagesToProcessLater) val newElement = targetFile.add(declaration) as JetNamedDeclaration declaration.delete() @@ -283,9 +259,13 @@ public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val optio } try { + val usageList = usages.toArrayList() + val oldToNewElementsMapping = HashMap() for ((oldDeclaration, oldLightElements) in kotlinToLightElements) { - val newDeclaration = moveDeclaration(oldDeclaration, options.moveTarget) + val oldFile = oldDeclaration.getContainingJetFile() + + val newDeclaration = moveDeclaration(oldDeclaration, options.moveTarget, usageList) if (newDeclaration == null) { for (oldElement in oldLightElements) { oldToNewElementsMapping[oldElement] = oldElement @@ -293,26 +273,15 @@ public class MoveKotlinTopLevelDeclarationsProcessor(project: Project, val optio continue } + oldToNewElementsMapping[oldFile] = newDeclaration.getContainingJetFile() + getTransaction()!!.getElementListener(oldDeclaration).elementMoved(newDeclaration) for ((oldElement, newElement) in oldLightElements.stream() zip newDeclaration.toLightElements().stream()) { oldToNewElementsMapping[oldElement] = newElement } } - if (usages == null) return - - nonCodeUsages = CommonMoveUtil.retargetUsages( - usages.filter { it !is MoveRenameUsageInfoForExtension }.copyToArray(), - oldToNewElementsMapping - ) - for (usage in usages.filterIsInstance(javaClass())) { - val newElement = oldToNewElementsMapping[usage.getReferencedElement()] - - ImportInsertHelper.addImportDirectiveIfNeeded( - newElement!!.getKotlinFqName()!!, - usage.getElement()!!.getContainingFile() as JetFile - ) - } + nonCodeUsages = postProcessMoveUsages(usageList, oldToNewElementsMapping).copyToArray() } catch (e: IncorrectOperationException) { nonCodeUsages = null diff --git a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveUtils.kt b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveUtils.kt index 4dda11df8c9..da71ad067b3 100644 --- a/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveUtils.kt +++ b/idea/src/org/jetbrains/jet/plugin/refactoring/move/moveUtils.kt @@ -26,7 +26,6 @@ import org.jetbrains.jet.plugin.references.JetSimpleNameReference import org.jetbrains.jet.lang.resolve.name.FqName import org.jetbrains.jet.lang.psi.JetFile import org.jetbrains.jet.lang.psi.JetElement -import org.jetbrains.jet.plugin.references.JetSimpleNameReference.ShorteningMode import org.jetbrains.jet.plugin.JetFileType import org.jetbrains.jet.lang.psi.JetNamedDeclaration import org.jetbrains.jet.plugin.imports.canBeReferencedViaImport @@ -34,30 +33,54 @@ import org.jetbrains.jet.plugin.codeInsight.DescriptorToDeclarationUtil import org.jetbrains.jet.lang.psi.psiUtil.isAncestor import java.util.Collections import org.jetbrains.jet.lang.resolve.name.isImported +import org.jetbrains.jet.lang.descriptors.DeclarationDescriptor +import org.jetbrains.jet.lang.descriptors.ClassDescriptor +import org.jetbrains.jet.lang.descriptors.PackageViewDescriptor +import com.intellij.usageView.UsageInfo +import org.jetbrains.jet.lang.psi.psiUtil.isExtensionDeclaration +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiReference +import com.intellij.refactoring.util.MoveRenameUsageInfo +import org.jetbrains.jet.plugin.references.JetReference +import org.jetbrains.jet.asJava.namedUnwrappedElement +import org.jetbrains.jet.lang.psi.psiUtil.getParentByType +import org.jetbrains.jet.lang.psi.JetImportDirective +import java.util.ArrayList +import com.intellij.refactoring.util.NonCodeUsageInfo +import org.jetbrains.jet.plugin.quickfix.ImportInsertHelper +import org.jetbrains.jet.plugin.refactoring.getKotlinFqName +import org.jetbrains.jet.lang.psi.JetThisExpression +import org.jetbrains.jet.plugin.references.JetSimpleNameReference.ShorteningMode +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.util.Comparing +import java.util.Comparator +import com.intellij.util.IncorrectOperationException +import com.intellij.psi.PsiFile public class PackageNameInfo(val oldPackageName: FqName, val newPackageName: FqName) -public fun JetElement.updateInternalReferencesOnPackageNameChange( - packageNameInfo: PackageNameInfo, - updateImportedReferences: Boolean, - shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING -) { +public fun JetElement.getInternalReferencesToUpdateOnPackageNameChange(packageNameInfo: PackageNameInfo): List { val file = getContainingFile() as? JetFile - if (file == null) return + if (file == null) return Collections.emptyList() - val importPaths = - if (updateImportedReferences) { - file.getImportDirectives().map { it.getImportPath() }.filterNotNull() - } - else Collections.emptyList() + val importPaths = file.getImportDirectives().map { it.getImportPath() }.filterNotNull() - fun isImportedName(fqName: FqName): Boolean = - updateImportedReferences && importPaths.any { fqName.isImported(it, false) } + [tailRecursive] fun isImported(descriptor: DeclarationDescriptor): Boolean { + val fqName = DescriptorUtils.getFqName(descriptor).let { if (it.isSafe()) it.toSafe() else return@isImported false } + if (importPaths.any { fqName.isImported(it, false) }) return true - val referenceToContext = JetFileReferencesResolver.resolve(file = file, elements = listOf(this), visitReceivers = false) + val containingDescriptor = descriptor.getContainingDeclaration() + return when (containingDescriptor) { + is ClassDescriptor, is PackageViewDescriptor -> isImported(containingDescriptor) + else -> false + } + } + val referenceToContext = JetFileReferencesResolver.resolve(file = file, elements = listOf(this), resolveQualifiers = false) + + val usages = ArrayList() for ((refExpr, bindingContext) in referenceToContext) { - if (refExpr !is JetSimpleNameExpression) continue + if (refExpr !is JetSimpleNameExpression || refExpr.getParent() is JetThisExpression) continue val descriptor = bindingContext[BindingContext.REFERENCE_TARGET, refExpr]?.let { descriptor -> if (descriptor is ConstructorDescriptor) descriptor.getContainingDeclaration() else descriptor @@ -70,23 +93,122 @@ public fun JetElement.updateInternalReferencesOnPackageNameChange( val fqName = DescriptorUtils.getFqName(descriptor) if (!fqName.isSafe()) continue - val fqNameSafe = fqName.toSafe() - val packageName = DescriptorUtils.getParentOfType( - descriptor, javaClass(), false - )?.let { DescriptorUtils.getFqName(it).toSafe() } + val packageName = DescriptorUtils.getParentOfType(descriptor, javaClass(), false)?.let { + DescriptorUtils.getFqName(it).toSafe() + } when { + declaration.isExtensionDeclaration(), packageName == packageNameInfo.oldPackageName, packageName == packageNameInfo.newPackageName, - isImportedName(fqNameSafe) -> { - (refExpr.getReference() as? JetSimpleNameReference)?.bindToFqName(fqNameSafe, shorteningMode) + isImported(descriptor) -> { + (refExpr.getReference() as? JetSimpleNameReference)?.let { usages.add(createMoveUsageInfo(it, declaration, false)) } } } } + + return usages +} + +class MoveRenameUsageInfoForExtension( + element: PsiElement, + reference: PsiReference, + startOffset: Int, + endOffset: Int, + referencedElement: PsiElement, + val originalFile: PsiFile, + val addImportToOriginalFile: Boolean +): MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false) + +fun createMoveUsageInfo( + reference: PsiReference, + referencedElement: PsiElement, + addImportToOriginalFile: Boolean +): UsageInfo { + val range = reference.getRangeInElement()!! + val element = reference.getElement() + val startOffset = range.getStartOffset() + val endOffset = range.getEndOffset() + + if (reference is JetReference + && referencedElement.namedUnwrappedElement!!.isExtensionDeclaration() + && element.getParentByType(javaClass()) == null) { + return MoveRenameUsageInfoForExtension( + element, reference, startOffset, endOffset, referencedElement, element.getContainingFile()!!, addImportToOriginalFile + ) + } + return MoveRenameUsageInfo(element, reference, startOffset, endOffset, referencedElement, false) } public fun JetNamedDeclaration.getFileNameAfterMove(): String? { return (getContainingFile() as? JetFile)?.let { file -> if (file.getDeclarations().size > 1) "${getName()}.${JetFileType.INSTANCE.getDefaultExtension()}" else file.getName() } +} + +/** + * Perform usage postprocessing and return non-code usages + */ +fun postProcessMoveUsages(usages: List, + oldToNewElementsMapping: Map = Collections.emptyMap(), + shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING +): List { + fun counterpart(e: PsiElement) = oldToNewElementsMapping[e] ?: e + + val sortedUsages = usages.sortBy( + object : Comparator { + override fun compare(o1: UsageInfo, o2: UsageInfo): Int { + val file1 = o1.getVirtualFile() + val file2 = o2.getVirtualFile() + if (Comparing.equal(file1, file2)) { + val rangeInElement1 = o1.getRangeInElement() + val rangeInElement2 = o2.getRangeInElement() + if (rangeInElement1 != null && rangeInElement2 != null) { + return rangeInElement2.getStartOffset() - rangeInElement1.getStartOffset() + } + return 0 + } + if (file1 == null) return -1 + if (file2 == null) return 1 + return Comparing.compare(file1.getPath(), file2.getPath()) + } + } + ) + + val nonCodeUsages = ArrayList() + + for (usage in sortedUsages) { + when (usage) { + is NonCodeUsageInfo -> { + nonCodeUsages.add(usage as NonCodeUsageInfo) + } + + is MoveRenameUsageInfoForExtension -> { + val element = counterpart(usage.getReferencedElement()!!) + val file = with(usage) { if (addImportToOriginalFile) originalFile else counterpart(originalFile) } as JetFile + ImportInsertHelper.addImportDirectiveIfNeeded(element.getKotlinFqName()!!, file) + } + + is MoveRenameUsageInfo -> { + val moveRenameUsage = usage as MoveRenameUsageInfo + val oldElement = moveRenameUsage.getReferencedElement()!! + val newElement = counterpart(oldElement) + moveRenameUsage.getReference()?.let { + try { + if (it is JetSimpleNameReference) { + it.bindToElement(newElement, shorteningMode) + } + else { + it.bindToElement(newElement) + } + } + catch (e: IncorrectOperationException) { + // Suppress exception if bindToElement is not implemented + } + } + } + } + } + + return nonCodeUsages } \ No newline at end of file diff --git a/idea/src/org/jetbrains/jet/plugin/references/JetSimpleNameReference.kt b/idea/src/org/jetbrains/jet/plugin/references/JetSimpleNameReference.kt index 20d2a5a5a48..2745dac6e53 100644 --- a/idea/src/org/jetbrains/jet/plugin/references/JetSimpleNameReference.kt +++ b/idea/src/org/jetbrains/jet/plugin/references/JetSimpleNameReference.kt @@ -105,9 +105,11 @@ public class JetSimpleNameReference( } // By default reference binding is delayed - override fun bindToElement(element: PsiElement): PsiElement { - return element.getKotlinFqName()?.let { fqName -> bindToFqName(fqName) } ?: expression - } + override fun bindToElement(element: PsiElement): PsiElement = + bindToElement(element, ShorteningMode.DELAYED_SHORTENING) + + fun bindToElement(element: PsiElement, shorteningMode: ShorteningMode): PsiElement = + element.getKotlinFqName()?.let { fqName -> bindToFqName(fqName, shorteningMode) } ?: expression public fun bindToFqName(fqName: FqName, shorteningMode: ShorteningMode = ShorteningMode.DELAYED_SHORTENING): PsiElement { if (fqName.isRoot()) return expression diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/library/misc.kt b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/library/misc.kt new file mode 100644 index 00000000000..31450ddae4a --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/library/misc.kt @@ -0,0 +1,7 @@ +package library + +open public class B { + fun foo(): B = this +} + +fun B.bar(): B = this \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/source/some.kt b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/source/some.kt new file mode 100644 index 00000000000..86569cdb0fa --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/source/some.kt @@ -0,0 +1,5 @@ +package source + +import target.A + +fun test1() = target.test(A()) \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/target/A.kt b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/target/A.kt new file mode 100644 index 00000000000..4cb9c1b227b --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/target/A.kt @@ -0,0 +1,21 @@ +package target + +import library.B +import library.bar + +public class A: B() { + fun test() { + bar() + foo() + this.bar() + this.foo() + } +} + +public fun test(a: A) { + a.bar() + a.foo() + B().foo().bar() + B().foo().bar() + B().bar().foo() +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/target/some.kt b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/target/some.kt new file mode 100644 index 00000000000..9faf72a450b --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/after/target/some.kt @@ -0,0 +1,3 @@ +package target + +fun test2() = test(A()) \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/library/misc.kt b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/library/misc.kt new file mode 100644 index 00000000000..31450ddae4a --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/library/misc.kt @@ -0,0 +1,7 @@ +package library + +open public class B { + fun foo(): B = this +} + +fun B.bar(): B = this \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/source/A.kt b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/source/A.kt new file mode 100644 index 00000000000..f077d04543c --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/source/A.kt @@ -0,0 +1,21 @@ +package source + +import library.B +import library.bar + +public class A: B() { + fun test() { + bar() + foo() + this.bar() + this.foo() + } +} + +public fun test(a: A) { + a.bar() + a.foo() + B().foo().bar() + library.B().foo().bar() + B().bar().foo() +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/source/some.kt b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/source/some.kt new file mode 100644 index 00000000000..b2d8e884024 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/source/some.kt @@ -0,0 +1,3 @@ +package source + +fun test1() = test(A()) \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/target/some.kt b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/target/some.kt new file mode 100644 index 00000000000..e1eebd508c2 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/before/target/some.kt @@ -0,0 +1,3 @@ +package target + +fun test2() = source.test(source.A()) \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/internalReferences.test b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/internalReferences.test new file mode 100644 index 00000000000..959a5db15c4 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveFile/internalReferences/internalReferences.test @@ -0,0 +1,5 @@ +{ + "mainFile": "source/A.kt", + "type": "MOVE_FILES", + "targetPackage": "target" +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/library/some.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/library/some.kt new file mode 100644 index 00000000000..31450ddae4a --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/library/some.kt @@ -0,0 +1,7 @@ +package library + +open public class B { + fun foo(): B = this +} + +fun B.bar(): B = this \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/source/some.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/source/some.kt new file mode 100644 index 00000000000..0ca538772f8 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/source/some.kt @@ -0,0 +1,5 @@ +package source + +import target.A + +fun test1() = A() \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/target/A.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/target/A.kt new file mode 100644 index 00000000000..8a61c2a9e87 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/target/A.kt @@ -0,0 +1,16 @@ +package target + +import library.bar +import library.B + +public class A: B() { + fun test() { + bar() + foo() + this.bar() + this.foo() + B().foo().bar() + B().foo().bar() + B().bar().foo() + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/target/some.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/target/some.kt new file mode 100644 index 00000000000..6269bc9f9cf --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/after/target/some.kt @@ -0,0 +1,3 @@ +package target + +fun test2() = A() \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/library/some.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/library/some.kt new file mode 100644 index 00000000000..31450ddae4a --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/library/some.kt @@ -0,0 +1,7 @@ +package library + +open public class B { + fun foo(): B = this +} + +fun B.bar(): B = this \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/source/A.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/source/A.kt new file mode 100644 index 00000000000..19115fece2f --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/source/A.kt @@ -0,0 +1,16 @@ +package source + +import library.B +import library.bar + +public class A: B() { + fun test() { + bar() + foo() + this.bar() + this.foo() + B().foo().bar() + library.B().foo().bar() + B().bar().foo() + } +} \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/source/some.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/source/some.kt new file mode 100644 index 00000000000..6d16d8d51cf --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/source/some.kt @@ -0,0 +1,3 @@ +package source + +fun test1() = A() \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/target/some.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/target/some.kt new file mode 100644 index 00000000000..e72b95320bb --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/before/target/some.kt @@ -0,0 +1,3 @@ +package target + +fun test2() = source.A() \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/internalReferences.test b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/internalReferences.test new file mode 100644 index 00000000000..69190fd7e7f --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/internalReferences.test @@ -0,0 +1,5 @@ +{ + "mainFile": "source/A.kt", + "type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS", + "targetPackage": "target" +} diff --git a/idea/tests/org/jetbrains/jet/plugin/refactoring/move/JetMoveTestGenerated.java b/idea/tests/org/jetbrains/jet/plugin/refactoring/move/JetMoveTestGenerated.java index a35730169cd..dd5a067eab9 100644 --- a/idea/tests/org/jetbrains/jet/plugin/refactoring/move/JetMoveTestGenerated.java +++ b/idea/tests/org/jetbrains/jet/plugin/refactoring/move/JetMoveTestGenerated.java @@ -212,6 +212,12 @@ public class JetMoveTestGenerated extends AbstractJetMoveTest { doTest(fileName); } + @TestMetadata("kotlin/moveFile/internalReferences/internalReferences.test") + public void testKotlin_moveFile_internalReferences_InternalReferences() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveFile/internalReferences/internalReferences.test"); + doTest(fileName); + } + @TestMetadata("kotlin/moveFile/moveFileToFile/moveFileToFile.test") public void testKotlin_moveFile_moveFileToFile_MoveFileToFile() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveFile/moveFileToFile/moveFileToFile.test"); @@ -236,6 +242,12 @@ public class JetMoveTestGenerated extends AbstractJetMoveTest { doTest(fileName); } + @TestMetadata("kotlin/moveTopLevelDeclarations/misc/internalReferences/internalReferences.test") + public void testKotlin_moveTopLevelDeclarations_misc_internalReferences_InternalReferences() throws Exception { + String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/internalReferences/internalReferences.test"); + doTest(fileName); + } + @TestMetadata("kotlin/moveTopLevelDeclarations/misc/moveClassWithImportsRetained/moveClassWithImportsRetained.test") public void testKotlin_moveTopLevelDeclarations_misc_moveClassWithImportsRetained_MoveClassWithImportsRetained() throws Exception { String fileName = JetTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveClassWithImportsRetained/moveClassWithImportsRetained.test");