Implement intention to add labeled return to last expression in a lambda
So #KT-20439 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
769e28519e
commit
59f6dc07cf
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
<spot>return@find</spot> true
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
+5
@@ -0,0 +1,5 @@
|
|||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
This intention adds labeled return to last expression in a lambda.
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
@@ -1562,6 +1562,11 @@
|
|||||||
<category>Kotlin</category>
|
<category>Kotlin</category>
|
||||||
</intentionAction>
|
</intentionAction>
|
||||||
|
|
||||||
|
<intentionAction>
|
||||||
|
<className>org.jetbrains.kotlin.idea.intentions.AddLabeledReturnInLambdaIntention</className>
|
||||||
|
<category>Kotlin</category>
|
||||||
|
</intentionAction>
|
||||||
|
|
||||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ObjectLiteralToLambdaInspection"
|
||||||
displayName="Object literal can be converted to lambda"
|
displayName="Object literal can be converted to lambda"
|
||||||
groupPath="Kotlin"
|
groupPath="Kotlin"
|
||||||
|
|||||||
@@ -0,0 +1,48 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license
|
||||||
|
* that can be found in the license/LICENSE.txt file.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.jetbrains.kotlin.idea.intentions
|
||||||
|
|
||||||
|
import com.intellij.codeInsight.intention.LowPriorityAction
|
||||||
|
import com.intellij.openapi.editor.Editor
|
||||||
|
import com.intellij.openapi.util.TextRange
|
||||||
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
|
import org.jetbrains.kotlin.resolve.bindingContextUtil.isUsedAsExpression
|
||||||
|
|
||||||
|
class AddLabeledReturnInLambdaIntention : SelfTargetingRangeIntention<KtLambdaExpression>(
|
||||||
|
KtLambdaExpression::class.java,
|
||||||
|
"Add labeled return to last expression in a lambda"
|
||||||
|
), LowPriorityAction {
|
||||||
|
override fun applicabilityRange(element: KtLambdaExpression): TextRange? {
|
||||||
|
if (!isApplicableTo(element)) return null
|
||||||
|
val labelName = createLabelName(element) ?: return null
|
||||||
|
text = "Add return@$labelName"
|
||||||
|
return element.bodyExpression?.statements?.last()?.textRange
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun applyTo(element: KtLambdaExpression, editor: Editor?) {
|
||||||
|
if (!isApplicableTo(element)) return
|
||||||
|
val labelName = createLabelName(element) ?: return
|
||||||
|
val lastStatement = element.bodyExpression?.statements?.last() ?: return
|
||||||
|
val newExpression = KtPsiFactory(element.project).createExpressionByPattern("return@$labelName $0", lastStatement)
|
||||||
|
lastStatement.replace(newExpression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun isApplicableTo(element: KtLambdaExpression): Boolean {
|
||||||
|
val block = element.bodyExpression ?: return false
|
||||||
|
val lastStatement = block.statements.last()
|
||||||
|
return lastStatement !is KtReturnExpression && lastStatement.isUsedAsExpression(lastStatement.analyze())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createLabelName(element: KtLambdaExpression): String? {
|
||||||
|
val block = element.bodyExpression ?: return null
|
||||||
|
val callExpression = element.getStrictParentOfType<KtCallExpression>() ?: return null
|
||||||
|
val valueArgument = callExpression.valueArguments.findArgumentWithGivenBlock(block) ?: return null
|
||||||
|
val lambdaLabelName = (valueArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
|
||||||
|
return lambdaLabelName ?: callExpression.getCallNameExpression()?.text
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-10
@@ -9,7 +9,6 @@ import com.intellij.codeInsight.intention.LowPriorityAction
|
|||||||
import com.intellij.openapi.editor.Editor
|
import com.intellij.openapi.editor.Editor
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
import org.jetbrains.kotlin.psi.psiUtil.getCallNameExpression
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getChildOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
|
|
||||||
class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpression>(
|
class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpression>(
|
||||||
@@ -21,15 +20,7 @@ class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpr
|
|||||||
val block = element.getStrictParentOfType<KtBlockExpression>() ?: return false
|
val block = element.getStrictParentOfType<KtBlockExpression>() ?: return false
|
||||||
if (block.statements.lastOrNull() != element) return false
|
if (block.statements.lastOrNull() != element) return false
|
||||||
val callExpression = block.getStrictParentOfType<KtCallExpression>() ?: return false
|
val callExpression = block.getStrictParentOfType<KtCallExpression>() ?: return false
|
||||||
val lambdaArgument = callExpression.lambdaArguments.firstOrNull {
|
val lambdaArgument = callExpression.lambdaArguments.findArgumentWithGivenBlock(block) ?: return false
|
||||||
val argumentExpression = it.getArgumentExpression()
|
|
||||||
val lambda = when (argumentExpression) {
|
|
||||||
is KtLambdaExpression -> argumentExpression
|
|
||||||
is KtLabeledExpression -> argumentExpression.baseExpression as? KtLambdaExpression
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
lambda?.bodyExpression === block
|
|
||||||
} ?: return false
|
|
||||||
val callName = (lambdaArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
|
val callName = (lambdaArgument.getArgumentExpression() as? KtLabeledExpression)?.getLabelName()
|
||||||
?: callExpression.getCallNameExpression()?.text ?: return false
|
?: callExpression.getCallNameExpression()?.text ?: return false
|
||||||
if (labelName != callName) return false
|
if (labelName != callName) return false
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ import org.jetbrains.kotlin.name.Name
|
|||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.CollectionLiteralResolver
|
import org.jetbrains.kotlin.resolve.CollectionLiteralResolver
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
|
||||||
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
import org.jetbrains.kotlin.resolve.descriptorUtil.fqNameUnsafe
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
@@ -295,3 +294,15 @@ fun KtCallExpression.isArrayOfMethod(): Boolean {
|
|||||||
return (descriptor.containingDeclaration as? PackageFragmentDescriptor)?.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME &&
|
return (descriptor.containingDeclaration as? PackageFragmentDescriptor)?.fqName == KotlinBuiltIns.BUILT_INS_PACKAGE_FQ_NAME &&
|
||||||
ARRAY_OF_METHODS.contains(descriptor.name)
|
ARRAY_OF_METHODS.contains(descriptor.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun <T : KtValueArgument> List<T>.findArgumentWithGivenBlock(
|
||||||
|
block: KtBlockExpression
|
||||||
|
): T? = firstOrNull {
|
||||||
|
val argumentExpression = it.getArgumentExpression()
|
||||||
|
val lambda = when (argumentExpression) {
|
||||||
|
is KtLambdaExpression -> argumentExpression
|
||||||
|
is KtLabeledExpression -> argumentExpression.baseExpression as? KtLambdaExpression
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
lambda?.bodyExpression === block
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1 @@
|
|||||||
|
org.jetbrains.kotlin.idea.intentions.AddLabeledReturnInLambdaIntention
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@find"
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find({
|
||||||
|
<caret>true
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@find"
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find({
|
||||||
|
return@find true
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@label"
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find label@{
|
||||||
|
<caret>true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@label"
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find label@{
|
||||||
|
return@label true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
for (i in 10..100) {
|
||||||
|
<caret>true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@bar"
|
||||||
|
|
||||||
|
private fun <T> List<T>.bar(a: (T) -> Boolean, b: (T) -> Boolean) {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).bar({
|
||||||
|
true
|
||||||
|
}) {
|
||||||
|
<caret>true
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@bar"
|
||||||
|
|
||||||
|
private fun <T> List<T>.bar(a: (T) -> Boolean, b: (T) -> Boolean) {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).bar({
|
||||||
|
true
|
||||||
|
}) {
|
||||||
|
return@bar true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@bar"
|
||||||
|
|
||||||
|
private fun <T> List<T>.bar(a: (T) -> Boolean, b: (T) -> Boolean) {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).bar({
|
||||||
|
<caret>true
|
||||||
|
}) {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@bar"
|
||||||
|
|
||||||
|
private fun <T> List<T>.bar(a: (T) -> Boolean, b: (T) -> Boolean) {}
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).bar({
|
||||||
|
return@bar true
|
||||||
|
}) {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@find"
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
<caret>true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@find"
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
return@find true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
<caret>1
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@find"
|
||||||
|
|
||||||
|
fun foo(): Int? {
|
||||||
|
return listOf(1,2,3).find {
|
||||||
|
<caret>true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// WITH_RUNTIME
|
||||||
|
// INTENTION_TEXT: "Add return@find"
|
||||||
|
|
||||||
|
fun foo(): Int? {
|
||||||
|
return listOf(1,2,3).find {
|
||||||
|
return@find true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).forEach {
|
||||||
|
<caret>true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo() {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
return@find <caret>true
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// IS_APPLICABLE: false
|
||||||
|
// WITH_RUNTIME
|
||||||
|
|
||||||
|
fun foo(): Boolean {
|
||||||
|
listOf(1,2,3).find {
|
||||||
|
return <caret>true
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@@ -364,6 +364,81 @@ public class IntentionTestGenerated extends AbstractIntentionTest {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("idea/testData/intentions/addLabeledReturnInLambda")
|
||||||
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
public static class AddLabeledReturnInLambda extends AbstractIntentionTest {
|
||||||
|
public void testAllFilesPresentInAddLabeledReturnInLambda() throws Exception {
|
||||||
|
KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/intentions/addLabeledReturnInLambda"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("insideParenthesis.kt")
|
||||||
|
public void testInsideParenthesis() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/insideParenthesis.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("labeledLambda.kt")
|
||||||
|
public void testLabeledLambda() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/labeledLambda.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("loop.kt")
|
||||||
|
public void testLoop() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/loop.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleHighOrderFun.kt")
|
||||||
|
public void testMultipleHighOrderFun() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/multipleHighOrderFun.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("multipleHighOrderFun2.kt")
|
||||||
|
public void testMultipleHighOrderFun2() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/multipleHighOrderFun2.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("normal.kt")
|
||||||
|
public void testNormal() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/normal.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("notLast.kt")
|
||||||
|
public void testNotLast() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/notLast.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("returnStatement.kt")
|
||||||
|
public void testReturnStatement() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/returnStatement.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("unit.kt")
|
||||||
|
public void testUnit() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/unit.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("withLabeledReturn.kt")
|
||||||
|
public void testWithLabeledReturn() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/withLabeledReturn.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
|
||||||
|
@TestMetadata("withReturn.kt")
|
||||||
|
public void testWithReturn() throws Exception {
|
||||||
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/intentions/addLabeledReturnInLambda/withReturn.kt");
|
||||||
|
doTest(fileName);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("idea/testData/intentions/addMissingDestructuring")
|
@TestMetadata("idea/testData/intentions/addMissingDestructuring")
|
||||||
@TestDataPath("$PROJECT_ROOT")
|
@TestDataPath("$PROJECT_ROOT")
|
||||||
@RunWith(JUnit3RunnerWithInners.class)
|
@RunWith(JUnit3RunnerWithInners.class)
|
||||||
|
|||||||
Reference in New Issue
Block a user