Refactoring: "replace put with assignment" is now applicability-based

This commit is contained in:
Mikhail Glukhikh
2017-12-20 21:06:31 +03:00
parent 9e919829c8
commit 5a1a35bb35
2 changed files with 40 additions and 67 deletions
@@ -16,73 +16,61 @@
package org.jetbrains.kotlin.idea.inspections package org.jetbrains.kotlin.idea.inspections
import com.intellij.codeInspection.LocalQuickFix import com.intellij.codeInspection.*
import com.intellij.codeInspection.ProblemDescriptor import com.intellij.openapi.editor.Editor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.openapi.project.Project import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor import com.intellij.psi.PsiElement
import org.jetbrains.kotlin.builtins.DefaultBuiltIns import org.jetbrains.kotlin.builtins.DefaultBuiltIns
import org.jetbrains.kotlin.descriptors.ClassDescriptor import org.jetbrains.kotlin.descriptors.ClassDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.intentions.callExpression
import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.getParentOfType
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue import org.jetbrains.kotlin.resolve.calls.resolvedCallUtil.getExplicitReceiverValue
import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf import org.jetbrains.kotlin.resolve.descriptorUtil.isSubclassOf
class ReplacePutWithAssignmentInspection : AbstractKotlinInspection() { class ReplacePutWithAssignmentInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): KtVisitorVoid =
return object : KtVisitorVoid() { object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) { override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(expression) super.visitDotQualifiedExpression(expression)
if (isActiveFor(expression)) { visitTargetElement(expression, holder, isOnTheFly)
holder.registerProblem(
expression.callExpression!!.calleeExpression!!,
"map.put() can be converted to assignment",
ProblemHighlightType.WEAK_WARNING,
ReplacePutWithAssignmentQuickfix()
)
} }
} }
}
override fun isApplicable(element: KtDotQualifiedExpression): Boolean {
val callExpression = element.callExpression
if (callExpression?.valueArguments?.size != 2) return false
val calleeExpression = callExpression.calleeExpression as? KtNameReferenceExpression ?: return false
if (calleeExpression.getReferencedName() !in compatibleNames) return false
val context = element.analyze()
if (element.isUsedAsExpression(context)) return false
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)
} }
override fun applyTo(element: PsiElement, project: Project, editor: Editor?) {
val expression = element.getParentOfType<KtDotQualifiedExpression>(strict = false) ?: return
val valueArguments = expression.callExpression?.valueArguments ?: return
expression.replace(KtPsiFactory(expression).createExpressionByPattern("$0[$1] = $2",
expression.receiverExpression,
valueArguments[0]?.getArgumentExpression() ?: return,
valueArguments[1]?.getArgumentExpression() ?: return))
}
override fun inspectionTarget(element: KtDotQualifiedExpression) = element.callExpression?.calleeExpression ?: element
override fun inspectionText(element: KtDotQualifiedExpression): String = "map.put() can be converted to assignment"
override val defaultFixText = "Convert put to assignment"
companion object { companion object {
private val compatibleNames = setOf("put") private val compatibleNames = setOf("put")
fun isActiveFor(expression: KtDotQualifiedExpression): Boolean {
val callExpression = expression.callExpression
if (callExpression?.valueArguments?.size != 2) return false
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)
}
} }
} }
@@ -78,7 +78,7 @@ object J2KPostProcessingRegistrar {
_processings.add(UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing()) _processings.add(UnresolvedVariableReferenceFromInitializerToThisReferenceProcessing())
_processings.add(RemoveRedundantSamAdaptersProcessing()) _processings.add(RemoveRedundantSamAdaptersProcessing())
_processings.add(RemoveRedundantCastToNullableProcessing()) _processings.add(RemoveRedundantCastToNullableProcessing())
_processings.add(ReplacePutWithAssignmentProcessing()) registerInspectionBasedProcessing(ReplacePutWithAssignmentInspection())
_processings.add(UseExpressionBodyProcessing()) _processings.add(UseExpressionBodyProcessing())
_processings.add(UnnecessaryVariableProcessing()) _processings.add(UnnecessaryVariableProcessing())
@@ -300,21 +300,6 @@ 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 {
if (ReplacePutWithAssignmentInspection.isActiveFor(element)) {
ReplacePutWithAssignmentInspection.simplify(element)
}
}
}
}
private class UseExpressionBodyProcessing : J2kPostProcessing { private class UseExpressionBodyProcessing : J2kPostProcessing {
override val writeActionNeeded = true override val writeActionNeeded = true