From 653e20dcbab14a5e07694df23b44cfca991879d6 Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Mon, 9 Dec 2019 12:26:02 +0900 Subject: [PATCH] Convert put to assignment: don't report when receiver object has custom 'set' method #KT-33212 Fixed --- .../ReplacePutWithAssignmentInspection.kt | 34 ++++++++++++------- .../replacePutWithAssignment/hasSet.1.java | 6 ++++ .../replacePutWithAssignment/hasSet.kt | 5 +++ .../replacePutWithAssignment/hasSet2.kt | 11 ++++++ .../LocalInspectionTestGenerated.java | 10 ++++++ 5 files changed, 54 insertions(+), 12 deletions(-) create mode 100644 idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.1.java create mode 100644 idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.kt create mode 100644 idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet2.kt diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt index 87a6703b5c3..a4f7ca7fe0f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt @@ -21,16 +21,19 @@ import com.intellij.openapi.project.Project import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.idea.KotlinBundle +import org.jetbrains.kotlin.idea.analysis.analyzeAsReplacement import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.util.calleeTextRangeInThis import org.jetbrains.kotlin.lexer.KtTokens +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.findDescendantOfType import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue +import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameOrNull import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf class ReplacePutWithAssignmentInspection : AbstractApplicabilityBasedInspection( @@ -60,25 +63,32 @@ class ReplacePutWithAssignmentInspection : AbstractApplicabilityBasedInspection< val resolvedCall = element.getResolvedCall(context) val receiverType = resolvedCall?.getExplicitReceiverValue()?.type ?: return false val receiverClass = receiverType.constructor.declarationDescriptor as? ClassDescriptor ?: return false - return receiverClass.isSubclassOf(DefaultBuiltIns.Instance.mutableMap) + if (!receiverClass.isSubclassOf(DefaultBuiltIns.Instance.mutableMap)) return false + + val assignment = createAssignmentExpression(element) ?: return false + val newContext = assignment.analyzeAsReplacement(element, context) + return assignment.left.getResolvedCall(newContext)?.resultingDescriptor?.fqNameOrNull() == FqName("kotlin.collections.set") } override fun applyTo(element: KtDotQualifiedExpression, project: Project, editor: Editor?) { - val valueArguments = element.callExpression?.valueArguments ?: return - val firstArg = valueArguments[0]?.getArgumentExpression() ?: return - val secondArg = valueArguments[1]?.getArgumentExpression() ?: return + val assignment = createAssignmentExpression(element) ?: return + element.replace(assignment) + } + + private fun createAssignmentExpression(element: KtDotQualifiedExpression): KtBinaryExpression? { + val valueArguments = element.callExpression?.valueArguments ?: return null + val firstArg = valueArguments[0]?.getArgumentExpression() ?: return null + val secondArg = valueArguments[1]?.getArgumentExpression() ?: return null val label = if (secondArg is KtLambdaExpression) { val returnLabel = secondArg.findDescendantOfType()?.getLabelName() compatibleNames.firstOrNull { it == returnLabel }?.plus("@") ?: "" } else "" - element.replace( - KtPsiFactory(element).createExpressionByPattern( - "$0[$1] = $label$2", - element.receiverExpression, - firstArg, - secondArg - ) - ) + return KtPsiFactory(element).createExpressionByPattern( + "$0[$1] = $label$2", + element.receiverExpression, + firstArg, + secondArg + ) as? KtBinaryExpression } override fun inspectionHighlightRangeInElement(element: KtDotQualifiedExpression) = element.calleeTextRangeInThis() diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.1.java b/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.1.java new file mode 100644 index 00000000000..77e563dde95 --- /dev/null +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.1.java @@ -0,0 +1,6 @@ +import java.util.HashMap; + +public class Foo extends HashMap { + public void set(String x, String y) { + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.kt new file mode 100644 index 00000000000..d3309085574 --- /dev/null +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.kt @@ -0,0 +1,5 @@ +// PROBLEM: none +fun test() { + val foo = Foo() + foo.put("a", "b") +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet2.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet2.kt new file mode 100644 index 00000000000..6c1336ec3ad --- /dev/null +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet2.kt @@ -0,0 +1,11 @@ +// PROBLEM: none +// WITH_RUNTIME +class Foo : HashMap() { + operator fun set(x: String, y: String) { + println("wrong method") + } +} + +fun main() { + Foo().put("x", "y") +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index aa275db46b4..27e964142c4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -11593,6 +11593,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/replacePutWithAssignment/beforeElvis.kt"); } + @TestMetadata("hasSet.kt") + public void testHasSet() throws Exception { + runTest("idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet.kt"); + } + + @TestMetadata("hasSet2.kt") + public void testHasSet2() throws Exception { + runTest("idea/testData/inspectionsLocal/replacePutWithAssignment/hasSet2.kt"); + } + @TestMetadata("nonMap.kt") public void testNonMap() throws Exception { runTest("idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt");