Add "replace map.put with assignment" to J2K

Related to KT-21502
This commit is contained in:
Mikhail Glukhikh
2017-12-05 17:29:58 +03:00
parent 8c305a137f
commit a90e3c2e45
9 changed files with 58 additions and 47 deletions
@@ -21,68 +21,68 @@ import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.TextRange
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.endOffset
import org.jetbrains.kotlin.psi.psiUtil.startOffset
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.isSubclassOf
class ReplacePutWithAssignmentInspection : AbstractKotlinInspection() {
private val compatibleNames = setOf("put")
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(expression)
val callExpression = expression.callExpression ?: return
if (callExpression.valueArguments.size != 2) return
val calleeExpression = callExpression.calleeExpression as? KtNameReferenceExpression ?: return
if (calleeExpression.getReferencedName() !in compatibleNames) return
val context = expression.analyze()
if (expression.isUsedAsExpression(context)) return
val resolvedCall = expression.getResolvedCall(context) ?: return
val receiverType = resolvedCall.getExplicitReceiverValue()?.type ?: return
val receiverClass = receiverType.constructor.declarationDescriptor as? ClassDescriptor ?: return
if (receiverClass.isSubclassOf(DefaultBuiltIns.Instance.mutableMap)) {
val argumentOffset = expression.startOffset
val problemDescriptor = holder.manager.createProblemDescriptor(
calleeExpression,
TextRange(expression.startOffset - argumentOffset,
callExpression.endOffset - argumentOffset),
if (isActiveFor(expression)) {
holder.registerProblem(
expression.callExpression!!.calleeExpression!!,
"map.put() can be converted to assignment",
ProblemHighlightType.WEAK_WARNING,
isOnTheFly,
ReplacePutWithAssignmentQuickfix()
)
holder.registerProblem(problemDescriptor)
}
}
}
}
}
class ReplacePutWithAssignmentQuickfix : LocalQuickFix {
override fun getName() = "Convert put to assignment"
companion object {
private val compatibleNames = setOf("put")
override fun getFamilyName() = name
fun isActiveFor(expression: KtDotQualifiedExpression): Boolean {
val callExpression = expression.callExpression
if (callExpression?.valueArguments?.size != 2) return false
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
val element = descriptor.psiElement as KtNameReferenceExpression
val valueArguments = (element.parent as? KtCallExpression)?.valueArguments ?: return
val qualifiedExpression = element.parent.parent as? KtDotQualifiedExpression ?: return
qualifiedExpression.replace(KtPsiFactory(element).createExpressionByPattern("$0[$1] = $2",
qualifiedExpression.receiverExpression,
valueArguments[0]?.getArgumentExpression() ?: return,
valueArguments[1]?.getArgumentExpression() ?: return))
val calleeExpression = callExpression.calleeExpression as? KtNameReferenceExpression ?: return false
if (calleeExpression.getReferencedName() !in compatibleNames) return false
val context = expression.analyze()
if (expression.isUsedAsExpression(context)) return false
val resolvedCall = expression.getResolvedCall(context)
val receiverType = resolvedCall?.getExplicitReceiverValue()?.type ?: return false
val receiverClass = receiverType.constructor.declarationDescriptor as? ClassDescriptor ?: return false
return receiverClass.isSubclassOf(DefaultBuiltIns.Instance.mutableMap)
}
fun simplify(expression: KtDotQualifiedExpression) {
val valueArguments = expression.callExpression?.valueArguments ?: return
expression.replace(KtPsiFactory(expression).createExpressionByPattern("$0[$1] = $2",
expression.receiverExpression,
valueArguments[0]?.getArgumentExpression() ?: return,
valueArguments[1]?.getArgumentExpression() ?: return))
}
}
class ReplacePutWithAssignmentQuickfix : LocalQuickFix {
override fun getName() = "Convert put to assignment"
override fun getFamilyName() = name
override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
simplify(descriptor.psiElement.parent.parent as? KtDotQualifiedExpression ?: return)
}
}
}
@@ -25,10 +25,7 @@ import org.jetbrains.kotlin.diagnostics.Errors
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.core.replaced
import org.jetbrains.kotlin.idea.core.setVisibility
import org.jetbrains.kotlin.idea.inspections.RedundantSamConstructorInspection
import org.jetbrains.kotlin.idea.inspections.UnnecessaryVariableInspection
import org.jetbrains.kotlin.idea.inspections.UseExpressionBodyInspection
import org.jetbrains.kotlin.idea.inspections.findExistingEditor
import org.jetbrains.kotlin.idea.inspections.*
import org.jetbrains.kotlin.idea.intentions.*
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnAsymmetricallyIntention
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.FoldIfToReturnIntention
@@ -81,6 +78,7 @@ object J2KPostProcessingRegistrar {
_processings.add(UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing())
_processings.add(RemoveRedundantSamAdaptersProcessing())
_processings.add(RemoveRedundantCastToNullableProcessing())
_processings.add(ReplacePutWithAssignmentProcessing())
_processings.add(UseExpressionBodyProcessing())
_processings.add(UnnecessaryVariableProcessing())
@@ -274,6 +272,19 @@ object J2KPostProcessingRegistrar {
}
}
private class ReplacePutWithAssignmentProcessing : J2kPostProcessing {
override val writeActionNeeded = true
override fun createAction(element: KtElement, diagnostics: Diagnostics): (() -> Unit)? {
if (element !is KtDotQualifiedExpression) return null
if (!ReplacePutWithAssignmentInspection.isActiveFor(element)) return null
return {
ReplacePutWithAssignmentInspection.simplify(element)
}
}
}
private class UseExpressionBodyProcessing : J2kPostProcessing {
override val writeActionNeeded = true
@@ -6,5 +6,5 @@ class A {
}
fun foo() {
A().put<caret>(1, "foo")
A().<caret>put(1, "foo")
}
@@ -3,4 +3,4 @@
val map = mutableMapOf(42 to "foo")
fun foo() = map.put<caret>(60, "bar")
fun foo() = map.<caret>put(60, "bar")
@@ -1,5 +1,5 @@
// WITH_RUNTIME
fun foo(map: MutableMap<Int, String>) {
map.put<caret>(42, "foo")
map.<caret>put(42, "foo")
}
@@ -2,6 +2,6 @@
class MyMap() : HashMap<String, String>() {
init {
this.put<caret>("foo", "bar")
this.<caret>put("foo", "bar")
}
}
@@ -2,5 +2,5 @@
fun foo() {
val map = mutableMapOf(42 to "foo")
map.put<caret>(60, "bar")
map.<caret>put(60, "bar")
}
@@ -2,5 +2,5 @@
fun foo() {
var map = mutableMapOf(42 to "foo")
map.put<caret>(60, "bar")
map.<caret>put(60, "bar")
}
+1 -1
View File
@@ -12,7 +12,7 @@ internal class A {
fun <K, V> getMap2(k: K, v: V): Map<K, V> {
val map = HashMap<K, V>()
map.put(k, v)
map[k] = v
return map
}
}