diff --git a/ChangeLog.md b/ChangeLog.md index b224d1abe92..d507222c53f 100644 --- a/ChangeLog.md +++ b/ChangeLog.md @@ -264,6 +264,7 @@ Using 'this' as function argument in constructor of non-final class - [`KT-13254`](https://youtrack.jetbrains.com/issue/KT-13254) Rename: Conflict detection for type parameters - [`KT-13282`](https://youtrack.jetbrains.com/issue/KT-13282), [`KT-13283`](https://youtrack.jetbrains.com/issue/KT-13283) Rename: Fix name quoting for automatic renamers - [`KT-13239`](https://youtrack.jetbrains.com/issue/KT-13239) Rename: Warn about function name conflicts +- [`KT-13174`](https://youtrack.jetbrains.com/issue/KT-13174) Move: Warn about accessibility conflicts due to moving to unrelated module ##### New features 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 036bd05247c..2ba7888373d 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 @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.move.changePackage +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile @@ -47,6 +48,8 @@ class KotlinChangePackageRefactoring(val file: KtFile) { override val directory: PsiDirectory = file.containingDirectory!! + override val targetFile: VirtualFile? = directory.virtualFile + override fun getOrCreateTargetPsi(originalPsi: PsiElement) = originalPsi.containingFile as? KtFile override fun getTargetPsiIfExists(originalPsi: PsiElement) = null diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt index aa079a0aeb9..b639a029d62 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/moveDeclarations/KotlinMoveTarget.kt @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations +import com.intellij.openapi.vfs.VirtualFile import com.intellij.psi.PsiDirectory import com.intellij.psi.PsiElement import com.intellij.psi.PsiFile @@ -30,6 +31,8 @@ import java.util.* interface KotlinMoveTarget { val targetContainerFqName: FqName? + val targetFile: VirtualFile? + fun getOrCreateTargetPsi(originalPsi: PsiElement): KtElement? fun getTargetPsiIfExists(originalPsi: PsiElement): KtElement? @@ -43,6 +46,8 @@ interface KotlinDirectoryBasedMoveTarget : KotlinMoveTarget { object EmptyKotlinMoveTarget: KotlinMoveTarget { override val targetContainerFqName = null + override val targetFile = null + override fun getOrCreateTargetPsi(originalPsi: PsiElement) = null override fun getTargetPsiIfExists(originalPsi: PsiElement) = null override fun verify(file: PsiFile) = null @@ -51,6 +56,8 @@ object EmptyKotlinMoveTarget: KotlinMoveTarget { class KotlinMoveTargetForExistingElement(val targetElement: KtElement): KotlinMoveTarget { override val targetContainerFqName = targetElement.getContainingKtFile().packageFqName + override val targetFile: VirtualFile? = targetElement.getContainingKtFile().virtualFile + override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetElement override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetElement @@ -63,6 +70,8 @@ class KotlinMoveTargetForCompanion(val targetClass: KtClass): KotlinMoveTarget { override val targetContainerFqName = targetClass.getCompanionObjects().firstOrNull()?.fqName ?: targetClass.fqName!!.child(SpecialNames.DEFAULT_NAME_FOR_COMPANION_OBJECT) + override val targetFile: VirtualFile? = targetClass.getContainingKtFile().virtualFile + override fun getOrCreateTargetPsi(originalPsi: PsiElement) = targetClass.getOrCreateCompanionObject() override fun getTargetPsiIfExists(originalPsi: PsiElement) = targetClass.getCompanionObjects().firstOrNull() @@ -74,6 +83,7 @@ class KotlinMoveTargetForCompanion(val targetClass: KtClass): KotlinMoveTarget { class KotlinMoveTargetForDeferredFile( override val targetContainerFqName: FqName, override val directory: PsiDirectory?, + override val targetFile: VirtualFile? = directory?.virtualFile, private val createFile: (KtFile) -> KtFile? ): KotlinDirectoryBasedMoveTarget { private val createdFiles = HashMap() 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 5b945f1d1cf..3adf235c0d5 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 @@ -90,7 +90,7 @@ class MoveDeclarationToSeparateFileIntention : } return } - val moveTarget = KotlinMoveTargetForDeferredFile(packageName, directory) { + val moveTarget = KotlinMoveTargetForDeferredFile(packageName, directory, null) { createKotlinFile(targetFileName, directory, packageName.asString()) } val moveOptions = MoveDeclarationsDescriptor( 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 c845816dc7b..ea6f962701c 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 @@ -22,20 +22,16 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.util.Ref import com.intellij.openapi.util.text.StringUtil -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiMember -import com.intellij.psi.PsiNamedElement -import com.intellij.psi.PsiReference +import com.intellij.psi.* +import com.intellij.psi.search.GlobalSearchScope import com.intellij.psi.search.searches.ReferencesSearch import com.intellij.refactoring.BaseRefactoringProcessor +import com.intellij.refactoring.RefactoringBundle import com.intellij.refactoring.move.MoveCallback import com.intellij.refactoring.move.MoveMultipleElementsViewDescriptor import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassHandler import com.intellij.refactoring.rename.RenameUtil -import com.intellij.refactoring.util.MoveRenameUsageInfo -import com.intellij.refactoring.util.NonCodeUsageInfo -import com.intellij.refactoring.util.RefactoringUIUtil -import com.intellij.refactoring.util.TextOccurrencesUtil +import com.intellij.refactoring.util.* import com.intellij.usageView.UsageInfo import com.intellij.usageView.UsageViewBundle import com.intellij.usageView.UsageViewDescriptor @@ -48,6 +44,7 @@ import gnu.trove.TObjectHashingStrategy import org.jetbrains.kotlin.asJava.elements.KtLightElement import org.jetbrains.kotlin.asJava.namedUnwrappedElement import org.jetbrains.kotlin.asJava.toLightElements +import org.jetbrains.kotlin.asJava.toLightMethods import org.jetbrains.kotlin.caches.resolve.KotlinCacheService import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.MutablePackageFragmentDescriptor @@ -64,13 +61,11 @@ import org.jetbrains.kotlin.idea.refactoring.move.postProcessMoveUsages import org.jetbrains.kotlin.idea.references.KtSimpleNameReference.ShorteningMode import org.jetbrains.kotlin.idea.search.projectScope import org.jetbrains.kotlin.psi.* -import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType -import org.jetbrains.kotlin.psi.psiUtil.getElementTextWithContext -import org.jetbrains.kotlin.psi.psiUtil.isAncestor -import org.jetbrains.kotlin.psi.psiUtil.isInsideOf +import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.source.KotlinSourceElement +import org.jetbrains.kotlin.utils.SmartSet import org.jetbrains.kotlin.utils.keysToMap import java.util.* @@ -253,6 +248,39 @@ class MoveKotlinDeclarationsProcessor( fun render(declaration: PsiElement) = RefactoringUIUtil.getDescription(declaration, false) + fun checkModuleConflictsInUsages(usages: List) { + val sourceRoot = descriptor.moveTarget.targetFile ?: return + RefactoringConflictsUtil.analyzeModuleConflicts(project, elementsToMove, usages.toTypedArray(), sourceRoot, conflicts) + } + + fun checkModuleConflictsInDeclarations() { + val sourceRoot = descriptor.moveTarget.targetFile ?: return + val targetModule = ModuleUtilCore.findModuleForFile(sourceRoot, project) ?: return + val resolveScope = GlobalSearchScope.moduleWithDependenciesAndLibrariesScope(targetModule) + for (declaration in elementsToMove) { + declaration.forEachDescendantOfType { refExpr -> + refExpr.references + .forEach { ref -> + val target = ref.resolve() ?: return@forEach + if (target.isInsideOf(elementsToMove)) return@forEach + if (target in resolveScope) return@forEach + + val superMethods = SmartSet.create() + target.toLightMethods().forEach { superMethods += it.findDeepestSuperMethods() } + if (superMethods.any { it in resolveScope }) return@forEach + + val refContainer = ref.element.getStrictParentOfType() ?: return@forEach + val scopeDescription = RefactoringUIUtil.getDescription(refContainer, true) + val message = RefactoringBundle.message("0.referenced.in.1.will.not.be.accessible.in.module.2", + RefactoringUIUtil.getDescription(target, true), + scopeDescription, + CommonRefactoringUtil.htmlEmphasize(targetModule.name)) + conflicts.putValue(target, CommonRefactoringUtil.capitalize(message)) + } + } + } + } + fun checkVisibilityInUsages(usages: List) { val declarationToContainers = HashMap>() for (usage in usages) { @@ -319,6 +347,8 @@ class MoveKotlinDeclarationsProcessor( usages += descriptor.delegate.findUsages(descriptor) collectUsages(kotlinToLightElements, usages) + checkModuleConflictsInUsages(usages) + checkModuleConflictsInDeclarations() checkVisibilityInUsages(usages) checkVisibilityInDeclarations() descriptor.delegate.collectConflicts(descriptor, usages, conflicts) 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 65e74057a3f..1e044fd9e78 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 @@ -375,6 +375,7 @@ public class MoveKotlinNestedClassesToUpperLevelDialog extends MoveDialogBase { moveTarget = new KotlinMoveTargetForDeferredFile( targetPackageFqName, targetDir, + null, new Function1() { @Override public KtFile invoke(@NotNull KtFile originalFile) { 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 a7acfedb379..5d181cb48f1 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 @@ -16,6 +16,7 @@ package org.jetbrains.kotlin.idea.refactoring.move.moveDeclarations.ui; +import com.intellij.ide.util.DirectoryChooser; import com.intellij.openapi.editor.Document; import com.intellij.openapi.editor.event.DocumentAdapter; import com.intellij.openapi.editor.event.DocumentEvent; @@ -25,9 +26,12 @@ import com.intellij.openapi.project.Project; import com.intellij.openapi.roots.JavaProjectRootsUtil; import com.intellij.openapi.ui.Messages; import com.intellij.openapi.ui.TextFieldWithBrowseButton; +import com.intellij.openapi.util.Comparing; import com.intellij.openapi.util.EmptyRunnable; +import com.intellij.openapi.util.Pair; import com.intellij.openapi.util.Pass; import com.intellij.openapi.util.text.StringUtil; +import com.intellij.openapi.vfs.VirtualFile; import com.intellij.psi.*; import com.intellij.refactoring.JavaRefactoringSettings; import com.intellij.refactoring.MoveDestination; @@ -38,7 +42,10 @@ import com.intellij.refactoring.classMembers.MemberInfoChange; import com.intellij.refactoring.classMembers.MemberInfoChangeListener; import com.intellij.refactoring.move.MoveCallback; import com.intellij.refactoring.move.MoveHandler; +import com.intellij.refactoring.move.moveClassesOrPackages.AutocreatingSingleSourceRootMoveDestination; import com.intellij.refactoring.move.moveClassesOrPackages.DestinationFolderComboBox; +import com.intellij.refactoring.move.moveClassesOrPackages.MoveClassesOrPackagesUtil; +import com.intellij.refactoring.move.moveClassesOrPackages.MultipleRootsMoveDestination; import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectoriesProcessor; import com.intellij.refactoring.ui.PackageNameReferenceEditorCombo; import com.intellij.refactoring.ui.RefactoringDialog; @@ -59,8 +66,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import org.jetbrains.kotlin.idea.KotlinFileType; import org.jetbrains.kotlin.idea.core.PackageUtilsKt; -import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtilKt; import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringBundle; +import org.jetbrains.kotlin.idea.refactoring.KotlinRefactoringUtilKt; import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberInfo; import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionPanel; import org.jetbrains.kotlin.idea.refactoring.memberInfo.KotlinMemberSelectionTable; @@ -103,6 +110,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { private KotlinMemberSelectionTable memberTable; private final MoveCallback moveCallback; + private final PsiDirectory initialTargetDirectory; public MoveKotlinTopLevelDeclarationsDialog( @NotNull Project project, @@ -120,6 +128,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { List sourceFiles = getSourceFiles(elementsToMove); this.moveCallback = moveCallback; + this.initialTargetDirectory = targetDirectory; init(); @@ -440,7 +449,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { } @Nullable - private MoveDestination selectPackageBasedMoveDestination(boolean askIfDoesNotExist) { + private Pair selectPackageBasedTargetDirAndDestination(boolean askIfDoesNotExist) { String packageName = getTargetPackage(); RecentsManager.getInstance(myProject).registerRecentEntry(RECENTS_KEY, packageName); @@ -451,7 +460,18 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { if (ret != Messages.YES) return null; } - return ((DestinationFolderComboBox) destinationFolderCB).selectDirectory(targetPackage, false); + DirectoryChooser.ItemWrapper selectedItem = (DirectoryChooser.ItemWrapper)destinationFolderCB.getComboBox().getSelectedItem(); + PsiDirectory selectedPsiDirectory = selectedItem.getDirectory(); + if (selectedPsiDirectory == null) return Pair.create(null, new MultipleRootsMoveDestination(targetPackage)); + + VirtualFile targetDirectory = selectedPsiDirectory.getVirtualFile(); + List sourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(getProject()); + if (initialTargetDirectory != null && Comparing.equal(targetDirectory, initialTargetDirectory.getVirtualFile()) && + sourceRoots.size() > 1) { + targetDirectory = MoveClassesOrPackagesUtil.chooseSourceRoot(targetPackage, sourceRoots, initialTargetDirectory); + } + if (targetDirectory == null) return null; + return Pair.create(targetDirectory, new AutocreatingSingleSourceRootMoveDestination(targetPackage, targetDirectory)); } private boolean checkTargetFileName(String fileName) { @@ -508,8 +528,11 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { PsiDirectory sourceDirectory = getSourceDirectory(sourceFiles); if (isMoveToPackage()) { - final MoveDestination moveDestination = selectPackageBasedMoveDestination(true); - if (moveDestination == null) return null; + Pair targetDirWithMoveDestination = selectPackageBasedTargetDirAndDestination(true); + if (targetDirWithMoveDestination == null) return null; + + VirtualFile targetDir = targetDirWithMoveDestination.getFirst(); + final MoveDestination moveDestination = targetDirWithMoveDestination.getSecond(); final String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText(); if (targetFileName != null && !checkTargetFileName(targetFileName)) return null; @@ -555,6 +578,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { return new KotlinMoveTargetForDeferredFile( new FqName(getTargetPackage()), moveDestination.getTargetIfExists(sourceFiles.get(0)), + targetDir, new Function1() { @Override public KtFile invoke(@NotNull KtFile originalFile) { @@ -610,6 +634,7 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { return new KotlinMoveTargetForDeferredFile( targetPackageFqName, psiDirectory, + null, new Function1() { @Override public KtFile invoke(@NotNull KtFile originalFile) { @@ -705,8 +730,10 @@ public class MoveKotlinTopLevelDeclarationsDialog extends RefactoringDialog { if (isFullFileMove()) { if (isMoveToPackage()) { - final MoveDestination moveDestination = selectPackageBasedMoveDestination(false); + Pair sourceRootWithMoveDestination = selectPackageBasedTargetDirAndDestination(false); //noinspection ConstantConditions + final MoveDestination moveDestination = sourceRootWithMoveDestination.getSecond(); + PsiDirectory targetDir = moveDestination.getTargetIfExists(sourceDirectory); final String targetFileName = sourceFiles.size() > 1 ? null : tfFileNameInPackage.getText(); List filesExistingInTargetDir = getFilesExistingInTargetDir(sourceFiles, targetFileName, targetDir); diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/A/A.iml b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/A/A.iml new file mode 100644 index 00000000000..c90834f2d60 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/A/A.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/A/src/a/test.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/A/src/a/test.kt new file mode 100644 index 00000000000..0b96cab78f4 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/A/src/a/test.kt @@ -0,0 +1,4 @@ +package a + +val val1 = 0 +val val2 = val1 \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/B/B.iml b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/B/B.iml new file mode 100644 index 00000000000..c90834f2d60 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/B/B.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/B/src/b/test.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/B/src/b/test.kt new file mode 100644 index 00000000000..e07fdeaee77 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/after/B/src/b/test.kt @@ -0,0 +1,3 @@ +package b + +class X \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/A/A.iml b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/A/A.iml new file mode 100644 index 00000000000..c90834f2d60 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/A/A.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/A/src/a/test.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/A/src/a/test.kt new file mode 100644 index 00000000000..a0f18d871ba --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/A/src/a/test.kt @@ -0,0 +1,4 @@ +package a + +val val1 = 0 +val val2 = val1 \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/B/B.iml b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/B/B.iml new file mode 100644 index 00000000000..c90834f2d60 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/B/B.iml @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/B/src/b/test.kt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/B/src/b/test.kt new file mode 100644 index 00000000000..e07fdeaee77 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/before/B/src/b/test.kt @@ -0,0 +1,3 @@ +package b + +class X \ No newline at end of file diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/conflicts.txt b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/conflicts.txt new file mode 100644 index 00000000000..7a621710d08 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/conflicts.txt @@ -0,0 +1,2 @@ +Property a.val1, referenced in file test.kt, will not be accessible from module A +Property a.val1, referenced in file test.kt, will not be accessible from module A diff --git a/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/moveToUnrelatedModuleConflict.test b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/moveToUnrelatedModuleConflict.test new file mode 100644 index 00000000000..9cd06fa9270 --- /dev/null +++ b/idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/moveToUnrelatedModuleConflict.test @@ -0,0 +1,8 @@ +{ + "mainFile": "A/src/a/test.kt", + "type": "MOVE_KOTLIN_TOP_LEVEL_DECLARATIONS", + "targetPackage": "target", + "targetSourceRoot": "B/src", + "isMultiModule": "true", + "withRuntime": "true" +} 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 dac4b734d47..2be046d44f4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/AbstractMoveTest.kt @@ -19,11 +19,15 @@ package org.jetbrains.kotlin.idea.refactoring.move import com.google.gson.JsonObject import com.google.gson.JsonParser import com.intellij.codeInsight.TargetElementUtilBase +import com.intellij.ide.highlighter.ModuleFileType import com.intellij.openapi.editor.EditorFactory import com.intellij.openapi.fileEditor.FileDocumentManager +import com.intellij.openapi.module.StdModuleTypes import com.intellij.openapi.util.io.FileUtil import com.intellij.openapi.vfs.VfsUtil +import com.intellij.openapi.vfs.VfsUtilCore import com.intellij.openapi.vfs.VirtualFile +import com.intellij.openapi.vfs.VirtualFileVisitor import com.intellij.psi.* import com.intellij.refactoring.BaseRefactoringProcessor.ConflictsInTestsException import com.intellij.refactoring.PackageWrapper @@ -36,6 +40,7 @@ import com.intellij.refactoring.move.moveFilesOrDirectories.MoveFilesOrDirectori import com.intellij.refactoring.move.moveInner.MoveInnerProcessor import com.intellij.refactoring.move.moveMembers.MockMoveMembersOptions import com.intellij.refactoring.move.moveMembers.MoveMembersProcessor +import com.intellij.testFramework.PsiTestUtil import com.intellij.util.ActionRunner import org.jetbrains.kotlin.idea.jsonUtils.getNullableString import org.jetbrains.kotlin.idea.jsonUtils.getString @@ -59,6 +64,8 @@ import org.jetbrains.kotlin.test.KotlinTestUtils import java.io.File abstract class AbstractMoveTest : KotlinMultiFileTestCase() { + private var isMultiModule = false + protected fun doTest(path: String) { val config = JsonParser().parse(FileUtil.loadFile(File(path), true)) as JsonObject @@ -73,6 +80,7 @@ abstract class AbstractMoveTest : KotlinMultiFileTestCase() { if (withRuntime) { ConfigLibraryUtil.configureKotlinRuntimeAndSdk(myModule, PluginTestCaseBase.mockJdk()) } + isMultiModule = config["isMultiModule"]?.asBoolean ?: false doTest({ rootDir, rootAfter -> val mainFile = rootDir.findFileByRelativePath(mainFilePath)!! @@ -112,6 +120,27 @@ abstract class AbstractMoveTest : KotlinMultiFileTestCase() { getTestDirName(true)) } + override fun prepareProject(rootDir: VirtualFile) { + if (isMultiModule) { + VfsUtilCore.visitChildrenRecursively( + rootDir, + object : VirtualFileVisitor() { + override fun visitFile(file: VirtualFile): Boolean { + if (!file.isDirectory && file.name.endsWith(ModuleFileType.DOT_DEFAULT_EXTENSION)) { + createModule(File(file.path), StdModuleTypes.JAVA) + return false + } + + return true + } + } + ) + } + else { + PsiTestUtil.addSourceContentToRoots(myModule, rootDir) + } + } + protected fun getTestDirName(lowercaseFirstLetter : Boolean) : String { val testName = getTestName(lowercaseFirstLetter) return testName.substring(0, testName.lastIndexOf('_')).replace('_', '/') @@ -250,8 +279,16 @@ enum class MoveAction { val elementToMove = elementAtCaret!!.getNonStrictParentOfType()!! val moveTarget = config.getNullableString("targetPackage")?.let { packageName -> + val targetSourceRootPath = config["targetSourceRoot"]?.asString val moveDestination = MultipleRootsMoveDestination(PackageWrapper(mainFile.getManager(), packageName)) - KotlinMoveTargetForDeferredFile(FqName(packageName), moveDestination.getTargetIfExists(mainFile)) { + val targetDir = moveDestination.getTargetIfExists(mainFile) + val targetSourceRoot = if (targetSourceRootPath != null) { + rootDir.findFileByRelativePath(targetSourceRootPath)!! + } else { + targetDir?.virtualFile + } + + KotlinMoveTargetForDeferredFile(FqName(packageName), targetDir, targetSourceRoot) { createKotlinFile(guessNewFileName(listOf(elementToMove))!!, moveDestination.getTargetDirectory(mainFile)) } } ?: config.getString("targetFile").let { filePath -> @@ -298,7 +335,7 @@ enum class MoveAction { val fileName = (delegate.newClassName ?: elementToMove.name!!) + ".kt" val targetPackageFqName = (mainFile as KtFile).packageFqName val targetDir = mainFile.containingDirectory!! - KotlinMoveTargetForDeferredFile(targetPackageFqName, targetDir) { + KotlinMoveTargetForDeferredFile(targetPackageFqName, targetDir, null) { createKotlinFile(fileName, targetDir, targetPackageFqName.asString()) } } diff --git a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java index 480e63dfa9f..e534904a630 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/refactoring/move/MoveTestGenerated.java @@ -437,6 +437,12 @@ public class MoveTestGenerated extends AbstractMoveTest { doTest(fileName); } + @TestMetadata("kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/moveToUnrelatedModuleConflict.test") + public void testKotlin_moveTopLevelDeclarations_misc_moveToUnrelatedModuleConflict_MoveToUnrelatedModuleConflict() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/moveToUnrelatedModuleConflict/moveToUnrelatedModuleConflict.test"); + doTest(fileName); + } + @TestMetadata("kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test") public void testKotlin_moveTopLevelDeclarations_misc_selfReferences_SelfReferences() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/refactoring/move/kotlin/moveTopLevelDeclarations/misc/selfReferences/selfReferences.test");