From 19093e2e02b19a042e2cd8712663bc38983278df Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Sat, 15 Feb 2020 10:17:30 +0900 Subject: [PATCH] Redundant companion reference: fix false positive when companion has same name member as companion name #KT-36707 Fixed --- .../RedundantCompanionReferenceInspection.kt | 26 ++++++++++++++----- .../namedWithSameNameFunction.kt | 13 ++++++++++ .../namedWithSameNameFunction2.kt | 12 +++++++++ .../namedWithSameNameFunction2.kt.after | 12 +++++++++ .../namedWithSameNameProperty.kt | 10 +++++++ .../LocalInspectionTestGenerated.java | 15 +++++++++++ 6 files changed, 81 insertions(+), 7 deletions(-) create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction.kt create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt.after create mode 100644 idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameProperty.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantCompanionReferenceInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantCompanionReferenceInspection.kt index 3b88750e41f..63ebe0da051 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantCompanionReferenceInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantCompanionReferenceInspection.kt @@ -12,6 +12,7 @@ import com.intellij.codeInspection.ProblemsHolder import com.intellij.openapi.project.Project import com.intellij.psi.PsiElementVisitor import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.idea.analysis.analyzeAsReplacement import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.references.mainReference import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor @@ -25,6 +26,7 @@ import org.jetbrains.kotlin.psi.psiUtil.containingClass import org.jetbrains.kotlin.psi.psiUtil.getQualifiedElementSelector import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny @@ -50,19 +52,22 @@ class RedundantCompanionReferenceInspection : AbstractKotlinInspection() { companion object { fun isRedundantCompanionReference(reference: KtReferenceExpression): Boolean { val parent = reference.parent as? KtDotQualifiedExpression ?: return false + val grandParent = parent.parent val selectorExpression = parent.selectorExpression - if (reference == selectorExpression && parent.parent !is KtDotQualifiedExpression) return false + if (reference == selectorExpression && grandParent !is KtDotQualifiedExpression) return false if (parent.getStrictParentOfType() != null) return false val objectDeclaration = reference.mainReference.resolve() as? KtObjectDeclaration ?: return false if (!objectDeclaration.isCompanion()) return false - if (reference.text != objectDeclaration.name) return false - - val context = reference.analyze() + val referenceText = reference.text + if (referenceText != objectDeclaration.name) return false + if (reference != selectorExpression && referenceText == (selectorExpression as? KtNameReferenceExpression)?.text) return false val containingClass = objectDeclaration.containingClass() ?: return false if (reference.containingClass() != containingClass && reference == parent.receiverExpression) return false - val containingClassDescriptor = containingClass.descriptor as? ClassDescriptor ?: return false + val context = reference.analyze() + val containingClassDescriptor = + context[BindingContext.DECLARATION_TO_DESCRIPTOR, containingClass] as? ClassDescriptor ?: return false when (val selectorDescriptor = selectorExpression?.getResolvedCall(context)?.resultingDescriptor) { is PropertyDescriptor -> { val name = selectorDescriptor.name @@ -89,11 +94,18 @@ class RedundantCompanionReferenceInspection : AbstractKotlinInspection() { ?.mainReference?.resolveToDescriptors(context)?.firstOrNull() ?.let { if (it != containingClassDescriptor) return false } - val grandParent = parent.parent as? KtQualifiedExpression - if (grandParent != null) { + if (grandParent is KtQualifiedExpression) { val grandParentDescriptor = grandParent.getResolvedCall(context)?.resultingDescriptor ?: return false if (grandParentDescriptor is ConstructorDescriptor || grandParentDescriptor is FakeCallableDescriptorForObject) return false } + + if (selectorExpression is KtCallExpression && referenceText == selectorExpression.calleeExpression?.text) { + val newExpression = KtPsiFactory(reference).createExpressionByPattern("$0", selectorExpression) + val newContext = newExpression.analyzeAsReplacement(parent, context) + val descriptor = newExpression.getResolvedCall(newContext)?.resultingDescriptor as? FunctionDescriptor + if (descriptor?.isOperator == true) return false + } + return true } } diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction.kt b/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction.kt new file mode 100644 index 00000000000..c6b371aa4f2 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction.kt @@ -0,0 +1,13 @@ +// PROBLEM: none +class B { + companion object foo { + fun foo() {} + + operator fun invoke() { + } + } + + fun test() { + foo.foo() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt b/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt new file mode 100644 index 00000000000..ac5d14d96a4 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt @@ -0,0 +1,12 @@ +class C { + companion object foo { + fun foo() {} + + operator fun invoke(i: Int) { + } + } + + fun test() { + foo.foo() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt.after b/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt.after new file mode 100644 index 00000000000..57d2413ca17 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt.after @@ -0,0 +1,12 @@ +class C { + companion object foo { + fun foo() {} + + operator fun invoke(i: Int) { + } + } + + fun test() { + foo() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameProperty.kt b/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameProperty.kt new file mode 100644 index 00000000000..b8e4ad94261 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameProperty.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +class A { + companion object foo { + val foo = 1 + } + + fun test() { + foo.foo + } +} \ 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 158cb6c5d86..73e800c2ac1 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -6950,6 +6950,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/redundantCompanionReference/named.kt"); } + @TestMetadata("namedWithSameNameFunction.kt") + public void testNamedWithSameNameFunction() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction.kt"); + } + + @TestMetadata("namedWithSameNameFunction2.kt") + public void testNamedWithSameNameFunction2() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameFunction2.kt"); + } + + @TestMetadata("namedWithSameNameProperty.kt") + public void testNamedWithSameNameProperty() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantCompanionReference/namedWithSameNameProperty.kt"); + } + @TestMetadata("notApplicableCollision.kt") public void testNotApplicableCollision() throws Exception { runTest("idea/testData/inspectionsLocal/redundantCompanionReference/notApplicableCollision.kt");