Redundant companion reference: fix false positive when companion has same name member as companion name

#KT-36707 Fixed
This commit is contained in:
Toshiaki Kameyama
2020-02-15 10:17:30 +09:00
committed by Vladimir Dolzhenko
parent 98ce49ba73
commit 19093e2e02
6 changed files with 81 additions and 7 deletions
@@ -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<KtImportDirective>() != 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
}
}
@@ -0,0 +1,13 @@
// PROBLEM: none
class B {
companion object foo {
fun foo() {}
operator fun invoke() {
}
}
fun test() {
<caret>foo.foo()
}
}
@@ -0,0 +1,12 @@
class C {
companion object foo {
fun foo() {}
operator fun invoke(i: Int) {
}
}
fun test() {
<caret>foo.foo()
}
}
@@ -0,0 +1,12 @@
class C {
companion object foo {
fun foo() {}
operator fun invoke(i: Int) {
}
}
fun test() {
foo()
}
}
@@ -0,0 +1,10 @@
// PROBLEM: none
class A {
companion object foo {
val foo = 1
}
fun test() {
<caret>foo.foo
}
}
@@ -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");