From 4c4427c280a28eebe9d6a9acc43615537efec6d9 Mon Sep 17 00:00:00 2001 From: Kirill Rakhman Date: Wed, 11 Oct 2017 23:15:30 +0300 Subject: [PATCH] Replace return@forEach with continue in ConvertForEachToForLoopIntention So #KT-17332 Fixed --- .../ConvertForEachToForLoopIntention.kt | 29 ++++++++++++++----- .../withNestedReturn.kt | 7 +++++ .../withNestedReturn.kt.after | 7 +++++ .../convertForEachToForLoop/withReturn.kt | 7 +++++ .../withReturn.kt.after | 7 +++++ .../intentions/IntentionTestGenerated.java | 12 ++++++++ 6 files changed, 62 insertions(+), 7 deletions(-) create mode 100644 idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt create mode 100644 idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt.after create mode 100644 idea/testData/intentions/convertForEachToForLoop/withReturn.kt create mode 100644 idea/testData/intentions/convertForEachToForLoop/withReturn.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertForEachToForLoopIntention.kt b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertForEachToForLoopIntention.kt index 2128cbe9f89..0494664314f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertForEachToForLoopIntention.kt +++ b/idea/src/org/jetbrains/kotlin/idea/intentions/ConvertForEachToForLoopIntention.kt @@ -20,7 +20,10 @@ import com.intellij.openapi.editor.Editor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.util.CommentSaver import org.jetbrains.kotlin.psi.* +import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils +import org.jetbrains.kotlin.resolve.bindingContextUtil.getTargetFunction import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver @@ -40,11 +43,11 @@ class ConvertForEachToForLoopIntention : SelfTargetingOffsetIndependentIntention } override fun applyTo(element: KtSimpleNameExpression, editor: Editor?) { - val (expressionToReplace, receiver, functionLiteral) = extractData(element)!! + val (expressionToReplace, receiver, functionLiteral, context) = extractData(element)!! val commentSaver = CommentSaver(expressionToReplace) - val loop = generateLoop(functionLiteral, receiver) + val loop = generateLoop(functionLiteral, receiver, context) val result = expressionToReplace.replace(loop) commentSaver.restore(result) @@ -53,7 +56,8 @@ class ConvertForEachToForLoopIntention : SelfTargetingOffsetIndependentIntention private data class Data( val expressionToReplace: KtExpression, val receiver: KtExpression, - val functionLiteral: KtLambdaExpression + val functionLiteral: KtLambdaExpression, + val context: BindingContext ) private fun extractData(nameExpr: KtSimpleNameExpression): Data? { @@ -64,20 +68,31 @@ class ConvertForEachToForLoopIntention : SelfTargetingOffsetIndependentIntention else -> null } ?: return null) as KtExpression //TODO: submit bug - val resolvedCall = expression.getResolvedCall(expression.analyze()) ?: return null + val context = expression.analyze() + val resolvedCall = expression.getResolvedCall(context) ?: return null if (DescriptorUtils.getFqName(resolvedCall.resultingDescriptor).toString() !in FOR_EACH_FQ_NAMES) return null val receiver = resolvedCall.call.explicitReceiver as? ExpressionReceiver ?: return null val argument = resolvedCall.call.valueArguments.singleOrNull() ?: return null val functionLiteral = argument.getArgumentExpression() as? KtLambdaExpression ?: return null - return Data(expression, receiver.expression, functionLiteral) + return Data(expression, receiver.expression, functionLiteral, context) } - private fun generateLoop(functionLiteral: KtLambdaExpression, receiver: KtExpression): KtExpression { + private fun generateLoop(functionLiteral: KtLambdaExpression, receiver: KtExpression, context: BindingContext): KtExpression { val factory = KtPsiFactory(functionLiteral) - val loopRange = KtPsiUtil.safeDeparenthesize(receiver) + val body = functionLiteral.bodyExpression!! + val function = functionLiteral.functionLiteral + + body.forEachDescendantOfType { + if (it.getTargetFunction(context) == function) { + it.replace(factory.createExpression("continue")) + } + } + + val loopRange = KtPsiUtil.safeDeparenthesize(receiver) val parameter = functionLiteral.valueParameters.singleOrNull() + return factory.createExpressionByPattern("for($0 in $1){ $2 }", parameter ?: "it", loopRange, body) } } diff --git a/idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt b/idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt new file mode 100644 index 00000000000..12df28e6020 --- /dev/null +++ b/idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun foo() { + listOf(1).forEach { + listOf(1).forEach { return@forEach } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt.after b/idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt.after new file mode 100644 index 00000000000..c98f29442b0 --- /dev/null +++ b/idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun foo() { + for (it in listOf(1)) { + listOf(1).forEach { return@forEach } + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertForEachToForLoop/withReturn.kt b/idea/testData/intentions/convertForEachToForLoop/withReturn.kt new file mode 100644 index 00000000000..3e70b27ea82 --- /dev/null +++ b/idea/testData/intentions/convertForEachToForLoop/withReturn.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun foo() { + listOf(1).forEach { + return@forEach + } +} \ No newline at end of file diff --git a/idea/testData/intentions/convertForEachToForLoop/withReturn.kt.after b/idea/testData/intentions/convertForEachToForLoop/withReturn.kt.after new file mode 100644 index 00000000000..f51f644eb05 --- /dev/null +++ b/idea/testData/intentions/convertForEachToForLoop/withReturn.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun foo() { + for (it in listOf(1)) { + continue + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java index 4505f096f4c..bade2b9b37c 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/intentions/IntentionTestGenerated.java @@ -4535,6 +4535,18 @@ public class IntentionTestGenerated extends AbstractIntentionTest { doTest(fileName); } + @TestMetadata("withNestedReturn.kt") + public void testWithNestedReturn() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertForEachToForLoop/withNestedReturn.kt"); + doTest(fileName); + } + + @TestMetadata("withReturn.kt") + public void testWithReturn() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertForEachToForLoop/withReturn.kt"); + doTest(fileName); + } + @TestMetadata("zeroArguments.kt") public void testZeroArguments() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/convertForEachToForLoop/zeroArguments.kt");