Redundant companion reference: fix false positive when companion has same name member as companion name
#KT-36707 Fixed
This commit is contained in:
committed by
Vladimir Dolzhenko
parent
98ce49ba73
commit
19093e2e02
+19
-7
@@ -12,6 +12,7 @@ import com.intellij.codeInspection.ProblemsHolder
|
|||||||
import com.intellij.openapi.project.Project
|
import com.intellij.openapi.project.Project
|
||||||
import com.intellij.psi.PsiElementVisitor
|
import com.intellij.psi.PsiElementVisitor
|
||||||
import org.jetbrains.kotlin.descriptors.*
|
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.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.references.mainReference
|
import org.jetbrains.kotlin.idea.references.mainReference
|
||||||
import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
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.getQualifiedElementSelector
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
import org.jetbrains.kotlin.psi.psiUtil.getReceiverExpression
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
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.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
import org.jetbrains.kotlin.resolve.calls.util.FakeCallableDescriptorForObject
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
import org.jetbrains.kotlin.resolve.descriptorUtil.getSuperClassNotAny
|
||||||
@@ -50,19 +52,22 @@ class RedundantCompanionReferenceInspection : AbstractKotlinInspection() {
|
|||||||
companion object {
|
companion object {
|
||||||
fun isRedundantCompanionReference(reference: KtReferenceExpression): Boolean {
|
fun isRedundantCompanionReference(reference: KtReferenceExpression): Boolean {
|
||||||
val parent = reference.parent as? KtDotQualifiedExpression ?: return false
|
val parent = reference.parent as? KtDotQualifiedExpression ?: return false
|
||||||
|
val grandParent = parent.parent
|
||||||
val selectorExpression = parent.selectorExpression
|
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
|
if (parent.getStrictParentOfType<KtImportDirective>() != null) return false
|
||||||
|
|
||||||
val objectDeclaration = reference.mainReference.resolve() as? KtObjectDeclaration ?: return false
|
val objectDeclaration = reference.mainReference.resolve() as? KtObjectDeclaration ?: return false
|
||||||
if (!objectDeclaration.isCompanion()) return false
|
if (!objectDeclaration.isCompanion()) return false
|
||||||
if (reference.text != objectDeclaration.name) return false
|
val referenceText = reference.text
|
||||||
|
if (referenceText != objectDeclaration.name) return false
|
||||||
val context = reference.analyze()
|
if (reference != selectorExpression && referenceText == (selectorExpression as? KtNameReferenceExpression)?.text) return false
|
||||||
|
|
||||||
val containingClass = objectDeclaration.containingClass() ?: return false
|
val containingClass = objectDeclaration.containingClass() ?: return false
|
||||||
if (reference.containingClass() != containingClass && reference == parent.receiverExpression) 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) {
|
when (val selectorDescriptor = selectorExpression?.getResolvedCall(context)?.resultingDescriptor) {
|
||||||
is PropertyDescriptor -> {
|
is PropertyDescriptor -> {
|
||||||
val name = selectorDescriptor.name
|
val name = selectorDescriptor.name
|
||||||
@@ -89,11 +94,18 @@ class RedundantCompanionReferenceInspection : AbstractKotlinInspection() {
|
|||||||
?.mainReference?.resolveToDescriptors(context)?.firstOrNull()
|
?.mainReference?.resolveToDescriptors(context)?.firstOrNull()
|
||||||
?.let { if (it != containingClassDescriptor) return false }
|
?.let { if (it != containingClassDescriptor) return false }
|
||||||
|
|
||||||
val grandParent = parent.parent as? KtQualifiedExpression
|
if (grandParent is KtQualifiedExpression) {
|
||||||
if (grandParent != null) {
|
|
||||||
val grandParentDescriptor = grandParent.getResolvedCall(context)?.resultingDescriptor ?: return false
|
val grandParentDescriptor = grandParent.getResolvedCall(context)?.resultingDescriptor ?: return false
|
||||||
if (grandParentDescriptor is ConstructorDescriptor || grandParentDescriptor is FakeCallableDescriptorForObject) 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
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
class B {
|
||||||
|
companion object foo {
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
operator fun invoke() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
<caret>foo.foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
class C {
|
||||||
|
companion object foo {
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
operator fun invoke(i: Int) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
<caret>foo.foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
Vendored
+12
@@ -0,0 +1,12 @@
|
|||||||
|
class C {
|
||||||
|
companion object foo {
|
||||||
|
fun foo() {}
|
||||||
|
|
||||||
|
operator fun invoke(i: Int) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
foo()
|
||||||
|
}
|
||||||
|
}
|
||||||
+10
@@ -0,0 +1,10 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
class A {
|
||||||
|
companion object foo {
|
||||||
|
val foo = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
<caret>foo.foo
|
||||||
|
}
|
||||||
|
}
|
||||||
+15
@@ -6950,6 +6950,21 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/named.kt");
|
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")
|
@TestMetadata("notApplicableCollision.kt")
|
||||||
public void testNotApplicableCollision() throws Exception {
|
public void testNotApplicableCollision() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/notApplicableCollision.kt");
|
runTest("idea/testData/inspectionsLocal/redundantCompanionReference/notApplicableCollision.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user