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
import com.intellij.codeInspection.LocalQuickFix
import com.intellij.codeInspection.ProblemDescriptor
import com.intellij.codeInspection.ProblemHighlightType
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.*
import com.intellij.openapi.editor.Editor
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.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.getParentOfType
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() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
return object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(expression)
if (isActiveFor(expression)) {
holder.registerProblem(
expression.callExpression!!.calleeExpression!!,
"map.put() can be converted to assignment",
ProblemHighlightType.WEAK_WARNING,
ReplacePutWithAssignmentQuickfix()
)
class ReplacePutWithAssignmentInspection : AbstractApplicabilityBasedInspection<KtDotQualifiedExpression>() {
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): KtVisitorVoid =
object : KtVisitorVoid() {
override fun visitDotQualifiedExpression(expression: KtDotQualifiedExpression) {
super.visitDotQualifiedExpression(expression)
visitTargetElement(expression, holder, isOnTheFly)
}
}
}
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 {
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(RemoveRedundantSamAdaptersProcessing())
_processings.add(RemoveRedundantCastToNullableProcessing())
_processings.add(ReplacePutWithAssignmentProcessing())
registerInspectionBasedProcessing(ReplacePutWithAssignmentInspection())
_processings.add(UseExpressionBodyProcessing())
_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 {
override val writeActionNeeded = true