Recursive equals: fix false positive with different argument
Related to KT-13702
This commit is contained in:
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
|
|||||||
import org.jetbrains.kotlin.lexer.KtTokens
|
import org.jetbrains.kotlin.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getNonStrictParentOfType
|
||||||
|
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.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver
|
import org.jetbrains.kotlin.resolve.scopes.receivers.ThisClassReceiver
|
||||||
@@ -35,16 +36,19 @@ class RecursiveEqualsCallInspection : AbstractKotlinInspection() {
|
|||||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
||||||
return object : KtVisitorVoid() {
|
return object : KtVisitorVoid() {
|
||||||
|
|
||||||
private fun KtExpression.isRecursiveEquals(): Boolean {
|
private fun KtExpression.isRecursiveEquals(argumentExpr: KtExpression?): Boolean {
|
||||||
|
if (argumentExpr !is KtNameReferenceExpression) return false
|
||||||
val context = analyze(BodyResolveMode.PARTIAL)
|
val context = analyze(BodyResolveMode.PARTIAL)
|
||||||
val resolvedCall = getResolvedCall(context)
|
val resolvedCall = getResolvedCall(context)
|
||||||
val dispatchReceiver = resolvedCall?.dispatchReceiver as? ThisClassReceiver ?: return false
|
val dispatchReceiver = resolvedCall?.dispatchReceiver as? ThisClassReceiver ?: return false
|
||||||
|
val argumentDescriptor = context[BindingContext.REFERENCE_TARGET, argumentExpr] ?: return false
|
||||||
val calledFunctionDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor
|
val calledFunctionDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor
|
||||||
if (calledFunctionDescriptor?.isAnyEquals() != true) return false
|
if (calledFunctionDescriptor?.isAnyEquals() != true) return false
|
||||||
|
|
||||||
val containingFunctionDescriptor = getNonStrictParentOfType<KtNamedFunction>()?.descriptor as? FunctionDescriptor
|
val containingFunctionDescriptor = getNonStrictParentOfType<KtNamedFunction>()?.descriptor as? FunctionDescriptor
|
||||||
return calledFunctionDescriptor == containingFunctionDescriptor &&
|
return calledFunctionDescriptor == containingFunctionDescriptor &&
|
||||||
dispatchReceiver.classDescriptor == containingFunctionDescriptor.containingDeclaration
|
dispatchReceiver.classDescriptor == containingFunctionDescriptor.containingDeclaration &&
|
||||||
|
argumentDescriptor == containingFunctionDescriptor.valueParameters.singleOrNull()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtExpression.reportRecursiveEquals() {
|
private fun KtExpression.reportRecursiveEquals() {
|
||||||
@@ -58,7 +62,7 @@ class RecursiveEqualsCallInspection : AbstractKotlinInspection() {
|
|||||||
super.visitBinaryExpression(expr)
|
super.visitBinaryExpression(expr)
|
||||||
if (expr.operationToken != KtTokens.EQEQ) return
|
if (expr.operationToken != KtTokens.EQEQ) return
|
||||||
|
|
||||||
if (!expr.isRecursiveEquals()) return
|
if (!expr.isRecursiveEquals(expr.right)) return
|
||||||
expr.reportRecursiveEquals()
|
expr.reportRecursiveEquals()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -68,7 +72,7 @@ class RecursiveEqualsCallInspection : AbstractKotlinInspection() {
|
|||||||
if (calleeExpression.getReferencedNameAsName() != OperatorNameConventions.EQUALS) return
|
if (calleeExpression.getReferencedNameAsName() != OperatorNameConventions.EQUALS) return
|
||||||
if (expr.parent is KtSafeQualifiedExpression) return
|
if (expr.parent is KtSafeQualifiedExpression) return
|
||||||
|
|
||||||
if (!expr.isRecursiveEquals()) return
|
if (!expr.isRecursiveEquals(expr.valueArguments.singleOrNull()?.getArgumentExpression())) return
|
||||||
expr.reportRecursiveEquals()
|
expr.reportRecursiveEquals()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// PROBLEM: none
|
||||||
|
|
||||||
|
class Test {
|
||||||
|
override fun equals(other: Any?): Boolean {
|
||||||
|
val s = Test()
|
||||||
|
if (<caret>this == s) return true
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1532,6 +1532,12 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
|||||||
doTest(fileName);
|
doTest(fileName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("recursiveFakeNoOther.kt")
|
||||||
|
public void testRecursiveFakeNoOther() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/recursiveEqualsCall/recursiveFakeNoOther.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("recursiveImplicit.kt")
|
@TestMetadata("recursiveImplicit.kt")
|
||||||
public void testRecursiveImplicit() throws Exception {
|
public void testRecursiveImplicit() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/recursiveEqualsCall/recursiveImplicit.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/recursiveEqualsCall/recursiveImplicit.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user