diff --git a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
index 5ded342805a..4db3d5459b6 100644
--- a/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
+++ b/compiler/frontend/src/org/jetbrains/kotlin/psi/KtPsiFactory.kt
@@ -216,6 +216,10 @@ class KtPsiFactory(private val project: Project) {
return (createFunction("fun foo() {$text}").bodyExpression as KtBlockExpression).statements.first() as KtDestructuringDeclaration
}
+ fun createDestructuringDeclarationInFor(text: String): KtDestructuringDeclaration {
+ return ((createFunction("fun foo() {for ($text in foo) {} }").bodyExpression as KtBlockExpression).statements.first() as KtForExpression).destructuringParameter!!
+ }
+
fun createDestructuringParameter(text: String): KtDestructuringDeclaration {
val dummyFun = createFunction("fun foo() { for ($text in foo) {} }")
return ((dummyFun.bodyExpression as KtBlockExpression).statements.first() as KtForExpression).destructuringParameter!!
diff --git a/idea/resources/inspectionDescriptions/SimplifyFor.html b/idea/resources/inspectionDescriptions/SimplifyFor.html
new file mode 100644
index 00000000000..1b1131ae986
--- /dev/null
+++ b/idea/resources/inspectionDescriptions/SimplifyFor.html
@@ -0,0 +1,5 @@
+
+
+This inspection reports any for expressions that can be simplified
+
+
diff --git a/idea/resources/intentionDescriptions/SimplifyForIntention/after.kt.template b/idea/resources/intentionDescriptions/SimplifyForIntention/after.kt.template
new file mode 100644
index 00000000000..11a3dcb18f1
--- /dev/null
+++ b/idea/resources/intentionDescriptions/SimplifyForIntention/after.kt.template
@@ -0,0 +1,3 @@
+val map = hashMapOf(1 to 2)
+for ((key, value) in map) {
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/SimplifyForIntention/before.kt.template b/idea/resources/intentionDescriptions/SimplifyForIntention/before.kt.template
new file mode 100644
index 00000000000..d2d421be7cf
--- /dev/null
+++ b/idea/resources/intentionDescriptions/SimplifyForIntention/before.kt.template
@@ -0,0 +1,5 @@
+val map = hashMapOf(1 to 2)
+for (entry in map.entries) {
+ val key = entry.key
+ val value = entry.value
+}
\ No newline at end of file
diff --git a/idea/resources/intentionDescriptions/SimplifyForIntention/description.html b/idea/resources/intentionDescriptions/SimplifyForIntention/description.html
new file mode 100644
index 00000000000..de61bdd34a1
--- /dev/null
+++ b/idea/resources/intentionDescriptions/SimplifyForIntention/description.html
@@ -0,0 +1,5 @@
+
+
+This intention simplifies for expression by replacing iteration over map entries and collections of data classes with destructing declaration
+
+
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 39975f056c8..d15fc3001ee 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -1105,6 +1105,11 @@
Kotlin
+
+ org.jetbrains.kotlin.idea.intentions.SimplifyForIntention
+ Kotlin
+
+
org.jetbrains.kotlin.idea.intentions.AnonymousFunctionToLambdaIntention
Kotlin
@@ -1315,6 +1320,13 @@
cleanupTool="true"
level="WARNING"/>
+
+
diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/SimplifyForIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/SimplifyForIntention.kt
new file mode 100644
index 00000000000..fefb9811f61
--- /dev/null
+++ b/idea/src/org/jetbrains/kotlin/idea/intentions/SimplifyForIntention.kt
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2010-2016 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.intentions
+
+import com.intellij.openapi.editor.Editor
+import com.intellij.openapi.util.TextRange
+import com.intellij.psi.PsiReference
+import com.intellij.psi.search.searches.ReferencesSearch
+import com.intellij.util.Query
+import org.jetbrains.kotlin.descriptors.CallableDescriptor
+import org.jetbrains.kotlin.descriptors.ClassDescriptor
+import org.jetbrains.kotlin.idea.caches.resolve.analyzeFullyAndGetResult
+import org.jetbrains.kotlin.idea.inspections.IntentionBasedInspection
+import org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.chooseApplicableComponentFunctions
+import org.jetbrains.kotlin.platform.JvmBuiltIns
+import org.jetbrains.kotlin.psi.*
+import org.jetbrains.kotlin.resolve.BindingContext
+import org.jetbrains.kotlin.resolve.DescriptorUtils
+import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
+
+class SimplifyForInspection : IntentionBasedInspection(SimplifyForIntention())
+
+class SimplifyForIntention : SelfTargetingRangeIntention(
+ KtForExpression::class.java,
+ "Simplify 'for' using destructing declaration",
+ "Simplify 'for'"
+) {
+ override fun applyTo(element: KtForExpression, editor: Editor?) {
+ applyTo(element)
+ }
+
+ fun applyTo(element: KtForExpression) {
+ val (propertiesToRemove, removeSelectorInLoopRange) = collectPropertiesToRemove(element) ?: return
+
+ val loopRange = element.loopRange ?: return
+ val loopParameter = element.loopParameter ?: return
+
+ loopParameter.replace(KtPsiFactory(element).createDestructuringDeclarationInFor("(${propertiesToRemove.joinToString { it.name!! }})"))
+ propertiesToRemove.forEach { p -> p.delete() }
+
+ if (removeSelectorInLoopRange && loopRange is KtDotQualifiedExpression) {
+ loopRange.replace(loopRange.receiverExpression)
+ }
+ }
+
+ override fun applicabilityRange(element: KtForExpression): TextRange? {
+ if (element.destructuringParameter != null) return null
+
+ if (collectPropertiesToRemove(element) != null) {
+ return element.loopParameter!!.textRange
+ }
+ return null
+ }
+
+ // Note: list should contains properties in order to create destructing declaration
+ private fun collectPropertiesToRemove(element: KtForExpression): Pair, Boolean>? {
+ val loopParameter = element.loopParameter ?: return null
+
+ val context = element.analyzeFullyAndGetResult().bindingContext
+
+ val loopParameterDescriptor = context.get(BindingContext.VALUE_PARAMETER, loopParameter) ?: return null
+ val classDescriptor = loopParameterDescriptor.type.constructor.declarationDescriptor as? ClassDescriptor ?: return null
+
+ var otherUsages = false
+ var removeSelectorInLoopRange = false
+ val propertiesToRemove: Array
+
+ if (DescriptorUtils.isSubclass(classDescriptor, JvmBuiltIns.Instance.mapEntry)) {
+ val loopRangeDescriptorName = element.loopRange.getResolvedCall(context)?.resultingDescriptor?.name
+ if (loopRangeDescriptorName != null &&
+ (loopRangeDescriptorName.asString().equals("entries") || loopRangeDescriptorName.asString().equals("entrySet"))) {
+ removeSelectorInLoopRange = true
+ }
+
+ propertiesToRemove = arrayOfNulls(2)
+
+ ReferencesSearch.search(loopParameter).iterateOverMapEntryPropertiesUsages(
+ context,
+ { index, property -> propertiesToRemove[index] = property },
+ { otherUsages = true }
+ )
+
+ if (!otherUsages && propertiesToRemove.all { it != null }) {
+ return propertiesToRemove.mapNotNull { it } to removeSelectorInLoopRange
+ }
+ }
+ else if (classDescriptor.isData) {
+ val valueParameters = classDescriptor.unsubstitutedPrimaryConstructor?.valueParameters ?: return null
+ propertiesToRemove = arrayOfNulls(valueParameters.size)
+
+ ReferencesSearch.search(loopParameter).iterateOverDataClassPropertiesUsagesWithIndex(
+ context,
+ classDescriptor,
+ { index, property -> propertiesToRemove[index] = property },
+ { otherUsages = true }
+ )
+
+ if (otherUsages) return null
+
+ val notNullProperties = propertiesToRemove.filterNotNull()
+ val droppedLastUnused = propertiesToRemove.dropLastWhile { it == null }
+ if (droppedLastUnused.size == notNullProperties.size) {
+ return notNullProperties to removeSelectorInLoopRange
+ }
+ }
+
+ return null
+ }
+
+ private fun Query.iterateOverMapEntryPropertiesUsages(
+ context: BindingContext,
+ process: (Int, KtProperty) -> Unit,
+ cancel: () -> Unit
+ ) {
+ forEach {
+ val applicableUsage = getDataIfUsageIsApplicable(it, context)
+ if (applicableUsage != null) {
+ val (property, descriptor) = applicableUsage
+ if (descriptor.name.asString().equals("key") || descriptor.name.asString().equals("getKey")) {
+ process(0, property)
+ return@forEach true
+ }
+ else if (descriptor.name.asString().equals("value") || descriptor.name.asString().equals("getValue")) {
+ process(1, property)
+ return@forEach true
+ }
+ }
+
+ cancel()
+ return@forEach false
+ }
+ }
+
+ private fun Query.iterateOverDataClassPropertiesUsagesWithIndex(
+ context: BindingContext,
+ dataClass: ClassDescriptor,
+ process: (Int, KtProperty) -> Unit,
+ cancel: () -> Unit
+ ) {
+ val valueParameters = dataClass.unsubstitutedPrimaryConstructor?.valueParameters ?: return
+
+ forEach {
+ val applicableUsage = getDataIfUsageIsApplicable(it, context)
+ if (applicableUsage != null) {
+ val (property, descriptor) = applicableUsage
+ for (valueParameter in valueParameters) {
+ if (context.get(BindingContext.VALUE_PARAMETER_AS_PROPERTY, valueParameter) == descriptor) {
+ process(valueParameter.index, property)
+ return@forEach true
+ }
+ }
+ }
+
+ cancel()
+ return@forEach false
+ }
+ }
+
+ private fun getDataIfUsageIsApplicable(usage: PsiReference, context: BindingContext): UsageData? {
+ val parentCall = usage.element.parent as? KtExpression ?: return null
+ val property = parentCall.parent as? KtProperty ?: return null
+ val resolvedCall = parentCall.getResolvedCall(context) ?: return null
+
+ val descriptor = resolvedCall.resultingDescriptor
+ return UsageData(property, descriptor)
+ }
+
+ private data class UsageData(val property: KtProperty, val descriptor: CallableDescriptor)
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/.intention b/idea/testData/intentions/iterationOverMap/.intention
new file mode 100644
index 00000000000..6cd5c448bf4
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/.intention
@@ -0,0 +1 @@
+org.jetbrains.kotlin.idea.intentions.SimplifyForIntention
diff --git a/idea/testData/intentions/iterationOverMap/AlreadyDestructing.kt b/idea/testData/intentions/iterationOverMap/AlreadyDestructing.kt
new file mode 100644
index 00000000000..1554c6ba5e9
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/AlreadyDestructing.kt
@@ -0,0 +1,10 @@
+// IS_APPLICABLE: false
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for ((key, value) in map) {
+ println(key)
+ println(value)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/CaretOffset.kt b/idea/testData/intentions/iterationOverMap/CaretOffset.kt
new file mode 100644
index 00000000000..0d2b7a29c9a
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/CaretOffset.kt
@@ -0,0 +1,13 @@
+// IS_APPLICABLE: false
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for (entry in map) {
+ val key = entry.key
+ val value = entry.value
+
+ println(key)
+ println(value)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/DataClass.kt b/idea/testData/intentions/iterationOverMap/DataClass.kt
new file mode 100644
index 00000000000..92af8ca6e3b
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/DataClass.kt
@@ -0,0 +1,12 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
+ for (klass in list) {
+ val a = klass.a
+ val b = klass.b
+ val c = klass.c
+ }
+}
+
+data class MyClass(val a: Int, val b: Int, val c: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/DataClass.kt.after b/idea/testData/intentions/iterationOverMap/DataClass.kt.after
new file mode 100644
index 00000000000..b038a08ba52
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/DataClass.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
+ for ((a, b, c) in list) {
+ }
+}
+
+data class MyClass(val a: Int, val b: Int, val c: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt b/idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt
new file mode 100644
index 00000000000..7528d203fac
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt
@@ -0,0 +1,11 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
+ for (klass in list) {
+ val a = klass.a
+ val b = klass.b
+ }
+}
+
+data class MyClass(val a: Int, val b: Int, val c: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt.after b/idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt.after
new file mode 100644
index 00000000000..3512fa8b3f7
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
+ for ((a, b) in list) {
+ }
+}
+
+data class MyClass(val a: Int, val b: Int, val c: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/DataClassNotAllPropertiesUsed.kt b/idea/testData/intentions/iterationOverMap/DataClassNotAllPropertiesUsed.kt
new file mode 100644
index 00000000000..667af459166
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/DataClassNotAllPropertiesUsed.kt
@@ -0,0 +1,12 @@
+// IS_APPLICABLE: false
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val list = listOf(MyClass(1, 2, 3, 4))
+ for (klass in list) {
+ val a = klass.a
+ val c = klass.c
+ }
+}
+
+data class MyClass(val a: Int, val b: Int, val c: Int, val d: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt b/idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt
new file mode 100644
index 00000000000..c1e66b6a010
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt
@@ -0,0 +1,12 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
+ for (klass in list) {
+ val a1 = klass.a
+ val c1 = klass.c
+ val b1 = klass.b
+ }
+}
+
+data class MyClass(val a: Int, val b: Int, val c: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt.after b/idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt.after
new file mode 100644
index 00000000000..29a966196a3
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt.after
@@ -0,0 +1,9 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val list = listOf(MyClass(1, 2, 3), MyClass(2, 3, 4))
+ for ((a1, b1, c1) in list) {
+ }
+}
+
+data class MyClass(val a: Int, val b: Int, val c: Int)
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt b/idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt
new file mode 100644
index 00000000000..9baf11ff49b
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt
@@ -0,0 +1,13 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ val entries = map.entries
+ for (entry in entries) {
+ val key = entry.key
+ val value = entry.value
+
+ println(key)
+ println(value)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt.after b/idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt.after
new file mode 100644
index 00000000000..d9a09609036
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt.after
@@ -0,0 +1,11 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ val entries = map.entries
+ for ((key, value) in entries) {
+
+ println(key)
+ println(value)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/Getters.kt b/idea/testData/intentions/iterationOverMap/Getters.kt
new file mode 100644
index 00000000000..00ce9f65ca8
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/Getters.kt
@@ -0,0 +1,10 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for (entry in map.entrySet()) {
+ val key = entry.getKey()
+ val value = entry.getValue()
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/Getters.kt.after b/idea/testData/intentions/iterationOverMap/Getters.kt.after
new file mode 100644
index 00000000000..8991fe092f3
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/Getters.kt.after
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for ((key, value) in map) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/OnlyKeyUsed.kt b/idea/testData/intentions/iterationOverMap/OnlyKeyUsed.kt
new file mode 100644
index 00000000000..01235dde169
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/OnlyKeyUsed.kt
@@ -0,0 +1,12 @@
+// IS_APPLICABLE: false
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for (entry in map) {
+ val key = entry.key
+ val key2 = entry.key
+
+ println(key)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/OtherUsages.kt b/idea/testData/intentions/iterationOverMap/OtherUsages.kt
new file mode 100644
index 00000000000..f9518785d23
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/OtherUsages.kt
@@ -0,0 +1,13 @@
+// IS_APPLICABLE: false
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for (entry in map) {
+ val myKey = entry.key
+ val myValue = entry.value
+
+ println(entry)
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/OtherUsages2.kt b/idea/testData/intentions/iterationOverMap/OtherUsages2.kt
new file mode 100644
index 00000000000..6944f9b7709
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/OtherUsages2.kt
@@ -0,0 +1,12 @@
+// IS_APPLICABLE: false
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for (entry in map) {
+ val myKey = entry.key
+
+ println(entry)
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/PropertiesNames.kt b/idea/testData/intentions/iterationOverMap/PropertiesNames.kt
new file mode 100644
index 00000000000..a129c81ad2d
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/PropertiesNames.kt
@@ -0,0 +1,10 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for (entry in map) {
+ val myKey = entry.key
+ val myValue = entry.value
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/PropertiesNames.kt.after b/idea/testData/intentions/iterationOverMap/PropertiesNames.kt.after
new file mode 100644
index 00000000000..53deb9b2df2
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/PropertiesNames.kt.after
@@ -0,0 +1,8 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for ((myKey, myValue) in map) {
+
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/Simple.kt b/idea/testData/intentions/iterationOverMap/Simple.kt
new file mode 100644
index 00000000000..e96e8ecf529
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/Simple.kt
@@ -0,0 +1,12 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for (entry in map) {
+ val key = entry.key
+ val value = entry.value
+
+ println(key)
+ println(value)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/Simple.kt.after b/idea/testData/intentions/iterationOverMap/Simple.kt.after
new file mode 100644
index 00000000000..0060bb4adbe
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/Simple.kt.after
@@ -0,0 +1,10 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ val map = hashMapOf(1 to 1)
+ for ((key, value) in map) {
+
+ println(key)
+ println(value)
+ }
+}
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt b/idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt
new file mode 100644
index 00000000000..bd03ff7bf9a
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt
@@ -0,0 +1,12 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ for (entry in 1.createListOfDataClasses()) {
+ val int1 = entry.a
+ val int2 = entry.b
+ }
+}
+
+fun Int.createListOfDataClasses() = listOf(MyClass(this, this))
+
+data class MyClass(val a: Int, val b: Int)
diff --git a/idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt.after b/idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt.after
new file mode 100644
index 00000000000..5de968b91f8
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt.after
@@ -0,0 +1,10 @@
+// WITH_RUNTIME
+
+fun main(args: Array) {
+ for ((int1, int2) in 1.createListOfDataClasses()) {
+ }
+}
+
+fun Int.createListOfDataClasses() = listOf(MyClass(this, this))
+
+data class MyClass(val a: Int, val b: Int)
diff --git a/idea/testData/intentions/iterationOverMap/inspectionData/expected.xml b/idea/testData/intentions/iterationOverMap/inspectionData/expected.xml
new file mode 100644
index 00000000000..82e465f9a19
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/inspectionData/expected.xml
@@ -0,0 +1,74 @@
+
+
+ SomeQualifiedExpressionInRange.kt
+ 4
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
+ Simple.kt
+ 5
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
+ PropertiesNames.kt
+ 5
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
+ Getters.kt
+ 5
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
+ DataClassParametersOrder.kt
+ 5
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
+ DataClass.kt
+ 5
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
+ EntriesCallIsMissing.kt
+ 6
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
+ DataClassFirstNPropertiesUsed.kt
+ 5
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
+ CaretOffset.kt
+ 6
+ light_idea_test_case
+
+ Simplify 'for' using destructing declaration
+ Simplify 'for' using destructing declaration
+
+
\ No newline at end of file
diff --git a/idea/testData/intentions/iterationOverMap/inspectionData/inspections.test b/idea/testData/intentions/iterationOverMap/inspectionData/inspections.test
new file mode 100644
index 00000000000..5ce71a19f3f
--- /dev/null
+++ b/idea/testData/intentions/iterationOverMap/inspectionData/inspections.test
@@ -0,0 +1 @@
+// INSPECTION_CLASS: org.jetbrains.kotlin.idea.intentions.SimplifyForInspection
diff --git a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
index 5a1ce2b7d4c..ca5fdaca7b1 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/codeInsight/InspectionTestGenerated.java
@@ -67,6 +67,12 @@ public class InspectionTestGenerated extends AbstractInspectionTest {
doTest(fileName);
}
+ @TestMetadata("iterationOverMap/inspectionData/inspections.test")
+ public void testIterationOverMap_inspectionData_Inspections_test() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/inspectionData/inspections.test");
+ doTest(fileName);
+ }
+
@TestMetadata("objectLiteralToLambda/inspectionData/inspections.test")
public void testObjectLiteralToLambda_inspectionData_Inspections_test() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/objectLiteralToLambda/inspectionData/inspections.test");
diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
index 7b1f2f300d5..f5d46c05cb6 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java
@@ -6199,6 +6199,100 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
}
}
+ @TestMetadata("idea/testData/intentions/iterationOverMap")
+ @TestDataPath("$PROJECT_ROOT")
+ @RunWith(JUnit3RunnerWithInners.class)
+ public static class IterationOverMap extends AbstractIntentionTest {
+ public void testAllFilesPresentInIterationOverMap() throws Exception {
+ KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/iterationOverMap"), Pattern.compile("^([\\w\\-_]+)\\.kt$"), true);
+ }
+
+ @TestMetadata("AlreadyDestructing.kt")
+ public void testAlreadyDestructing() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/AlreadyDestructing.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("CaretOffset.kt")
+ public void testCaretOffset() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/CaretOffset.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("DataClass.kt")
+ public void testDataClass() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/DataClass.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("DataClassFirstNPropertiesUsed.kt")
+ public void testDataClassFirstNPropertiesUsed() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("DataClassNotAllPropertiesUsed.kt")
+ public void testDataClassNotAllPropertiesUsed() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/DataClassNotAllPropertiesUsed.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("DataClassParametersOrder.kt")
+ public void testDataClassParametersOrder() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("EntriesCallIsMissing.kt")
+ public void testEntriesCallIsMissing() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("Getters.kt")
+ public void testGetters() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/Getters.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("OnlyKeyUsed.kt")
+ public void testOnlyKeyUsed() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/OnlyKeyUsed.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("OtherUsages.kt")
+ public void testOtherUsages() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/OtherUsages.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("OtherUsages2.kt")
+ public void testOtherUsages2() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/OtherUsages2.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("PropertiesNames.kt")
+ public void testPropertiesNames() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/PropertiesNames.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("Simple.kt")
+ public void testSimple() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/Simple.kt");
+ doTest(fileName);
+ }
+
+ @TestMetadata("SomeQualifiedExpressionInRange.kt")
+ public void testSomeQualifiedExpressionInRange() throws Exception {
+ String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt");
+ doTest(fileName);
+ }
+
+ }
+
@TestMetadata("idea/testData/intentions/moveAssignmentToInitializer")
@TestDataPath("$PROJECT_ROOT")
@RunWith(JUnit3RunnerWithInners.class)