Convert put to assignment: don't report when receiver object has custom 'set' method
#KT-33212 Fixed
This commit is contained in:
committed by
Yan Zhulanow
parent
5abb6bc7a5
commit
653e20dcba
+22
-12
@@ -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<KtDotQualifiedExpression>(
|
||||
@@ -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<KtReturnExpression>()?.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()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import java.util.HashMap;
|
||||
|
||||
public class Foo extends HashMap<String, String> {
|
||||
public void set(String x, String y) {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
// PROBLEM: none
|
||||
fun test() {
|
||||
val foo = Foo()
|
||||
foo.<caret>put("a", "b")
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
class Foo : HashMap<String, String>() {
|
||||
operator fun set(x: String, y: String) {
|
||||
println("wrong method")
|
||||
}
|
||||
}
|
||||
|
||||
fun main() {
|
||||
Foo().<caret>put("x", "y")
|
||||
}
|
||||
+10
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user