Recursive equals: fix false positive with different argument

Related to KT-13702
This commit is contained in:
Mikhail Glukhikh
2017-11-28 11:20:44 +03:00
parent 917be5d271
commit 13e0ba0836
3 changed files with 23 additions and 4 deletions
@@ -26,6 +26,7 @@ import org.jetbrains.kotlin.idea.search.usagesSearch.descriptor
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.psi.*
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.lazy.BodyResolveMode
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 {
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 resolvedCall = getResolvedCall(context)
val dispatchReceiver = resolvedCall?.dispatchReceiver as? ThisClassReceiver ?: return false
val argumentDescriptor = context[BindingContext.REFERENCE_TARGET, argumentExpr] ?: return false
val calledFunctionDescriptor = resolvedCall.resultingDescriptor as? FunctionDescriptor
if (calledFunctionDescriptor?.isAnyEquals() != true) return false
val containingFunctionDescriptor = getNonStrictParentOfType<KtNamedFunction>()?.descriptor as? FunctionDescriptor
return calledFunctionDescriptor == containingFunctionDescriptor &&
dispatchReceiver.classDescriptor == containingFunctionDescriptor.containingDeclaration
dispatchReceiver.classDescriptor == containingFunctionDescriptor.containingDeclaration &&
argumentDescriptor == containingFunctionDescriptor.valueParameters.singleOrNull()
}
private fun KtExpression.reportRecursiveEquals() {
@@ -58,7 +62,7 @@ class RecursiveEqualsCallInspection : AbstractKotlinInspection() {
super.visitBinaryExpression(expr)
if (expr.operationToken != KtTokens.EQEQ) return
if (!expr.isRecursiveEquals()) return
if (!expr.isRecursiveEquals(expr.right)) return
expr.reportRecursiveEquals()
}
@@ -68,7 +72,7 @@ class RecursiveEqualsCallInspection : AbstractKotlinInspection() {
if (calleeExpression.getReferencedNameAsName() != OperatorNameConventions.EQUALS) return
if (expr.parent is KtSafeQualifiedExpression) return
if (!expr.isRecursiveEquals()) return
if (!expr.isRecursiveEquals(expr.valueArguments.singleOrNull()?.getArgumentExpression())) return
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);
}
@TestMetadata("recursiveFakeNoOther.kt")
public void testRecursiveFakeNoOther() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/recursiveEqualsCall/recursiveFakeNoOther.kt");
doTest(fileName);
}
@TestMetadata("recursiveImplicit.kt")
public void testRecursiveImplicit() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/recursiveEqualsCall/recursiveImplicit.kt");