diff --git a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/PackageDirectoryMismatchInspection.kt b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/PackageDirectoryMismatchInspection.kt index f7979c26594..a05fab02637 100644 --- a/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/PackageDirectoryMismatchInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/refactoring/move/changePackage/PackageDirectoryMismatchInspection.kt @@ -6,10 +6,14 @@ package org.jetbrains.kotlin.idea.refactoring.move.changePackage import com.intellij.CommonBundle -import com.intellij.codeInspection.* +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder import com.intellij.openapi.project.Project import com.intellij.openapi.roots.JavaProjectRootsUtil import com.intellij.openapi.ui.Messages +import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiManager import com.intellij.refactoring.PackageWrapper import com.intellij.refactoring.move.moveClassesOrPackages.AutocreatingSingleSourceRootMoveDestination @@ -24,44 +28,38 @@ import org.jetbrains.kotlin.idea.refactoring.hasIdentifiersOnly import org.jetbrains.kotlin.idea.refactoring.isInjectedFragment import org.jetbrains.kotlin.idea.util.application.runWriteAction import org.jetbrains.kotlin.name.FqName +import org.jetbrains.kotlin.psi.KtFile import org.jetbrains.kotlin.psi.KtPackageDirective -import org.jetbrains.kotlin.psi.KtVisitorVoid +import org.jetbrains.kotlin.psi.packageDirectiveVisitor class PackageDirectoryMismatchInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = packageDirectiveVisitor(fun(directive: KtPackageDirective) { + val file = directive.containingKtFile + if (file.isInjectedFragment || file.packageMatchesDirectoryOrImplicit()) return - override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession) = - object : KtVisitorVoid() { - override fun visitPackageDirective(directive: KtPackageDirective) { - super.visitPackageDirective(directive) - if (directive.text.isEmpty()) return - - val file = directive.containingKtFile - if (file.isInjectedFragment || file.packageMatchesDirectoryOrImplicit()) return - - val fixes = mutableListOf() - val qualifiedName = directive.qualifiedName - val dirName = if (qualifiedName.isEmpty()) "source root" else "'${qualifiedName.replace('.', '/')}'" - fixes += MoveFileToPackageFix(dirName) - val fqNameByDirectory = file.getFqNameByDirectory() - when { - fqNameByDirectory.isRoot -> - fixes += ChangePackageFix("source root", fqNameByDirectory) - fqNameByDirectory.hasIdentifiersOnly() -> - fixes += ChangePackageFix("'${fqNameByDirectory.asString()}'", fqNameByDirectory) - } - val fqNameWithImplicitPrefix = file.parent?.getFqNameWithImplicitPrefix() - if (fqNameWithImplicitPrefix != null && fqNameWithImplicitPrefix != fqNameByDirectory) { - fixes += ChangePackageFix("'${fqNameWithImplicitPrefix.asString()}'", fqNameWithImplicitPrefix) - } - - holder.registerProblem( - directive, - "Package directive doesn't match file location", - ProblemHighlightType.GENERIC_ERROR_OR_WARNING, - *fixes.toTypedArray() - ) - } + val fixes = mutableListOf() + val qualifiedName = directive.qualifiedName + val dirName = if (qualifiedName.isEmpty()) "source root" else "'${qualifiedName.replace('.', '/')}'" + fixes += MoveFileToPackageFix(dirName) + val fqNameByDirectory = file.getFqNameByDirectory() + when { + fqNameByDirectory.isRoot -> + fixes += ChangePackageFix("source root", fqNameByDirectory) + fqNameByDirectory.hasIdentifiersOnly() -> + fixes += ChangePackageFix("'${fqNameByDirectory.asString()}'", fqNameByDirectory) } + val fqNameWithImplicitPrefix = file.parent?.getFqNameWithImplicitPrefix() + if (fqNameWithImplicitPrefix != null && fqNameWithImplicitPrefix != fqNameByDirectory) { + fixes += ChangePackageFix("'${fqNameWithImplicitPrefix.asString()}'", fqNameWithImplicitPrefix) + } + + holder.registerProblem( + file, + directive.textRange, + "Package directive doesn't match file location", + *fixes.toTypedArray() + ) + }) private class MoveFileToPackageFix(val dirName: String) : LocalQuickFix { override fun getFamilyName() = "Move file to package-matching directory" @@ -71,16 +69,16 @@ class PackageDirectoryMismatchInspection : AbstractKotlinInspection() { override fun startInWriteAction() = false override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val directive = descriptor.psiElement as? KtPackageDirective ?: return - val file = directive.containingKtFile + val file = descriptor.psiElement as? KtFile ?: return + val directive = file.packageDirective ?: return val sourceRoots = JavaProjectRootsUtil.getSuitableDestinationSourceRoots(project) val packageWrapper = PackageWrapper(PsiManager.getInstance(project), directive.qualifiedName) val fileToMove = directive.containingFile val chosenRoot = sourceRoots.singleOrNull() - ?: MoveClassesOrPackagesUtil.chooseSourceRoot(packageWrapper, sourceRoots, fileToMove.containingDirectory) - ?: return + ?: MoveClassesOrPackagesUtil.chooseSourceRoot(packageWrapper, sourceRoots, fileToMove.containingDirectory) + ?: return val targetDirFactory = AutocreatingSingleSourceRootMoveDestination(packageWrapper, chosenRoot) targetDirFactory.verify(fileToMove)?.let { Messages.showMessageDialog(project, it, CommonBundle.getErrorTitle(), Messages.getErrorIcon()) @@ -107,8 +105,7 @@ class PackageDirectoryMismatchInspection : AbstractKotlinInspection() { override fun getName() = "Change file's package to $packageName" override fun applyFix(project: Project, descriptor: ProblemDescriptor) { - val directive = descriptor.psiElement as? KtPackageDirective ?: return - val file = directive.containingKtFile + val file = descriptor.psiElement as? KtFile ?: return KotlinChangePackageRefactoring(file).run(packageFqName) } } diff --git a/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/before/foo/rootPackageMatched.kt b/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/before/foo/rootPackageMatched.kt new file mode 100644 index 00000000000..93f2bb9d117 --- /dev/null +++ b/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/before/foo/rootPackageMatched.kt @@ -0,0 +1,4 @@ + +class Foo { + +} \ No newline at end of file diff --git a/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/expected.xml b/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/expected.xml new file mode 100644 index 00000000000..0ffbf4c15d9 --- /dev/null +++ b/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/expected.xml @@ -0,0 +1,10 @@ + + + rootPackageMatched.kt + 1 + testMismatchedProjectAndDirectoryRoot_MismatchedProjectAndDirectoryRoot + + Package name does not match containing directory + Package directive doesn't match file location + + \ No newline at end of file diff --git a/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/mismatchedProjectAndDirectoryRoot.test b/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/mismatchedProjectAndDirectoryRoot.test new file mode 100644 index 00000000000..3ac2faa69dc --- /dev/null +++ b/idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/mismatchedProjectAndDirectoryRoot.test @@ -0,0 +1,3 @@ +{ + "inspectionClass": "org.jetbrains.kotlin.idea.refactoring.move.changePackage.PackageDirectoryMismatchInspection" +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/after/source/dummy.kt b/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/after/source/dummy.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/after/test.kt b/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/after/test.kt new file mode 100644 index 00000000000..3409dddcd69 --- /dev/null +++ b/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/after/test.kt @@ -0,0 +1,6 @@ + +class Foo + +fun foo() { + +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/before/source/dummy.kt b/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/before/source/dummy.kt new file mode 100644 index 00000000000..e69de29bb2d diff --git a/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/before/source/test.kt b/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/before/source/test.kt new file mode 100644 index 00000000000..9ebdad4a93c --- /dev/null +++ b/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/before/source/test.kt @@ -0,0 +1,6 @@ + +class Foo + +fun foo() { + +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/moveToDefaultDirectoryWithoutPackageKeyword.test b/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/moveToDefaultDirectoryWithoutPackageKeyword.test new file mode 100644 index 00000000000..9b826406763 --- /dev/null +++ b/idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/moveToDefaultDirectoryWithoutPackageKeyword.test @@ -0,0 +1,5 @@ +{ + "mainFile": "source/test.kt", + "inspectionClass": "org.jetbrains.kotlin.idea.refactoring.move.changePackage.PackageDirectoryMismatchInspection", + "fix": "Move file to source root" +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/target/test.kt b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/target/test.kt new file mode 100644 index 00000000000..0c71841bbb9 --- /dev/null +++ b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/target/test.kt @@ -0,0 +1,7 @@ +package target + +class Foo + +fun foo() { + +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/target/usagesWithFqNames.kt b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/target/usagesWithFqNames.kt new file mode 100644 index 00000000000..a14e5ef3203 --- /dev/null +++ b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/target/usagesWithFqNames.kt @@ -0,0 +1,6 @@ +package target + +fun test() { + Foo() + foo() +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/usages/usagesWithFqNames.kt b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/usages/usagesWithFqNames.kt new file mode 100644 index 00000000000..b84c4624081 --- /dev/null +++ b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/after/usages/usagesWithFqNames.kt @@ -0,0 +1,9 @@ +package usages + +import target.foo +import target.Foo + +fun test() { + Foo() + foo() +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/target/test.kt b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/target/test.kt new file mode 100644 index 00000000000..a89679abbdf --- /dev/null +++ b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/target/test.kt @@ -0,0 +1,5 @@ +class Foo + +fun foo() { + +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/target/usagesWithFqNames.kt b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/target/usagesWithFqNames.kt new file mode 100644 index 00000000000..d4a81659954 --- /dev/null +++ b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/target/usagesWithFqNames.kt @@ -0,0 +1,9 @@ +package target + +import foo +import Foo + +fun test() { + Foo() + foo() +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/usages/usagesWithFqNames.kt b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/usages/usagesWithFqNames.kt new file mode 100644 index 00000000000..c83afb36b95 --- /dev/null +++ b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/before/usages/usagesWithFqNames.kt @@ -0,0 +1,9 @@ +package usages + +import foo +import Foo + +fun test() { + Foo() + foo() +} \ No newline at end of file diff --git a/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/changeToNonDefaultPackageFromRoot.test b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/changeToNonDefaultPackageFromRoot.test new file mode 100644 index 00000000000..1978afd152d --- /dev/null +++ b/idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/changeToNonDefaultPackageFromRoot.test @@ -0,0 +1,5 @@ +{ + "mainFile": "target/test.kt", + "inspectionClass": "org.jetbrains.kotlin.idea.refactoring.move.changePackage.PackageDirectoryMismatchInspection", + "fix": "Change file's package to 'target'" +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java index 245a39ca41d..0c3faf051a4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/MultiFileInspectionTestGenerated.java @@ -54,6 +54,11 @@ public class MultiFileInspectionTestGenerated extends AbstractMultiFileInspectio runTest("idea/testData/multiFileInspections/mismatchedProjectAndDirectory/mismatchedProjectAndDirectory.test"); } + @TestMetadata("mismatchedProjectAndDirectoryRoot/mismatchedProjectAndDirectoryRoot.test") + public void testMismatchedProjectAndDirectoryRoot_MismatchedProjectAndDirectoryRoot() throws Exception { + runTest("idea/testData/multiFileInspections/mismatchedProjectAndDirectoryRoot/mismatchedProjectAndDirectoryRoot.test"); + } + @TestMetadata("platformExtensionReceiverOfInline/platformExtensionReceiverOfInline.test") public void testPlatformExtensionReceiverOfInline_PlatformExtensionReceiverOfInline() throws Exception { runTest("idea/testData/multiFileInspections/platformExtensionReceiverOfInline/platformExtensionReceiverOfInline.test"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/MultiFileLocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/MultiFileLocalInspectionTestGenerated.java index 2d31e4c9108..fcd6d6f9ea2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/MultiFileLocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/MultiFileLocalInspectionTestGenerated.java @@ -44,6 +44,11 @@ public class MultiFileLocalInspectionTestGenerated extends AbstractMultiFileLoca runTest("idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectory/moveToDefaultDirectory.test"); } + @TestMetadata("moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/moveToDefaultDirectoryWithoutPackageKeyword.test") + public void testMoveFileToPackageMatchingDirectory_moveToDefaultDirectoryWithoutPackageKeyword_MoveToDefaultDirectoryWithoutPackageKeyword() throws Exception { + runTest("idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToDefaultDirectoryWithoutPackageKeyword/moveToDefaultDirectoryWithoutPackageKeyword.test"); + } + @TestMetadata("moveFileToPackageMatchingDirectory/moveToNonDefaultDirectory/moveToNonDefaultDirectory.test") public void testMoveFileToPackageMatchingDirectory_moveToNonDefaultDirectory_MoveToNonDefaultDirectory() throws Exception { runTest("idea/testData/multiFileLocalInspections/moveFileToPackageMatchingDirectory/moveToNonDefaultDirectory/moveToNonDefaultDirectory.test"); @@ -69,6 +74,11 @@ public class MultiFileLocalInspectionTestGenerated extends AbstractMultiFileLoca runTest("idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackage/changeToNonDefaultPackage.test"); } + @TestMetadata("reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/changeToNonDefaultPackageFromRoot.test") + public void testReconcilePackageWithDirectory_changeToNonDefaultPackageFromRoot_ChangeToNonDefaultPackageFromRoot() throws Exception { + runTest("idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/changeToNonDefaultPackageFromRoot/changeToNonDefaultPackageFromRoot.test"); + } + @TestMetadata("reconcilePackageWithDirectory/innerClass/innerClass.test") public void testReconcilePackageWithDirectory_innerClass_InnerClass() throws Exception { runTest("idea/testData/multiFileLocalInspections/reconcilePackageWithDirectory/innerClass/innerClass.test");