From a90e3c2e45a7e6e0e4a02d9270a941ed4e546dbf Mon Sep 17 00:00:00 2001 From: Mikhail Glukhikh Date: Tue, 5 Dec 2017 17:29:58 +0300 Subject: [PATCH] Add "replace map.put with assignment" to J2K Related to KT-21502 --- .../ReplacePutWithAssignmentInspection.kt | 72 +++++++++---------- .../kotlin/idea/j2k/J2kPostProcessings.kt | 19 +++-- .../replacePutWithAssignment/nonMap.kt | 2 +- .../putAsExpression.kt | 2 +- .../putOnParameter.kt | 2 +- .../replacePutWithAssignment/putOnThis.kt | 2 +- .../replacePutWithAssignment/putOnVal.kt | 2 +- .../replacePutWithAssignment/putOnVar.kt | 2 +- .../typeParameters/needTypeArgs.kt | 2 +- 9 files changed, 58 insertions(+), 47 deletions(-) diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt index bf6a52a8005..11d1b985ff3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt @@ -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) + } } } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt index 0f28aedb396..d4764a893b6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt +++ b/idea/src/org/jetbrains/kotlin/idea/j2k/J2kPostProcessings.kt @@ -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 diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt index 0ea3eb9326a..bfc61d5ce28 100644 --- a/idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt @@ -6,5 +6,5 @@ class A { } fun foo() { - A().put(1, "foo") + A().put(1, "foo") } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putAsExpression.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putAsExpression.kt index 4c72160c011..18e5483b731 100644 --- a/idea/testData/inspectionsLocal/replacePutWithAssignment/putAsExpression.kt +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putAsExpression.kt @@ -3,4 +3,4 @@ val map = mutableMapOf(42 to "foo") -fun foo() = map.put(60, "bar") +fun foo() = map.put(60, "bar") diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt index e63456f84aa..73a8567d71e 100644 --- a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt @@ -1,5 +1,5 @@ // WITH_RUNTIME fun foo(map: MutableMap) { - map.put(42, "foo") + map.put(42, "foo") } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt index d10d0f28c82..9c4637272ef 100644 --- a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt @@ -2,6 +2,6 @@ class MyMap() : HashMap() { init { - this.put("foo", "bar") + this.put("foo", "bar") } } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt index de036175267..60fb2b9593b 100644 --- a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt @@ -2,5 +2,5 @@ fun foo() { val map = mutableMapOf(42 to "foo") - map.put(60, "bar") + map.put(60, "bar") } \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt index 62b3eb1bab4..1f28960096b 100644 --- a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt +++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt @@ -2,5 +2,5 @@ fun foo() { var map = mutableMapOf(42 to "foo") - map.put(60, "bar") + map.put(60, "bar") } \ No newline at end of file diff --git a/j2k/testData/fileOrElement/typeParameters/needTypeArgs.kt b/j2k/testData/fileOrElement/typeParameters/needTypeArgs.kt index d1e8dac8bf2..5c722d7f1dc 100644 --- a/j2k/testData/fileOrElement/typeParameters/needTypeArgs.kt +++ b/j2k/testData/fileOrElement/typeParameters/needTypeArgs.kt @@ -12,7 +12,7 @@ internal class A { fun getMap2(k: K, v: V): Map { val map = HashMap() - map.put(k, v) + map[k] = v return map } } \ No newline at end of file