From e6e46809deb50d6c1f97ce08cf2f6d3fdfb1ad32 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Sat, 20 Aug 2016 14:11:57 +0200 Subject: [PATCH] fix "Can be replaced with comparison" false positive if extension method called 'equals' is used Fixes #KT-13480 --- .../ReplaceCallWithBinaryOperatorIntention.kt | 12 +++++++++--- .../equalsExtensionFunction.kt | 11 +++++++++++ .../idea/intentions/IntentionTestGenerated.java | 6 ++++++ 3 files changed, 26 insertions(+), 3 deletions(-) create mode 100644 idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsExtensionFunction.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt index 6ab00e7c2a1..0baa079311f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/conventionNameCalls/ReplaceCallWithBinaryOperatorIntention.kt @@ -32,6 +32,8 @@ import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.getLastParentOfTypeInRow import org.jetbrains.kotlin.resolve.calls.model.ArgumentMatch import org.jetbrains.kotlin.resolve.calls.model.isReallySuccess +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe +import org.jetbrains.kotlin.resolve.findOriginalTopMostOverriddenDescriptors import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.types.expressions.OperatorConventions import org.jetbrains.kotlin.util.OperatorNameConventions @@ -107,16 +109,20 @@ class ReplaceCallWithBinaryOperatorIntention : SelfTargetingRangeIntention { - val prefixExpression = calleeExpression.parent?.parent?.getWrappingPrefixExpressionIfAny() + val resolvedCall = dotQualified.toResolvedCall(BodyResolveMode.PARTIAL) ?: return null + val overriddenDescriptors = resolvedCall.resultingDescriptor.findOriginalTopMostOverriddenDescriptors() + if (overriddenDescriptors.none { it.fqNameUnsafe.asString() == "kotlin.Any.equals" }) return null + + val prefixExpression = dotQualified.getWrappingPrefixExpressionIfAny() if (prefixExpression != null && prefixExpression.operationToken == KtTokens.EXCL) KtTokens.EXCLEQ else KtTokens.EQEQ } OperatorNameConventions.COMPARE_TO -> { // callee -> call -> DotQualified -> Binary - val dotQualified = calleeExpression.parent?.parent - val binaryParent = dotQualified?.parent as? KtBinaryExpression ?: return null + val binaryParent = dotQualified.parent as? KtBinaryExpression ?: return null val notZero = when { binaryParent.right?.text == "0" -> binaryParent.left binaryParent.left?.text == "0" -> binaryParent.right diff --git a/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsExtensionFunction.kt b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsExtensionFunction.kt new file mode 100644 index 00000000000..cf6eeae1287 --- /dev/null +++ b/idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsExtensionFunction.kt @@ -0,0 +1,11 @@ +// IS_APPLICABLE: false + +class Foo + +fun Foo?.equals(other: Foo?) = true + +fun bar(f1: Foo?, f2: Foo?) { + if (f1.equals(f2)) { + + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 270fd38ab15..5d28a6fd0a4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -2807,6 +2807,12 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("equalsExtensionFunction.kt") + public void testEqualsExtensionFunction() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/equalsExtensionFunction.kt"); + doTest(fileName); + } + @TestMetadata("extensionFunction.kt") public void testExtensionFunction() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/conventionNameCalls/replaceCallWithBinaryOperator/extensionFunction.kt");