From ea3ba6f534b212f57b5779bc35d33efb20dd2dca Mon Sep 17 00:00:00 2001 From: Natalia Ukhorskaya Date: Wed, 13 Jan 2016 14:47:02 +0300 Subject: [PATCH] Intention to simplify for using destructing declaration --- .../org/jetbrains/kotlin/psi/KtPsiFactory.kt | 4 + .../inspectionDescriptions/SimplifyFor.html | 5 + .../SimplifyForIntention/after.kt.template | 3 + .../SimplifyForIntention/before.kt.template | 5 + .../SimplifyForIntention/description.html | 5 + idea/src/META-INF/plugin.xml | 12 ++ .../idea/intentions/SimplifyForIntention.kt | 183 ++++++++++++++++++ .../intentions/iterationOverMap/.intention | 1 + .../iterationOverMap/AlreadyDestructing.kt | 10 + .../iterationOverMap/CaretOffset.kt | 13 ++ .../intentions/iterationOverMap/DataClass.kt | 12 ++ .../iterationOverMap/DataClass.kt.after | 9 + .../DataClassFirstNPropertiesUsed.kt | 11 ++ .../DataClassFirstNPropertiesUsed.kt.after | 9 + .../DataClassNotAllPropertiesUsed.kt | 12 ++ .../DataClassParametersOrder.kt | 12 ++ .../DataClassParametersOrder.kt.after | 9 + .../iterationOverMap/EntriesCallIsMissing.kt | 13 ++ .../EntriesCallIsMissing.kt.after | 11 ++ .../intentions/iterationOverMap/Getters.kt | 10 + .../iterationOverMap/Getters.kt.after | 8 + .../iterationOverMap/OnlyKeyUsed.kt | 12 ++ .../iterationOverMap/OtherUsages.kt | 13 ++ .../iterationOverMap/OtherUsages2.kt | 12 ++ .../iterationOverMap/PropertiesNames.kt | 10 + .../iterationOverMap/PropertiesNames.kt.after | 8 + .../intentions/iterationOverMap/Simple.kt | 12 ++ .../iterationOverMap/Simple.kt.after | 10 + .../SomeQualifiedExpressionInRange.kt | 12 ++ .../SomeQualifiedExpressionInRange.kt.after | 10 + .../inspectionData/expected.xml | 74 +++++++ .../inspectionData/inspections.test | 1 + .../codeInsight/InspectionTestGenerated.java | 6 + .../intentions/IntentionTestGenerated.java | 94 +++++++++ 34 files changed, 631 insertions(+) create mode 100644 idea/resources/inspectionDescriptions/SimplifyFor.html create mode 100644 idea/resources/intentionDescriptions/SimplifyForIntention/after.kt.template create mode 100644 idea/resources/intentionDescriptions/SimplifyForIntention/before.kt.template create mode 100644 idea/resources/intentionDescriptions/SimplifyForIntention/description.html create mode 100644 idea/src/org/jetbrains/kotlin/idea/intentions/SimplifyForIntention.kt create mode 100644 idea/testData/intentions/iterationOverMap/.intention create mode 100644 idea/testData/intentions/iterationOverMap/AlreadyDestructing.kt create mode 100644 idea/testData/intentions/iterationOverMap/CaretOffset.kt create mode 100644 idea/testData/intentions/iterationOverMap/DataClass.kt create mode 100644 idea/testData/intentions/iterationOverMap/DataClass.kt.after create mode 100644 idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt create mode 100644 idea/testData/intentions/iterationOverMap/DataClassFirstNPropertiesUsed.kt.after create mode 100644 idea/testData/intentions/iterationOverMap/DataClassNotAllPropertiesUsed.kt create mode 100644 idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt create mode 100644 idea/testData/intentions/iterationOverMap/DataClassParametersOrder.kt.after create mode 100644 idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt create mode 100644 idea/testData/intentions/iterationOverMap/EntriesCallIsMissing.kt.after create mode 100644 idea/testData/intentions/iterationOverMap/Getters.kt create mode 100644 idea/testData/intentions/iterationOverMap/Getters.kt.after create mode 100644 idea/testData/intentions/iterationOverMap/OnlyKeyUsed.kt create mode 100644 idea/testData/intentions/iterationOverMap/OtherUsages.kt create mode 100644 idea/testData/intentions/iterationOverMap/OtherUsages2.kt create mode 100644 idea/testData/intentions/iterationOverMap/PropertiesNames.kt create mode 100644 idea/testData/intentions/iterationOverMap/PropertiesNames.kt.after create mode 100644 idea/testData/intentions/iterationOverMap/Simple.kt create mode 100644 idea/testData/intentions/iterationOverMap/Simple.kt.after create mode 100644 idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt create mode 100644 idea/testData/intentions/iterationOverMap/SomeQualifiedExpressionInRange.kt.after create mode 100644 idea/testData/intentions/iterationOverMap/inspectionData/expected.xml create mode 100644 idea/testData/intentions/iterationOverMap/inspectionData/inspections.test 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)