diff --git a/plugins/kotlin/idea/resources-en/inspectionDescriptions/ReplaceWithImportAlias.html b/plugins/kotlin/idea/resources-en/inspectionDescriptions/ReplaceWithImportAlias.html new file mode 100644 index 000000000000..f0e2bb362661 --- /dev/null +++ b/plugins/kotlin/idea/resources-en/inspectionDescriptions/ReplaceWithImportAlias.html @@ -0,0 +1,19 @@ + + +Reports fully qualified names that can be replaced with an existing import alias. +

Example:

+

+import foo.Foo as Bar
+fun main() {
+    foo.Foo()
+}
+
+

After the quick-fix is applied:

+

+import foo.Foo as Bar
+fun main() {
+    Bar()
+}
+
+ + diff --git a/plugins/kotlin/idea/resources-en/messages/KotlinBundle.properties b/plugins/kotlin/idea/resources-en/messages/KotlinBundle.properties index e56f22547b6b..12121d021726 100644 --- a/plugins/kotlin/idea/resources-en/messages/KotlinBundle.properties +++ b/plugins/kotlin/idea/resources-en/messages/KotlinBundle.properties @@ -2375,3 +2375,6 @@ floating.point.literal.precision.inspection=Floating-point literal cannot be rep inspection.replace.mapIndexed.with.list.generator.display.name=Replace 'mapIndexed' with List generator should.be.replaced.with.list.generator=Should be replaced with List generator replace.with.list.generator.fix.text=Replace with List generator + +inspection.replace.with.import.alias.display.name=Fully qualified name can be replaced with existing import alias +replace.with.import.alias=Replace with import alias diff --git a/plugins/kotlin/idea/resources/META-INF/inspections.xml b/plugins/kotlin/idea/resources/META-INF/inspections.xml index d5490eb9445a..718a52280dc0 100644 --- a/plugins/kotlin/idea/resources/META-INF/inspections.xml +++ b/plugins/kotlin/idea/resources/META-INF/inspections.xml @@ -2672,6 +2672,14 @@ language="kotlin" key="inspection.replace.mapIndexed.with.list.generator.display.name" bundle="messages.KotlinBundle"/> + + \ No newline at end of file diff --git a/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceWithImportAliasInspection.kt b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceWithImportAliasInspection.kt new file mode 100644 index 000000000000..864977dc2a77 --- /dev/null +++ b/plugins/kotlin/idea/src/org/jetbrains/kotlin/idea/inspections/ReplaceWithImportAliasInspection.kt @@ -0,0 +1,51 @@ +// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file. + +package org.jetbrains.kotlin.idea.inspections + +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElement +import com.intellij.psi.SmartPsiElementPointer +import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.imports.importableFqName +import org.jetbrains.kotlin.idea.references.resolveMainReferenceToDescriptors +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer +import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElement +import org.jetbrains.kotlin.psi.psiUtil.isInImportDirective +import org.jetbrains.kotlin.utils.addToStdlib.safeAs + +class ReplaceWithImportAliasInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean) = simpleNameExpressionVisitor (fun(expression) { + if (expression !is KtNameReferenceExpression || expression.getIdentifier() == null || expression.isInImportDirective()) return + val qualifiedElement = expression.getQualifiedElement() + if (qualifiedElement !is KtDotQualifiedExpression && qualifiedElement !is KtUserType) return + val aliasNameIdentifier = expression.aliasNameIdentifier() ?: return + holder.registerProblem( + expression, + KotlinBundle.message("replace.with.import.alias"), + ReplaceWithImportAliasFix(aliasNameIdentifier.createSmartPointer(), aliasNameIdentifier.text) + ) + }) + + private fun KtNameReferenceExpression.aliasNameIdentifier(): PsiElement? { + val fqName = resolveMainReferenceToDescriptors().firstOrNull()?.importableFqName ?: return null + return containingKtFile.findAliasByFqName(fqName)?.nameIdentifier + } + + private class ReplaceWithImportAliasFix( + private val aliasNameIdentifierPointer: SmartPsiElementPointer, + private val aliasName: String + ): LocalQuickFix { + override fun getName() = KotlinBundle.message("replace.with.0", aliasName) + override fun getFamilyName() = name + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val expression = descriptor.psiElement as? KtNameReferenceExpression ?: return + val aliasNameIdentifier = aliasNameIdentifierPointer.element ?: return + expression.getIdentifier()?.replace(aliasNameIdentifier.copy()) + expression.getQualifiedElement().replace(expression.parent.safeAs() ?: expression) + } + } +} diff --git a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index eb49d1d59d6e..64d31528aff6 100644 --- a/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/plugins/kotlin/idea/tests/test/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -12978,6 +12978,54 @@ public abstract class LocalInspectionTestGenerated extends AbstractLocalInspecti } } + @RunWith(JUnit3RunnerWithInners.class) + @TestMetadata("testData/inspectionsLocal/replaceWithImportAlias") + public static class ReplaceWithImportAlias extends AbstractLocalInspectionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); + } + + @TestMetadata("class.kt") + public void testClass() throws Exception { + runTest("testData/inspectionsLocal/replaceWithImportAlias/class.kt"); + } + + @TestMetadata("extension.kt") + public void testExtension() throws Exception { + runTest("testData/inspectionsLocal/replaceWithImportAlias/extension.kt"); + } + + @TestMetadata("function.kt") + public void testFunction() throws Exception { + runTest("testData/inspectionsLocal/replaceWithImportAlias/function.kt"); + } + + @TestMetadata("inImport.kt") + public void testInImport() throws Exception { + runTest("testData/inspectionsLocal/replaceWithImportAlias/inImport.kt"); + } + + @TestMetadata("multipleAliases.kt") + public void testMultipleAliases() throws Exception { + runTest("testData/inspectionsLocal/replaceWithImportAlias/multipleAliases.kt"); + } + + @TestMetadata("notQualifed.kt") + public void testNotQualifed() throws Exception { + runTest("testData/inspectionsLocal/replaceWithImportAlias/notQualifed.kt"); + } + + @TestMetadata("property.kt") + public void testProperty() throws Exception { + runTest("testData/inspectionsLocal/replaceWithImportAlias/property.kt"); + } + + @TestMetadata("type.kt") + public void testType() throws Exception { + runTest("testData/inspectionsLocal/replaceWithImportAlias/type.kt"); + } + } + @RunWith(JUnit3RunnerWithInners.class) @TestMetadata("testData/inspectionsLocal/replaceWithOperatorAssignment") public static class ReplaceWithOperatorAssignment extends AbstractLocalInspectionTest { diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/.inspection b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/.inspection new file mode 100644 index 000000000000..365d2bd9e3ee --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.ReplaceWithImportAliasInspection \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/class.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/class.kt new file mode 100644 index 000000000000..a2aa3dc65900 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/class.kt @@ -0,0 +1,10 @@ +package foo + +import foo.Foo as Bar + +class Foo + +fun main() { + foo.Foo() + foo.Foo() +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/class.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/class.kt.after new file mode 100644 index 000000000000..6ed34da8545d --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/class.kt.after @@ -0,0 +1,10 @@ +package foo + +import foo.Foo as Bar + +class Foo + +fun main() { + Bar() + foo.Foo() +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/extension.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/extension.kt new file mode 100644 index 000000000000..34445905fe5c --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/extension.kt @@ -0,0 +1,7 @@ +package foo + +import foo.Foo as Bar + +class Foo + +fun foo.Foo.test() {} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/extension.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/extension.kt.after new file mode 100644 index 000000000000..e36962f5c979 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/extension.kt.after @@ -0,0 +1,7 @@ +package foo + +import foo.Foo as Bar + +class Foo + +fun Bar.test() {} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/function.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/function.kt new file mode 100644 index 000000000000..197f7f3662fb --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/function.kt @@ -0,0 +1,10 @@ +package foo + +import foo.foo as bar + +fun foo() {} + +fun main() { + foo.foo() + foo.foo() +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/function.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/function.kt.after new file mode 100644 index 000000000000..48f15ad1eb50 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/function.kt.after @@ -0,0 +1,10 @@ +package foo + +import foo.foo as bar + +fun foo() {} + +fun main() { + bar() + foo.foo() +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/inImport.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/inImport.kt new file mode 100644 index 000000000000..844a05b1fdb5 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/inImport.kt @@ -0,0 +1,6 @@ +// PROBLEM: none +package foo + +import foo.Foo as Bar + +class Foo diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/multipleAliases.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/multipleAliases.kt new file mode 100644 index 000000000000..ff8cdd17a852 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/multipleAliases.kt @@ -0,0 +1,10 @@ +package foo + +import foo.Foo as Bar +import foo.Foo as Baz + +class Foo + +fun main() { + foo.Foo() +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/multipleAliases.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/multipleAliases.kt.after new file mode 100644 index 000000000000..67b7dcf67eae --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/multipleAliases.kt.after @@ -0,0 +1,10 @@ +package foo + +import foo.Foo as Bar +import foo.Foo as Baz + +class Foo + +fun main() { + Bar() +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/notQualifed.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/notQualifed.kt new file mode 100644 index 000000000000..d2b8be6f0e37 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/notQualifed.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +package foo + +import foo.Foo as Bar + +class Foo + +fun main() { + Bar() +} diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/property.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/property.kt new file mode 100644 index 000000000000..016af8f49c0e --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/property.kt @@ -0,0 +1,10 @@ +package foo + +import foo.foo as bar + +val foo = 1 + +fun main() { + foo.foo + foo.foo +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/property.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/property.kt.after new file mode 100644 index 000000000000..ab56d1769c56 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/property.kt.after @@ -0,0 +1,10 @@ +package foo + +import foo.foo as bar + +val foo = 1 + +fun main() { + foo.foo + bar +} \ No newline at end of file diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/type.kt b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/type.kt new file mode 100644 index 000000000000..29d73dafd069 --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/type.kt @@ -0,0 +1,7 @@ +package foo + +import foo.Foo as Bar + +class Foo + +fun test(): foo.Foo? = null diff --git a/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/type.kt.after b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/type.kt.after new file mode 100644 index 000000000000..acb29e81710e --- /dev/null +++ b/plugins/kotlin/idea/tests/testData/inspectionsLocal/replaceWithImportAlias/type.kt.after @@ -0,0 +1,7 @@ +package foo + +import foo.Foo as Bar + +class Foo + +fun test(): Bar? = null