diff --git a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt index f39d4ea5180..87661cd9879 100644 --- a/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt +++ b/core/descriptors/src/org/jetbrains/kotlin/resolve/DescriptorUtils.kt @@ -401,3 +401,8 @@ fun DeclarationDescriptor.isPublishedApi(): Boolean { val descriptor = if (this is CallableMemberDescriptor) DescriptorUtils.getDirectMember(this) else this return descriptor.annotations.hasAnnotation(KotlinBuiltIns.FQ_NAMES.publishedApi) } + +fun DeclarationDescriptor.isAncestorOf(descriptor: DeclarationDescriptor, strict: Boolean): Boolean + = DescriptorUtils.isAncestor(this, descriptor, strict) + +fun DeclarationDescriptor.isCompanionObject(): Boolean = DescriptorUtils.isCompanionObject(this) \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberOutOfCompanionObjectIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberOutOfCompanionObjectIntention.kt index cc629d431f0..f1c1901a700 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberOutOfCompanionObjectIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/MoveMemberOutOfCompanionObjectIntention.kt @@ -58,10 +58,11 @@ class MoveMemberOutOfCompanionObjectIntention : SelfTargetingRangeIntention + element.processInternalReferencesToUpdateOnPackageNameChange(packageNameInfo) { expr, factory -> val infos = expr.internalUsageInfos ?: LinkedHashMap UsageInfo?>().apply { expr.internalUsageInfos = this } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt index 3f5aa739541..f2c7c175436 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/KotlinChangePackageRefactoring.kt @@ -40,8 +40,8 @@ class KotlinChangePackageRefactoring(val file: KtFile) { val currentFqName = packageDirective.fqName val declarationProcessor = MoveKotlinDeclarationsProcessor( - project, MoveDeclarationsDescriptor( + project = project, elementsToMove = file.declarations.filterIsInstance(), moveTarget = object: KotlinDirectoryBasedMoveTarget { override val targetContainerFqName = newFqName diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationToSeparateFileIntention.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationToSeparateFileIntention.kt index 376cba7da66..aa9cce1020f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationToSeparateFileIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationToSeparateFileIntention.kt @@ -93,7 +93,8 @@ class MoveDeclarationToSeparateFileIntention : val moveTarget = KotlinMoveTargetForDeferredFile(packageName, directory, null) { createKotlinFile(targetFileName, directory, packageName.asString()) } - val moveOptions = MoveDeclarationsDescriptor( + val descriptor = MoveDeclarationsDescriptor( + project = project, elementsToMove = listOf(element), moveTarget = moveTarget, delegate = MoveDeclarationsDelegate.TopLevel, @@ -107,7 +108,7 @@ class MoveDeclarationToSeparateFileIntention : } ) - val move = { MoveKotlinDeclarationsProcessor(project, moveOptions).run() } + val move = { MoveKotlinDeclarationsProcessor(descriptor).run() } val optimizeImports = { OptimizeImportsProcessor(project, file).run() } move.runRefactoringWithPostprocessing(project, MoveKotlinDeclarationsProcessor.REFACTORING_ID, optimizeImports) diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt index 42de851dd1f..a4ceac24534 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveDeclarationsDelegate.kt @@ -16,7 +16,6 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations -import com.intellij.openapi.project.Project import com.intellij.psi.PsiElement import com.intellij.refactoring.move.moveInner.MoveInnerClassUsagesHandler import com.intellij.refactoring.util.MoveRenameUsageInfo @@ -35,37 +34,30 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject sealed class MoveDeclarationsDelegate { abstract fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo - abstract fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List - abstract fun collectConflicts( + + open fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List = emptyList() + + open fun collectConflicts( descriptor: MoveDeclarationsDescriptor, internalUsages: MutableSet, conflicts: MultiMap - ) - abstract fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) - abstract fun preprocessUsages(project: Project, descriptor: MoveDeclarationsDescriptor, usages: List) + ) { + + } + + open fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) { + + } + + open fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List) { + + } object TopLevel : MoveDeclarationsDelegate() { override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo { - return ContainerChangeInfo(ContainerInfo.Package(originalDeclaration.containingKtFile.packageFqName), - ContainerInfo.Package(moveTarget.targetContainerFqName!!)) - } - - override fun findInternalUsages(descriptor: MoveDeclarationsDescriptor): List = emptyList() - - override fun collectConflicts( - descriptor: MoveDeclarationsDescriptor, - internalUsages: MutableSet, - conflicts: MultiMap - ) { - - } - - override fun preprocessDeclaration(descriptor: MoveDeclarationsDescriptor, originalDeclaration: KtNamedDeclaration) { - - } - - override fun preprocessUsages(project: Project, descriptor: MoveDeclarationsDescriptor, usages: List) { - + val sourcePackage = ContainerInfo.Package(originalDeclaration.containingKtFile.packageFqName) + val targetPackage = moveTarget.targetContainerFqName?.let { ContainerInfo.Package(it) } ?: ContainerInfo.UnknownPackage + return ContainerChangeInfo(sourcePackage, targetPackage) } } @@ -76,10 +68,11 @@ sealed class MoveDeclarationsDelegate { override fun getContainerChangeInfo(originalDeclaration: KtNamedDeclaration, moveTarget: KotlinMoveTarget): ContainerChangeInfo { val originalInfo = ContainerInfo.Class(originalDeclaration.containingClassOrObject!!.fqName!!) val movingToClass = (moveTarget as? KotlinMoveTargetForExistingElement)?.targetElement is KtClassOrObject - val newInfo = if (movingToClass) { - ContainerInfo.Class(moveTarget.targetContainerFqName!!) - } else { - ContainerInfo.Package(moveTarget.targetContainerFqName!!) + val targetContainerFqName = moveTarget.targetContainerFqName + val newInfo = when { + targetContainerFqName == null -> ContainerInfo.UnknownPackage + movingToClass -> ContainerInfo.Class(targetContainerFqName) + else -> ContainerInfo.Package(targetContainerFqName) } return ContainerChangeInfo(originalInfo, newInfo) } @@ -137,9 +130,9 @@ sealed class MoveDeclarationsDelegate { } } - override fun preprocessUsages(project: Project, descriptor: MoveDeclarationsDescriptor, usages: List) { + override fun preprocessUsages(descriptor: MoveDeclarationsDescriptor, usages: List) { if (outerInstanceParameterName == null) return - val psiFactory = KtPsiFactory(project) + val psiFactory = KtPsiFactory(descriptor.project) val newOuterInstanceRef = psiFactory.createExpression(outerInstanceParameterName) val classToMove = descriptor.elementsToMove.singleOrNull() as? KtClass diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt index 37dd6578390..f43b2ac74a7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/MoveKotlinDeclarationsProcessor.kt @@ -86,6 +86,7 @@ interface Mover : (KtNamedDeclaration, KtElement) -> KtNamedDeclaration { internal var KtSimpleNameExpression.internalUsageInfo: UsageInfo? by CopyableUserDataProperty(Key.create("INTERNAL_USAGE_INFO")) class MoveDeclarationsDescriptor( + val project: Project, val elementsToMove: Collection, val moveTarget: KotlinMoveTarget, val delegate: MoveDeclarationsDelegate, @@ -99,15 +100,35 @@ class MoveDeclarationsDescriptor( class ConflictUsageInfo(element: PsiElement, val messages: Collection) : UsageInfo(element) +private object ElementHashingStrategy : TObjectHashingStrategy { + override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean { + if (e1 === e2) return true + // Name should be enough to distinguish different light elements based on the same original declaration + if (e1 is KtLightElement<*, *> && e2 is KtLightElement<*, *>) { + return e1.kotlinOrigin == e2.kotlinOrigin && e1.name == e2.name + } + return e1 == e2 + } + + override fun computeHashCode(e: PsiElement?): Int { + return when (e) { + null -> 0 + is KtLightElement<*, *> -> (e.kotlinOrigin?.hashCode() ?: 0) * 31 + (e.name?.hashCode() ?: 0) + else -> e.hashCode() + } + } +} + class MoveKotlinDeclarationsProcessor( - val project: Project, val descriptor: MoveDeclarationsDescriptor, - val mover: Mover = Mover.Default) : BaseRefactoringProcessor(project) { + val mover: Mover = Mover.Default) : BaseRefactoringProcessor(descriptor.project) { companion object { private val REFACTORING_NAME = "Move declarations" val REFACTORING_ID = "move.kotlin.declarations" } + val project get() = descriptor.project + private var nonCodeUsages: Array? = null private val elementsToMove = descriptor.elementsToMove.filter { e -> e.parent != descriptor.moveTarget.getTargetPsiIfExists(e) } private val kotlinToLightElementsBySourceFile = elementsToMove @@ -132,16 +153,16 @@ class MoveKotlinDeclarationsProcessor( val newContainerName = descriptor.moveTarget.targetContainerFqName?.asString() ?: "" fun collectUsages(kotlinToLightElements: Map>, result: MutableCollection) { - kotlinToLightElements.values.flatMap { it }.flatMapTo(result) { lightElement -> + kotlinToLightElements.values.flatten().flatMapTo(result) { lightElement -> val newFqName = StringUtil.getQualifiedName(newContainerName, lightElement.name) val foundReferences = HashSet() - val projectScope = lightElement.project.projectScope() + val projectScope = project.projectScope() val results = ReferencesSearch - .search(lightElement, projectScope, false) + .search(lightElement, projectScope) .mapNotNullTo(ArrayList()) { ref -> - if (foundReferences.add(ref) && elementsToMove.all { !it.isAncestor(ref.element)}) { - createMoveUsageInfoIfPossible(ref, lightElement, true, null) + if (foundReferences.add(ref) && elementsToMove.none { it.isAncestor(ref.element)}) { + createMoveUsageInfoIfPossible(ref, lightElement, true, false) } else null } @@ -166,7 +187,6 @@ class MoveKotlinDeclarationsProcessor( } } - val usagesToProcessBeforeMove = LinkedHashSet() val usages = ArrayList() val conflictChecker = MoveConflictChecker(project, elementsToMove, descriptor.moveTarget, elementsToMove.first()) for ((sourceFile, kotlinToLightElements) in kotlinToLightElementsBySourceFile) { @@ -196,8 +216,6 @@ class MoveKotlinDeclarationsProcessor( usages += externalUsages } - descriptor.delegate.collectConflicts(descriptor, usagesToProcessBeforeMove, conflicts) - return UsageViewUtil.removeDuplicatedUsages(usages.toTypedArray()) } @@ -216,7 +234,7 @@ class MoveKotlinDeclarationsProcessor( } val usageList = usages.toList() - val (oldInternalUsages, externalUsages) = usageList.partition { (it as? InternableUsage)?.scope != null } + val (oldInternalUsages, externalUsages) = usageList.partition { it is KotlinMoveUsage && it.isInternal } val newInternalUsages = ArrayList() oldInternalUsages.forEach { (it.element as? KtSimpleNameExpression)?.internalUsageInfo = it } @@ -224,28 +242,9 @@ class MoveKotlinDeclarationsProcessor( val usagesToProcess = ArrayList(externalUsages) try { - descriptor.delegate.preprocessUsages(project, descriptor, usageList) + descriptor.delegate.preprocessUsages(descriptor, usageList) - val oldToNewElementsMapping = THashMap( - object: TObjectHashingStrategy { - override fun equals(e1: PsiElement?, e2: PsiElement?): Boolean { - if (e1 === e2) return true - // Name should be enough to distinguish different light elements based on the same original declaration - if (e1 is KtLightElement<*, *> && e2 is KtLightElement<*, *>) { - return e1.kotlinOrigin == e2.kotlinOrigin && e1.name == e2.name - } - return e1 == e2 - } - - override fun computeHashCode(e: PsiElement?): Int { - return when (e) { - null -> 0 - is KtLightElement<*, *> -> (e.kotlinOrigin?.hashCode() ?: 0) * 31 + (e.name?.hashCode() ?: 0) - else -> e.hashCode() - } - } - } - ) + val oldToNewElementsMapping = THashMap(ElementHashingStrategy) val newDeclarations = ArrayList() @@ -279,9 +278,9 @@ class MoveKotlinDeclarationsProcessor( 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 + val newReferencedElement = mapToNewOrThis(referencedElement, oldToNewElementsMapping) if (!newReferencedElement.isValid) return@mapNotNull null - (usageInfo as? InternableUsage)?.refresh(it, newReferencedElement) + (usageInfo as? KotlinMoveUsage)?.refresh(it, newReferencedElement) } } @@ -295,10 +294,7 @@ class MoveKotlinDeclarationsProcessor( } finally { (newInternalUsages + oldInternalUsages).forEach { - val element = it.element as? KtSimpleNameExpression ?: return@forEach - if (element.isValid) { - element.internalUsageInfo = null - } + (it.element as? KtSimpleNameExpression)?.internalUsageInfo = null } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesDialog.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesDialog.java index 1a9a7f74b99..ebca977825d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesDialog.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesDialog.java @@ -253,9 +253,9 @@ public class MoveKotlinNestedClassesDialog extends RefactoringDialog { KotlinMoveTarget target = new KotlinMoveTargetForExistingElement(targetClass); MoveDeclarationsDelegate.NestedClass delegate = new MoveDeclarationsDelegate.NestedClass(); MoveDeclarationsDescriptor descriptor = new MoveDeclarationsDescriptor( - elementsToMove, target, delegate, false, false, false, false, moveCallback, openInEditorCheckBox.isSelected() + myProject, elementsToMove, target, delegate, false, false, false, false, moveCallback, openInEditorCheckBox.isSelected() ); - invokeRefactoring(new MoveKotlinDeclarationsProcessor(myProject, descriptor, Mover.Default.INSTANCE)); + invokeRefactoring(new MoveKotlinDeclarationsProcessor(descriptor, Mover.Default.INSTANCE)); } @Override diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesToUpperLevelDialog.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesToUpperLevelDialog.java index e5a9922a5ae..5f825c4d607 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesToUpperLevelDialog.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinNestedClassesToUpperLevelDialog.java @@ -406,6 +406,7 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase { String newClassName = getClassName(); MoveDeclarationsDelegate delegate = new MoveDeclarationsDelegate.NestedClass(newClassName, outerInstanceParameterName); MoveDeclarationsDescriptor moveDescriptor = new MoveDeclarationsDescriptor( + project, CollectionsKt.listOf(innerClass), moveTarget, delegate, @@ -418,6 +419,6 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase { ); saveOpenInEditorOption(); - invokeRefactoring(new MoveKotlinDeclarationsProcessor(project, moveDescriptor, Mover.Default.INSTANCE)); + invokeRefactoring(new MoveKotlinDeclarationsProcessor(moveDescriptor, Mover.Default.INSTANCE)); } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java index 6d2d0c85e56..f8350cb1446 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/ui/MoveKotlinTopLevelDeclarationsDialog.java @@ -24,7 +24,6 @@ import com.intellij.openapi.fileTypes.FileTypeManager; import com.intellij.openapi.options.ConfigurationException; import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.JavaProjectRootsUtil; -import com.intellij.openapi.ui.DialogWrapper; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; import com.intellij.openapi.util.Pair; @@ -764,6 +763,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { } MoveDeclarationsDescriptor options = new MoveDeclarationsDescriptor( + myProject, elementsToMove, target, MoveDeclarationsDelegate.TopLevel.INSTANCE, @@ -774,7 +774,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { moveCallback, false ); - invokeRefactoring(new MoveKotlinDeclarationsProcessor(myProject, options, Mover.Default.INSTANCE)); + invokeRefactoring(new MoveKotlinDeclarationsProcessor(options, Mover.Default.INSTANCE)); } catch (IncorrectOperationException e) { CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.getMessage(), null, myProject); diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt index 19ddbe651c9..994afb1a8fb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesOrDirectories/MoveKotlinFileHandler.kt @@ -18,7 +18,10 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveFilesOrDirectories import com.intellij.openapi.module.ModuleUtilCore import com.intellij.openapi.roots.JavaProjectRootsUtil -import com.intellij.psi.* +import com.intellij.psi.PsiCompiledElement +import com.intellij.psi.PsiDirectory +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile import com.intellij.psi.impl.light.LightElement import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFileHandler import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil @@ -35,7 +38,6 @@ import org.jetbrains.kotlin.name.FqNameUnsafe import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtNamedDeclaration import org.jetbrains.kotlin.utils.addToStdlib.firstIsInstanceOrNull -import java.util.* class MoveKotlinFileHandler : MoveFileHandler() { internal class InternalUsagesWrapper(file: KtFile, val usages: List) : UsageInfo(file) @@ -88,8 +90,8 @@ class MoveKotlinFileHandler : MoveFileHandler() { } val declarationMoveProcessor = MoveKotlinDeclarationsProcessor( - project, MoveDeclarationsDescriptor( + project = project, elementsToMove = psiFile.declarations.filterIsInstance(), moveTarget = moveTarget, delegate = MoveDeclarationsDelegate.TopLevel, diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesUtil.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesUtil.kt new file mode 100644 index 00000000000..abd1445cb8e --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveFilesUtil.kt @@ -0,0 +1,148 @@ +/* + * Copyright 2010-2017 JetBrains s.r.o. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.jetbrains.kotlin.idea.refactoring.move + +import com.intellij.openapi.application.ApplicationManager +import com.intellij.openapi.project.Project +import com.intellij.openapi.ui.DialogWrapper +import com.intellij.psi.PsiDirectory +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiFile +import com.intellij.refactoring.RefactoringBundle +import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler +import com.intellij.refactoring.move.MoveCallback +import com.intellij.refactoring.move.MoveHandler +import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor +import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil +import com.intellij.refactoring.util.CommonRefactoringUtil +import com.intellij.util.IncorrectOperationException +import org.jetbrains.kotlin.idea.refactoring.isInJavaSourceRoot +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveFilesWithDeclarationsProcessor +import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog +import org.jetbrains.kotlin.idea.util.ProjectRootsUtil +import org.jetbrains.kotlin.idea.util.application.executeCommand +import org.jetbrains.kotlin.idea.util.application.runWriteAction +import org.jetbrains.kotlin.psi.KtElement +import org.jetbrains.kotlin.psi.KtFile + +fun invokeMoveFilesOrDirectoriesRefactoring( + moveDialog: KotlinAwareMoveFilesOrDirectoriesDialog?, + project: Project, + elements: Array, + initialTargetDirectory: PsiDirectory?, + moveCallback: MoveCallback? +) { + fun closeDialog() { + moveDialog?.close(DialogWrapper.CANCEL_EXIT_CODE) + } + + project.executeCommand(MoveHandler.REFACTORING_NAME) { + val selectedDir = if (moveDialog != null) moveDialog.targetDirectory else initialTargetDirectory + val updatePackageDirective = (moveDialog as? KotlinAwareMoveFilesOrDirectoriesDialog)?.updatePackageDirective + + try { + val choice = if (elements.size > 1 || elements[0] is PsiDirectory) intArrayOf(-1) else null + val elementsToMove = elements + .filterNot { + it is PsiFile + && runWriteAction { CopyFilesOrDirectoriesHandler.checkFileExist(selectedDir, choice, it, it.name, "Move") } + } + .sortedWith( // process Kotlin files first so that light classes are updated before changing references in Java files + java.util.Comparator { o1, o2 -> + when { + o1 is KtElement && o2 !is KtElement -> -1 + o1 !is KtElement && o2 is KtElement -> 1 + else -> 0 + } + } + ) + + elementsToMove.forEach { + MoveFilesOrDirectoriesUtil.checkMove(it, selectedDir!!) + if (it is KtFile && it.isInJavaSourceRoot()) { + it.updatePackageDirective = updatePackageDirective + } + } + + val enableSearchReferences = elements.any { ProjectRootsUtil.isInProjectSource(it) } + + if (elementsToMove.isNotEmpty()) { + @Suppress("UNCHECKED_CAST") + val processor = if (elementsToMove.all { it is KtFile } && selectedDir != null) { + MoveFilesWithDeclarationsProcessor( + project, + elementsToMove as List, + selectedDir, + null, + false, + false, + moveCallback, + Runnable(::closeDialog) + ) + } + else { + MoveFilesOrDirectoriesProcessor( + project, + elementsToMove.toTypedArray(), + selectedDir, + enableSearchReferences, + false, + false, + moveCallback, + Runnable(::closeDialog) + ) + } + processor.run() + } + else { + closeDialog() + } + } + catch (e: IncorrectOperationException) { + CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.message, "refactoring.moveFile", project) + } + } +} + +// Mostly copied from MoveFilesOrDirectoriesUtil.doMove() +fun moveFilesOrDirectories( + project: Project, + elements: Array, + targetElement: PsiElement?, + moveCallback: MoveCallback? = null +) { + elements.forEach { if (it !is PsiFile && it !is PsiDirectory) throw IllegalArgumentException("unexpected element type: " + it) } + + val targetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, targetElement) + if (targetElement != null && targetDirectory == null) return + + val initialTargetDirectory = MoveFilesOrDirectoriesUtil.getInitialTargetDirectory(targetDirectory, elements) + + fun doRun(moveDialog: KotlinAwareMoveFilesOrDirectoriesDialog?) { + invokeMoveFilesOrDirectoriesRefactoring(moveDialog, project, elements, initialTargetDirectory, moveCallback) + } + + if (ApplicationManager.getApplication().isUnitTestMode) { + doRun(null) + return + } + + with(KotlinAwareMoveFilesOrDirectoriesDialog(project, ::doRun)) { + setData(elements, initialTargetDirectory, "refactoring.moveFile") + show() + } +} \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt index 5936f9a1047..28ca53f7685 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveUtils.kt @@ -16,24 +16,13 @@ package org.jetbrains.kotlin.idea.refactoring.move -import com.intellij.openapi.application.ApplicationManager import com.intellij.openapi.progress.ProgressManager -import com.intellij.openapi.project.Project -import com.intellij.openapi.ui.DialogWrapper import com.intellij.openapi.util.Comparing import com.intellij.openapi.util.Key import com.intellij.psi.* -import com.intellij.psi.impl.source.resolve.reference.impl.PsiMultiReference -import com.intellij.refactoring.RefactoringBundle -import com.intellij.refactoring.copy.CopyFilesOrDirectoriesHandler -import com.intellij.refactoring.move.MoveCallback -import com.intellij.refactoring.move.MoveHandler -import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor -import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesUtil import com.intellij.refactoring.move.moveMembers.MockMoveMembersOptions import com.intellij.refactoring.move.moveMembers.MoveMemberHandler import com.intellij.refactoring.move.moveMembers.MoveMembersProcessor -import com.intellij.refactoring.util.CommonRefactoringUtil import com.intellij.refactoring.util.MoveRenameUsageInfo import com.intellij.refactoring.util.NonCodeUsageInfo import com.intellij.usageView.UsageInfo @@ -46,19 +35,12 @@ import org.jetbrains.kotlin.idea.KotlinFileType import org.jetbrains.kotlin.idea.caches.resolve.analyzeFully import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor import org.jetbrains.kotlin.idea.codeInsight.DescriptorToSourceUtilsIde -import org.jetbrains.kotlin.idea.codeInsight.KotlinFileReferencesResolver import org.jetbrains.kotlin.idea.codeInsight.shorten.addDelayedImportRequest import org.jetbrains.kotlin.idea.imports.importableFqName import org.jetbrains.kotlin.idea.refactoring.fqName.isImported -import org.jetbrains.kotlin.idea.refactoring.isInJavaSourceRoot -import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.MoveFilesWithDeclarationsProcessor -import org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui.KotlinAwareMoveFilesOrDirectoriesDialog import org.jetbrains.kotlin.idea.references.KtSimpleNameReference import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode import org.jetbrains.kotlin.idea.references.mainReference -import org.jetbrains.kotlin.idea.util.ProjectRootsUtil -import org.jetbrains.kotlin.idea.util.application.executeCommand -import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* @@ -66,6 +48,8 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.descriptorUtil.getImportableDescriptor +import org.jetbrains.kotlin.resolve.descriptorUtil.isAncestorOf +import org.jetbrains.kotlin.resolve.descriptorUtil.isCompanionObject import org.jetbrains.kotlin.resolve.descriptorUtil.parents import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitClassReceiver import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver @@ -98,13 +82,15 @@ data class ContainerChangeInfo(val oldContainer: ContainerInfo, val newContainer fun KtElement.getInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo: ContainerChangeInfo): List { val usages = ArrayList() - lazilyProcessInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo) { expr, factory -> usages.addIfNotNull(factory(expr)) } + processInternalReferencesToUpdateOnPackageNameChange(containerChangeInfo) { expr, factory -> usages.addIfNotNull(factory(expr)) } return usages } -fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange( +private typealias UsageInfoFactory = (KtSimpleNameExpression) -> UsageInfo? + +fun KtElement.processInternalReferencesToUpdateOnPackageNameChange( containerChangeInfo: ContainerChangeInfo, - body: (originalRefExpr: KtSimpleNameExpression, usageFactory: (KtSimpleNameExpression) -> UsageInfo?) -> Unit + body: (originalRefExpr: KtSimpleNameExpression, usageFactory: UsageInfoFactory) -> Unit ) { val file = containingFile as? KtFile ?: return @@ -121,11 +107,10 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange( } } - fun processReference(refExpr: KtSimpleNameExpression, bindingContext: BindingContext): ((KtSimpleNameExpression) -> UsageInfo?)? { + fun processReference(refExpr: KtSimpleNameExpression, bindingContext: BindingContext): (UsageInfoFactory)? { val descriptor = bindingContext[BindingContext.REFERENCE_TARGET, refExpr]?.getImportableDescriptor() ?: return null val containingDescriptor = descriptor.containingDeclaration ?: return null - var declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null val callableKind = (descriptor as? CallableMemberDescriptor)?.kind if (callableKind != null && callableKind != CallableMemberDescriptor.Kind.DECLARATION) return null @@ -135,7 +120,7 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange( if (descriptor is ClassDescriptor && descriptor.isInner && refExpr.parent is KtCallExpression) return null val isCallable = descriptor is CallableDescriptor - val isExtension = isCallable && declaration.isExtensionDeclaration() + val isExtension = isCallable && (descriptor as CallableDescriptor).extensionReceiverParameter != null val isCallableReference = isCallableReference(refExpr.mainReference) if (isCallable) { @@ -143,7 +128,7 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange( if (isExtension && containingDescriptor is ClassDescriptor) { val dispatchReceiver = refExpr.getResolvedCall(bindingContext)?.dispatchReceiver val implicitClass = (dispatchReceiver as? ImplicitClassReceiver)?.classDescriptor - if (DescriptorUtils.isCompanionObject(implicitClass)) { + if (implicitClass?.isCompanionObject ?: false) { return ::ImplicitCompanionAsDispatchReceiverUsageInfo } if (dispatchReceiver != null || containingDescriptor.kind != ClassKind.OBJECT) return null @@ -156,14 +141,14 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange( } } - if (declaration is KtObjectDeclaration - && declaration.isCompanion() + var declaration = DescriptorToSourceUtilsIde.getAnyDeclaration(project, descriptor) ?: return null + + if (descriptor.isCompanionObject() && bindingContext[BindingContext.SHORT_REFERENCE_TO_COMPANION_OBJECT, refExpr] != null) { - declaration = declaration.containingClassOrObject ?: declaration + declaration = (declaration as KtObjectDeclaration).containingClassOrObject ?: declaration } - val fqName = DescriptorUtils.getFqName(descriptor) - if (!fqName.isSafe) return null + if (!DescriptorUtils.getFqName(descriptor).isSafe) return null val (oldContainer, newContainer) = containerChangeInfo @@ -179,16 +164,15 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange( .firstOrNull() if (isExtension || containerFqName != null || isImported(descriptor)) return { - createMoveUsageInfoIfPossible(it.mainReference, declaration, false, this@lazilyProcessInternalReferencesToUpdateOnPackageNameChange) + createMoveUsageInfoIfPossible(it.mainReference, declaration, false, true) } return null } - val referenceToContext = KotlinFileReferencesResolver.resolve(file = file, elements = listOf(this)) - - for ((refExpr, bindingContext) in referenceToContext) { - if (refExpr !is KtSimpleNameExpression || refExpr.parent is KtThisExpression) continue + val bindingContext = analyzeFully() + forEachDescendantOfType { refExpr -> + if (refExpr !is KtSimpleNameExpression || refExpr.parent is KtThisExpression) return@forEachDescendantOfType processReference(refExpr, bindingContext)?.let { body(refExpr, it) } } @@ -196,8 +180,8 @@ fun KtElement.lazilyProcessInternalReferencesToUpdateOnPackageNameChange( class ImplicitCompanionAsDispatchReceiverUsageInfo(callee: KtSimpleNameExpression) : UsageInfo(callee) -interface InternableUsage { - val scope: PsiElement? +interface KotlinMoveUsage { + val isInternal: Boolean fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? } @@ -208,10 +192,10 @@ class UnqualifiableMoveRenameUsageInfo( referencedElement: PsiElement, val originalFile: PsiFile, val addImportToOriginalFile: Boolean, - override val scope: PsiElement? -): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), InternableUsage { + override val isInternal: Boolean +): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), KotlinMoveUsage { override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? { - return UnqualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, originalFile, addImportToOriginalFile, scope) + return UnqualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, originalFile, addImportToOriginalFile, isInternal) } } @@ -219,11 +203,11 @@ class QualifiableMoveRenameUsageInfo( element: PsiElement, reference: PsiReference, referencedElement: PsiElement, - override val scope: PsiElement? + override val isInternal: Boolean ): MoveRenameUsageInfo(element, reference, reference.rangeInElement.startOffset, reference.rangeInElement.endOffset, referencedElement, false), - InternableUsage { + KotlinMoveUsage { override fun refresh(refExpr: KtSimpleNameExpression, referencedElement: PsiElement): UsageInfo? { - return QualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, scope) + return QualifiableMoveRenameUsageInfo(refExpr, refExpr.mainReference, referencedElement, isInternal) } } @@ -231,17 +215,15 @@ fun createMoveUsageInfoIfPossible( reference: PsiReference, referencedElement: PsiElement, addImportToOriginalFile: Boolean, - scope: PsiElement? + isInternal: Boolean ): UsageInfo? { val element = reference.element - if (element.getStrictParentOfType() != null) return null - return when (getReferenceKind(reference, referencedElement)) { ReferenceKind.QUALIFIABLE -> QualifiableMoveRenameUsageInfo( - element, reference, referencedElement, scope + element, reference, referencedElement, isInternal ) ReferenceKind.UNQUALIFIABLE -> UnqualifiableMoveRenameUsageInfo( - element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, scope + element, reference, referencedElement, element.containingFile!!, addImportToOriginalFile, isInternal ) else -> null } @@ -256,6 +238,8 @@ private enum class ReferenceKind { private fun getReferenceKind(reference: PsiReference, referencedElement: PsiElement): ReferenceKind { val element = reference.element as? KtSimpleNameExpression ?: return ReferenceKind.QUALIFIABLE + if (element.getStrictParentOfType() != null) return ReferenceKind.IRRELEVANT + if ((referencedElement.namedUnwrappedElement as? KtDeclaration)?.isExtensionDeclaration() ?: false && reference.element.getNonStrictParentOfType() == null) return ReferenceKind.UNQUALIFIABLE @@ -315,7 +299,7 @@ private fun updateJavaReference(reference: PsiReferenceExpression, oldElement: P return false } -private fun mapToNewOrThis(e: PsiElement, oldToNewElementsMapping: Map) = oldToNewElementsMapping[e] ?: e +internal fun mapToNewOrThis(e: PsiElement, oldToNewElementsMapping: Map) = oldToNewElementsMapping[e] ?: e private fun postProcessMoveUsage( usage: UsageInfo, @@ -337,7 +321,7 @@ private fun postProcessMoveUsage( is MoveRenameUsageInfo -> { val oldElement = usage.referencedElement!! val newElement = mapToNewOrThis(oldElement, oldToNewElementsMapping) - val reference = usage.reference ?: (usage.element as? KtSimpleNameExpression)?.mainReference + val reference = (usage.element as? KtSimpleNameExpression)?.mainReference ?: usage.reference processReference(reference, newElement, shorteningMode, oldElement) } } @@ -346,7 +330,6 @@ private fun postProcessMoveUsage( 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) @@ -399,115 +382,18 @@ fun postProcessMoveUsages( var KtFile.updatePackageDirective: Boolean? by UserDataProperty(Key.create("UPDATE_PACKAGE_DIRECTIVE")) -fun invokeMoveFilesOrDirectoriesRefactoring( - moveDialog: KotlinAwareMoveFilesOrDirectoriesDialog?, - project: Project, - elements: Array, - initialTargetDirectory: PsiDirectory?, - moveCallback: MoveCallback? -) { - fun closeDialog() { - moveDialog?.close(DialogWrapper.CANCEL_EXIT_CODE) - } - - project.executeCommand(MoveHandler.REFACTORING_NAME) { - val selectedDir = if (moveDialog != null) moveDialog.targetDirectory else initialTargetDirectory - val updatePackageDirective = (moveDialog as? KotlinAwareMoveFilesOrDirectoriesDialog)?.updatePackageDirective - - try { - val choice = if (elements.size > 1 || elements[0] is PsiDirectory) intArrayOf(-1) else null - val elementsToMove = elements - .filterNot { - it is PsiFile - && runWriteAction { CopyFilesOrDirectoriesHandler.checkFileExist(selectedDir, choice, it, it.name, "Move") } - } - .sortedWith( // process Kotlin files first so that light classes are updated before changing references in Java files - Comparator { o1, o2 -> - when { - o1 is KtElement && o2 !is KtElement -> -1 - o1 !is KtElement && o2 is KtElement -> 1 - else -> 0 - } - } - ) - - elementsToMove.forEach { - MoveFilesOrDirectoriesUtil.checkMove(it, selectedDir!!) - if (it is KtFile && it.isInJavaSourceRoot()) { - it.updatePackageDirective = updatePackageDirective - } - } - - val enableSearchReferences = elements.any { ProjectRootsUtil.isInProjectSource(it) } - - if (elementsToMove.isNotEmpty()) { - @Suppress("UNCHECKED_CAST") - val processor = if (elementsToMove.all { it is KtFile } && selectedDir != null) { - MoveFilesWithDeclarationsProcessor( - project, - elementsToMove as List, - selectedDir, - null, - false, - false, - moveCallback, - Runnable(::closeDialog) - ) - } - else { - MoveFilesOrDirectoriesProcessor( - project, - elementsToMove.toTypedArray(), - selectedDir, - enableSearchReferences, - false, - false, - moveCallback, - Runnable(::closeDialog) - ) - } - processor.run() - } - else { - closeDialog() - } - } - catch (e: IncorrectOperationException) { - CommonRefactoringUtil.showErrorMessage(RefactoringBundle.message("error.title"), e.message, "refactoring.moveFile", project) - } - } -} - -// Mostly copied from MoveFilesOrDirectoriesUtil.doMove() -fun moveFilesOrDirectories( - project: Project, - elements: Array, - targetElement: PsiElement?, - moveCallback: MoveCallback? = null -) { - elements.forEach { if (it !is PsiFile && it !is PsiDirectory) throw IllegalArgumentException("unexpected element type: " + it) } - - val targetDirectory = MoveFilesOrDirectoriesUtil.resolveToDirectory(project, targetElement) - if (targetElement != null && targetDirectory == null) return - - val initialTargetDirectory = MoveFilesOrDirectoriesUtil.getInitialTargetDirectory(targetDirectory, elements) - - fun doRun(moveDialog: KotlinAwareMoveFilesOrDirectoriesDialog?) { - invokeMoveFilesOrDirectoriesRefactoring(moveDialog, project, elements, initialTargetDirectory, moveCallback) - } - - if (ApplicationManager.getApplication().isUnitTestMode) { - doRun(null) - return - } - - with(KotlinAwareMoveFilesOrDirectoriesDialog(project, ::doRun)) { - setData(elements, initialTargetDirectory, "refactoring.moveFile") - show() - } -} - sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, val isIndirectOuter: Boolean) : UsageInfo(element) { + open fun reportConflictIfAny(conflicts: MultiMap): Boolean { + val element = element ?: return false + + if (isIndirectOuter) { + conflicts.putValue(element, "Indirect outer instances won't be extracted: ${element.text}") + return true + } + + return false + } + class ExplicitThis( expression: KtThisExpression, isIndirectOuter: Boolean @@ -523,6 +409,24 @@ sealed class OuterInstanceReferenceUsageInfo(element: PsiElement, val isIndirect ) : OuterInstanceReferenceUsageInfo(callElement, isIndirectOuter) { val callElement: KtElement? get() = element as? KtElement + + override fun reportConflictIfAny(conflicts: MultiMap): Boolean { + if (super.reportConflictIfAny(conflicts)) return true + + val fullCall = callElement?.let { it.getQualifiedExpressionForSelector() ?: it } ?: return false + return when { + fullCall is KtQualifiedExpression -> { + conflicts.putValue(fullCall, "Qualified call won't be processed: ${fullCall.text}") + true + } + + isDoubleReceiver -> { + conflicts.putValue(fullCall, "Member extension call won't be processed: ${fullCall.text}") + true + } + else -> false + } + } } } @@ -542,7 +446,7 @@ fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boo val descriptor = context[BindingContext.REFERENCE_TARGET, element.instanceReference] val isIndirect = when { descriptor == outerClassDescriptor -> false - DescriptorUtils.isAncestor(descriptor, outerClassDescriptor, true) -> true + descriptor?.isAncestorOf(outerClassDescriptor, true) ?: false -> true else -> return null } OuterInstanceReferenceUsageInfo.ExplicitThis(element, isIndirect) @@ -552,15 +456,15 @@ fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boo val dispatchReceiver = resolvedCall.dispatchReceiver as? ImplicitReceiver val extensionReceiver = resolvedCall.extensionReceiver as? ImplicitReceiver var isIndirect = false - val isDoubleReceiver = when { - dispatchReceiver?.declarationDescriptor == outerClassDescriptor -> extensionReceiver != null - extensionReceiver?.declarationDescriptor == outerClassDescriptor -> dispatchReceiver != null + val isDoubleReceiver = when (outerClassDescriptor) { + dispatchReceiver?.declarationDescriptor -> extensionReceiver != null + extensionReceiver?.declarationDescriptor -> dispatchReceiver != null else -> { isIndirect = true when { - DescriptorUtils.isAncestor(dispatchReceiver?.declarationDescriptor, outerClassDescriptor, true) -> + dispatchReceiver?.declarationDescriptor?.isAncestorOf(outerClassDescriptor, true) ?: false -> extensionReceiver != null - DescriptorUtils.isAncestor(extensionReceiver?.declarationDescriptor, outerClassDescriptor, true) -> + extensionReceiver?.declarationDescriptor?.isAncestorOf(outerClassDescriptor, true) ?: false -> dispatchReceiver != null else -> return null } @@ -587,30 +491,7 @@ fun traverseOuterInstanceReferences(member: KtNamedDeclaration, stopAtFirst: Boo } fun collectOuterInstanceReferences(member: KtNamedDeclaration): List { - return SmartList().apply { traverseOuterInstanceReferences(member, false) { add(it) } } -} - -fun OuterInstanceReferenceUsageInfo.reportConflictIfAny(conflicts: MultiMap): Boolean { - val element = element ?: return false - - if (isIndirectOuter) { - conflicts.putValue(element, "Indirect outer instances won't be extracted: ${element.text}") - return true - } - - if (this !is OuterInstanceReferenceUsageInfo.ImplicitReceiver) return false - - val fullCall = callElement?.let { it.getQualifiedExpressionForSelector() ?: it } ?: return false - return when { - fullCall is KtQualifiedExpression -> { - conflicts.putValue(fullCall, "Qualified call won't be processed: ${fullCall.text}") - true - } - - isDoubleReceiver -> { - conflicts.putValue(fullCall, "Call with two implicit receivers won't be processed: ${fullCall.text}") - true - } - else -> false - } + val result = SmartList() + traverseOuterInstanceReferences(member, false) { result += it } + return result } \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelWithThisOuterRefConflicts/conflicts.txt b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelWithThisOuterRefConflicts/conflicts.txt index a00781457d5..98a38129274 100644 --- a/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelWithThisOuterRefConflicts/conflicts.txt +++ b/idea/testData/refactoring/move/kotlin/moveNestedClass/innerToTopLevelWithThisOuterRefConflicts/conflicts.txt @@ -1,3 +1,3 @@ -Call with two implicit receivers won't be processed: bar() -Call with two implicit receivers won't be processed: foo() +Member extension call won't be processed: bar() +Member extension call won't be processed: foo() Qualified call won't be processed: 1.foo() \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt index 856c1f5c3e5..9a1753fbf5d 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt @@ -295,7 +295,7 @@ enum class MoveAction { val moveTarget = config.getNullableString("targetPackage")?.let { packageName -> val targetSourceRootPath = config["targetSourceRoot"]?.asString - val packageWrapper = PackageWrapper(mainFile.getManager(), packageName) + val packageWrapper = PackageWrapper(mainFile.manager, packageName) val moveDestination: MoveDestination = targetSourceRootPath?.let { AutocreatingSingleSourceRootMoveDestination(packageWrapper, rootDir.findFileByRelativePath(it)!!) } ?: MultipleRootsMoveDestination(packageWrapper) @@ -313,8 +313,8 @@ enum class MoveAction { KotlinMoveTargetForExistingElement(PsiManager.getInstance(project).findFile(rootDir.findFileByRelativePath(filePath)!!) as KtFile) } - val options = MoveDeclarationsDescriptor(elementsToMove, moveTarget, MoveDeclarationsDelegate.TopLevel) - MoveKotlinDeclarationsProcessor(mainFile.getProject(), options).run() + val descriptor = MoveDeclarationsDescriptor(project, elementsToMove, moveTarget, MoveDeclarationsDelegate.TopLevel) + MoveKotlinDeclarationsProcessor(descriptor).run() } }, @@ -357,8 +357,8 @@ enum class MoveAction { createKotlinFile(fileName, targetDir, targetPackageFqName.asString()) } } - val descriptor = MoveDeclarationsDescriptor(listOf(elementToMove), moveTarget, delegate) - MoveKotlinDeclarationsProcessor(project, descriptor).run() + val descriptor = MoveDeclarationsDescriptor(project, listOf(elementToMove), moveTarget, delegate) + MoveKotlinDeclarationsProcessor(descriptor).run() } };