From 3fbf85dc37106d573714ad99cf6d9f3498e1e671 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Thu, 8 Mar 2018 05:32:02 +0300 Subject: [PATCH] Add inspection to highlight unnecessary explicit companion references So #KT-22971 Fixed --- .../RedundantCompanionReference.html | 17 ++++++ idea/src/META-INF/plugin.xml | 9 ++++ .../RedundantCompanionReferenceInspection.kt | 53 +++++++++++++++++++ .../redundantCompanionReference/.inspection | 1 + .../redundantCompanionReference/basic.kt | 9 ++++ .../basic.kt.after | 9 ++++ .../directCompanion.kt | 8 +++ .../directCompanion.kt.after | 8 +++ .../functionReference.kt | 8 +++ .../notCompanion.kt | 10 ++++ .../onlyCompanion.kt | 10 ++++ .../LocalInspectionTestGenerated.java | 39 ++++++++++++++ 12 files changed, 181 insertions(+) create mode 100644 idea/resources/inspectionDescriptions/RedundantCompanionReference.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/inspections/RedundantCompanionReferenceInspection.kt create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/.inspection create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/functionReference.kt create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/notCompanion.kt create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/onlyCompanion.kt diff --git a/idea/resources/inspectionDescriptions/RedundantCompanionReference.html b/idea/resources/inspectionDescriptions/RedundantCompanionReference.html new file mode 100644 index 00000000000..fc4250288bb --- /dev/null +++ b/idea/resources/inspectionDescriptions/RedundantCompanionReference.html @@ -0,0 +1,17 @@ + + +This inspection reports redundant Companion reference, for example: +

+ +
+class A {
+    companion object {
+        fun create() = "Hello"
+    }
+}
+fun test() {
+    val s = A.Companion.create() // redundant Companion reference
+}
+
+ + diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 42b1bafd1bc..a29c1cc0526 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -2746,6 +2746,15 @@ language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantCompanionReferenceInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantCompanionReferenceInspection.kt new file mode 100644 index 00000000000..62cdb233971 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantCompanionReferenceInspection.kt @@ -0,0 +1,53 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.inspections + +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.psi.PsiElementVisitor +import org.jetbrains.kotlin.idea.references.mainReference +import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor +import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.DescriptorUtils + +class RedundantCompanionReferenceInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { + return referenceExpressionVisitor(fun(expression) { + + val descriptor = (expression.mainReference.resolve() as? KtObjectDeclaration)?.descriptor ?: return + if (!DescriptorUtils.isCompanionObject(descriptor)) return + + val parent = expression.getStrictParentOfType() ?: return + if (expression == parent.receiverExpression && expression.text == descriptor.containingDeclaration?.name?.asString()) return + if (expression == parent.selectorExpression && parent.parent !is KtDotQualifiedExpression) return + + holder.registerProblem( + expression, + "Redundant Companion reference", + ProblemHighlightType.LIKE_UNUSED_SYMBOL, + RemoveRedundantCompanionReferenceFix() + ) + }) + } +} + +private class RemoveRedundantCompanionReferenceFix : LocalQuickFix { + override fun getName() = "Remove redundant Companion reference" + + override fun getFamilyName() = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + val expression = descriptor.psiElement as? KtReferenceExpression ?: return + val parent = expression.getStrictParentOfType() ?: return + val selector = parent.selectorExpression ?: return + val receiver = parent.receiverExpression + if (expression == receiver) parent.replace(selector) else parent.replace(receiver) + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/.inspection b/idea/testData/inspectionsLocal/redundantCompanionReference/.inspection new file mode 100644 index 00000000000..e9affe4e724 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.RedundantCompanionReferenceInspection \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt b/idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt new file mode 100644 index 00000000000..a249c7d559a --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt @@ -0,0 +1,9 @@ +class C { + companion object { + fun create() = C() + } +} + +fun test() { + C.Companion.create() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt.after b/idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt.after new file mode 100644 index 00000000000..b60b16ac507 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt.after @@ -0,0 +1,9 @@ +class C { + companion object { + fun create() = C() + } +} + +fun test() { + C.create() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt b/idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt new file mode 100644 index 00000000000..3ff8434f01b --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt @@ -0,0 +1,8 @@ +class C { + companion object { + fun create() = C() + } + fun test() { + Companion.create() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt.after b/idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt.after new file mode 100644 index 00000000000..c7fcdacd27c --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt.after @@ -0,0 +1,8 @@ +class C { + companion object { + fun create() = C() + } + fun test() { + create() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/functionReference.kt b/idea/testData/inspectionsLocal/redundantCompanionReference/functionReference.kt new file mode 100644 index 00000000000..86649604a4e --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/functionReference.kt @@ -0,0 +1,8 @@ +// PROBLEM: none +class C { + companion object { + fun foo() {} + } +} + +fun test() = C.Companion::foo \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/notCompanion.kt b/idea/testData/inspectionsLocal/redundantCompanionReference/notCompanion.kt new file mode 100644 index 00000000000..77ae62c8e8e --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/notCompanion.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +class C { + companion object { + fun create() = C() + } +} + +fun test() { + C.create() +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/onlyCompanion.kt b/idea/testData/inspectionsLocal/redundantCompanionReference/onlyCompanion.kt new file mode 100644 index 00000000000..77b3af409ec --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/onlyCompanion.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +class C { + companion object { + fun create() = C() + } +} + +fun test() { + C.Companion +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index f94fb23b9a8..3b29c7c7afe 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -2515,6 +2515,45 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/redundantCompanionReference") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class RedundantCompanionReference extends AbstractLocalInspectionTest { + public void testAllFilesPresentInRedundantCompanionReference() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantCompanionReference"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("basic.kt") + public void testBasic() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantCompanionReference/basic.kt"); + doTest(fileName); + } + + @TestMetadata("directCompanion.kt") + public void testDirectCompanion() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantCompanionReference/directCompanion.kt"); + doTest(fileName); + } + + @TestMetadata("functionReference.kt") + public void testFunctionReference() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantCompanionReference/functionReference.kt"); + doTest(fileName); + } + + @TestMetadata("notCompanion.kt") + public void testNotCompanion() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantCompanionReference/notCompanion.kt"); + doTest(fileName); + } + + @TestMetadata("onlyCompanion.kt") + public void testOnlyCompanion() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/redundantCompanionReference/onlyCompanion.kt"); + doTest(fileName); + } + } + @TestMetadata("idea/testData/inspectionsLocal/redundantExplicitType") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)