Introduce inspection "replace map.put with assignment" #KT-21502 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
ab28bdf32f
commit
8c305a137f
@@ -0,0 +1,5 @@
|
||||
<html>
|
||||
<body>
|
||||
This inspection reports <code>map.put</code> function calls replaceable with the indexing operator (<code>[]</code>).
|
||||
</body>
|
||||
</html>
|
||||
@@ -2681,6 +2681,14 @@
|
||||
language="kotlin"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.ReplacePutWithAssignmentInspection"
|
||||
displayName="map.put() can be converted to assignment"
|
||||
groupPath="Kotlin"
|
||||
groupName="Style issues"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
language="kotlin"
|
||||
/>
|
||||
<referenceImporter implementation="org.jetbrains.kotlin.idea.quickfix.KotlinReferenceImporter"/>
|
||||
|
||||
<fileType.fileViewProviderFactory filetype="KJSM" implementationClass="com.intellij.psi.ClassFileViewProviderFactory"/>
|
||||
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright 2010-2017 JetBrains s.r.o.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
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.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),
|
||||
"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"
|
||||
|
||||
override fun getFamilyName() = name
|
||||
|
||||
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))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
org.jetbrains.kotlin.idea.inspections.ReplacePutWithAssignmentInspection
|
||||
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: none
|
||||
|
||||
class A {
|
||||
fun put(x: Int, y: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun foo() {
|
||||
A().put<caret>(1, "foo")
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
|
||||
val map = mutableMapOf(42 to "foo")
|
||||
|
||||
fun foo() = map.put<caret>(60, "bar")
|
||||
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(map: MutableMap<Int, String>) {
|
||||
map.put<caret>(42, "foo")
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo(map: MutableMap<Int, String>) {
|
||||
map[42] = "foo"
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
class MyMap() : HashMap<String, String>() {
|
||||
init {
|
||||
this.put<caret>("foo", "bar")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
class MyMap() : HashMap<String, String>() {
|
||||
init {
|
||||
this["foo"] = "bar"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
val map = mutableMapOf(42 to "foo")
|
||||
map.put<caret>(60, "bar")
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
val map = mutableMapOf(42 to "foo")
|
||||
map[60] = "bar"
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var map = mutableMapOf(42 to "foo")
|
||||
map.put<caret>(60, "bar")
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun foo() {
|
||||
var map = mutableMapOf(42 to "foo")
|
||||
map[60] = "bar"
|
||||
}
|
||||
@@ -2193,6 +2193,51 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/replacePutWithAssignment")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
public static class ReplacePutWithAssignment extends AbstractLocalInspectionTest {
|
||||
public void testAllFilesPresentInReplacePutWithAssignment() throws Exception {
|
||||
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/replacePutWithAssignment"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), TargetBackend.ANY, true);
|
||||
}
|
||||
|
||||
@TestMetadata("nonMap.kt")
|
||||
public void testNonMap() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("putAsExpression.kt")
|
||||
public void testPutAsExpression() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replacePutWithAssignment/putAsExpression.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("putOnParameter.kt")
|
||||
public void testPutOnParameter() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("putOnThis.kt")
|
||||
public void testPutOnThis() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("putOnVal.kt")
|
||||
public void testPutOnVal() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
|
||||
@TestMetadata("putOnVar.kt")
|
||||
public void testPutOnVar() throws Exception {
|
||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt");
|
||||
doTest(fileName);
|
||||
}
|
||||
}
|
||||
|
||||
@TestMetadata("idea/testData/inspectionsLocal/replaceRangeToWithUntil")
|
||||
@TestDataPath("$PROJECT_ROOT")
|
||||
@RunWith(JUnit3RunnerWithInners.class)
|
||||
|
||||
Reference in New Issue
Block a user