diff --git a/idea/resources/inspectionDescriptions/ReplacePutWithAssignment.html b/idea/resources/inspectionDescriptions/ReplacePutWithAssignment.html
new file mode 100644
index 00000000000..7497d29f8eb
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/ReplacePutWithAssignment.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports map.put function calls replaceable with the indexing operator ([]).
+
+
\ No newline at end of file
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 2bd8d003980..0bc77fe9c7e 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -2681,6 +2681,14 @@
language="kotlin"
/>
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt
new file mode 100644
index 00000000000..bf6a52a8005
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/inspections/ReplacePutWithAssignmentInspection.kt
@@ -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))
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/.inspection b/idea/testData/inspectionsLocal/replacePutWithAssignment/.inspection
new file mode 100644
index 00000000000..5f8cc4fe6ed
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/.inspection
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.inspections.ReplacePutWithAssignmentInspection
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt
new file mode 100644
index 00000000000..0ea3eb9326a
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/nonMap.kt
@@ -0,0 +1,10 @@
+// PROBLEM: none
+
+class A {
+ fun put(x: Int, y: String) {
+ }
+}
+
+fun 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
new file mode 100644
index 00000000000..4c72160c011
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putAsExpression.kt
@@ -0,0 +1,6 @@
+// PROBLEM: none
+// WITH_RUNTIME
+
+val map = mutableMapOf(42 to "foo")
+
+fun foo() = map.put(60, "bar")
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt
new file mode 100644
index 00000000000..e63456f84aa
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+
+fun foo(map: MutableMap) {
+ map.put(42, "foo")
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt.after b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt.after
new file mode 100644
index 00000000000..ddbad2d0f33
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnParameter.kt.after
@@ -0,0 +1,5 @@
+// WITH_RUNTIME
+
+fun foo(map: MutableMap) {
+ map[42] = "foo"
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt
new file mode 100644
index 00000000000..d10d0f28c82
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+
+class MyMap() : HashMap() {
+ init {
+ this.put("foo", "bar")
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt.after b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt.after
new file mode 100644
index 00000000000..41bbdde21f5
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnThis.kt.after
@@ -0,0 +1,7 @@
+// WITH_RUNTIME
+
+class MyMap() : HashMap() {
+ init {
+ this["foo"] = "bar"
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt
new file mode 100644
index 00000000000..de036175267
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+
+fun foo() {
+ val map = mutableMapOf(42 to "foo")
+ map.put(60, "bar")
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt.after b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt.after
new file mode 100644
index 00000000000..9b3861057b6
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVal.kt.after
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+
+fun foo() {
+ val map = mutableMapOf(42 to "foo")
+ map[60] = "bar"
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt
new file mode 100644
index 00000000000..62b3eb1bab4
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+
+fun foo() {
+ var map = mutableMapOf(42 to "foo")
+ map.put(60, "bar")
+}
\ No newline at end of file
diff --git a/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt.after b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt.after
new file mode 100644
index 00000000000..db5ea610a89
--- /dev/null
+++ b/idea/testData/inspectionsLocal/replacePutWithAssignment/putOnVar.kt.after
@@ -0,0 +1,6 @@
+// WITH_RUNTIME
+
+fun foo() {
+ var map = mutableMapOf(42 to "foo")
+ map[60] = "bar"
+}
\ No newline at end of file
diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
index d5a0114d5e1..b0a09390208 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java
@@ -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)