KTIJ-22971 [kotlin] Transform RemoveLabeledReturnInLambdaIntention into LabeledReturnAsLastExpressionInLambdaInspection

- convert intention to inspection, so it can be executed in the batch mode
- move the inspection to the shared module for both K1 and K2
- move and adjust the testdata where needed

^KTIJ-22971 Fixed

GitOrigin-RevId: e01f190aa70a1f440bb7d8b523b785d249b7e7de
This commit is contained in:
Roman Golyshev
2024-03-12 18:53:05 +01:00
committed by intellij-monorepo-bot
parent e23937ffab
commit fbf7da2525
28 changed files with 204 additions and 114 deletions

View File

@@ -2086,6 +2086,7 @@ inspection.kotlin.throwable.not.thrown.display.name=Throwable not thrown
inspection.redundant.require.not.null.call.display.name=Redundant 'requireNotNull' or 'checkNotNull' call
inspection.replace.range.start.end.inclusive.with.first.last.display.name=Boxed properties should be replaced with unboxed
inspection.redundant.enum.constructor.invocation.display.name=Redundant enum constructor invocation
inspection.labeled.return.on.last.expression.in.lambda.display.name=Labeled return on the last expression in a lambda
inspection.replace.negated.is.empty.with.is.not.empty.display.name=Negated call can be simplified
inspection.function.with.lambda.expression.body.display.name=Function with '= { ... }' and inferred return type
inspection.suspend.function.on.coroutine.scope.display.name=Ambiguous coroutineContext due to CoroutineScope receiver of suspend function

View File

@@ -0,0 +1,26 @@
<html>
<body>
Reports labeled returns used on the last expressions in lambdas.
<p>Such returns are unnecessary and can be safely removed.</p>
<p><b>Example:</b></p>
<pre><code>
fun foo() {
listOf(1,2,3).find {
return@find true
}
}
</code></pre>
<p>After the quick-fix is applied:</p>
<pre><code>
fun foo() {
listOf(1,2,3).find {
true
}
}
</code></pre>
<!-- tooltip end -->
</body>
</html>

View File

@@ -1,5 +0,0 @@
fun foo() {
listOf(1,2,3).find {
<spot>return@find</spot> true
}
}

View File

@@ -1,5 +0,0 @@
<html>
<body>
Removes a labeled return from the last expression in a lambda.
</body>
</html>

View File

@@ -315,5 +315,13 @@
language="kotlin"
key="inspection.control.flow.with.empty.body.display.name" bundle="messages.KotlinBundle"/>
<localInspection implementationClass="org.jetbrains.kotlin.idea.codeInsight.inspections.shared.LabeledReturnAsLastExpressionInLambdaInspection"
groupPath="Kotlin"
groupBundle="messages.KotlinBundle" groupKey="group.names.redundant.constructs"
enabledByDefault="true"
level="INFORMATION"
language="kotlin"
key="inspection.labeled.return.on.last.expression.in.lambda.display.name" bundle="messages.KotlinBundle"/>
</extensions>
</idea-plugin>

View File

@@ -0,0 +1,68 @@
// Copyright 2000-2024 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package org.jetbrains.kotlin.idea.codeInsight.inspections.shared
import com.intellij.codeInspection.LocalInspectionToolSession
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.util.InspectionMessage
import com.intellij.codeInspection.util.IntentionFamilyName
import com.intellij.modcommand.ModPsiUpdater
import com.intellij.openapi.project.Project
import com.intellij.psi.PsiElementVisitor
import org.jetbrains.kotlin.idea.base.psi.getParentLambdaLabelName
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsight.api.applicable.inspections.AbstractKotlinApplicableInspection
import org.jetbrains.kotlin.idea.codeinsight.api.applicators.KotlinApplicabilityRange
import org.jetbrains.kotlin.idea.codeinsights.impl.base.applicators.ApplicabilityRanges
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtReturnExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
import org.jetbrains.kotlin.psi.returnExpressionVisitor
/**
* Tests:
* - [org.jetbrains.kotlin.idea.codeInsight.inspections.shared.SharedK1LocalInspectionTestGenerated.LabeledReturnAsLastExpressionInLambda]
* - [org.jetbrains.kotlin.idea.k2.codeInsight.inspections.shared.SharedK2LocalInspectionTestGenerated.LabeledReturnAsLastExpressionInLambda]
*/
internal class LabeledReturnAsLastExpressionInLambdaInspection : AbstractKotlinApplicableInspection<KtReturnExpression>() {
override fun getProblemDescription(element: KtReturnExpression): @InspectionMessage String =
KotlinBundle.message("inspection.labeled.return.on.last.expression.in.lambda.display.name")
override fun getActionFamilyName(): @IntentionFamilyName String =
KotlinBundle.message("remove.labeled.return.from.last.expression.in.a.lambda")
override fun getActionName(element: KtReturnExpression): @InspectionMessage String {
val labelName = element.getLabelName().orEmpty()
return KotlinBundle.message("remove.return.0", labelName)
}
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor =
returnExpressionVisitor {
visitTargetElement(it, holder, isOnTheFly)
}
override fun isApplicableByPsi(element: KtReturnExpression): Boolean {
val labelName = element.getLabelName() ?: return false
val block = element.getStrictParentOfType<KtBlockExpression>() ?: return false
if (block.statements.lastOrNull() != element) return false
val callName = block.getParentLambdaLabelName() ?: return false
if (labelName != callName) return false
return true
}
override fun getApplicabilityRange(): KotlinApplicabilityRange<KtReturnExpression> = ApplicabilityRanges.SELF
override fun apply(
element: KtReturnExpression,
project: Project,
updater: ModPsiUpdater
) {
val returnedExpression = element.returnedExpression
if (returnedExpression == null) {
element.delete()
} else {
element.replace(returnedExpression)
}
}
}

View File

@@ -516,6 +516,54 @@ public abstract class SharedK1LocalInspectionTestGenerated extends AbstractShare
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda")
public static class LabeledReturnAsLastExpressionInLambda extends AbstractSharedK1LocalInspectionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("labeledLambda.kt")
public void testLabeledLambda() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/labeledLambda.kt");
}
@TestMetadata("multipleBlocks.kt")
public void testMultipleBlocks() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/multipleBlocks.kt");
}
@TestMetadata("normal.kt")
public void testNormal() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/normal.kt");
}
@TestMetadata("notInsideLabeled.kt")
public void testNotInsideLabeled() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/notInsideLabeled.kt");
}
@TestMetadata("notLabeled.kt")
public void testNotLabeled() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/notLabeled.kt");
}
@TestMetadata("notLastLine.kt")
public void testNotLastLine() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/notLastLine.kt");
}
@TestMetadata("outerLambda.kt")
public void testOuterLambda() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/outerLambda.kt");
}
@TestMetadata("unit.kt")
public void testUnit() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/unit.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../testData/inspectionsLocal/redundantConstructorKeyword")
public static class RedundantConstructorKeyword extends AbstractSharedK1LocalInspectionTest {

View File

@@ -516,6 +516,54 @@ public abstract class SharedK2LocalInspectionTestGenerated extends AbstractShare
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda")
public static class LabeledReturnAsLastExpressionInLambda extends AbstractSharedK2LocalInspectionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("labeledLambda.kt")
public void testLabeledLambda() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/labeledLambda.kt");
}
@TestMetadata("multipleBlocks.kt")
public void testMultipleBlocks() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/multipleBlocks.kt");
}
@TestMetadata("normal.kt")
public void testNormal() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/normal.kt");
}
@TestMetadata("notInsideLabeled.kt")
public void testNotInsideLabeled() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/notInsideLabeled.kt");
}
@TestMetadata("notLabeled.kt")
public void testNotLabeled() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/notLabeled.kt");
}
@TestMetadata("notLastLine.kt")
public void testNotLastLine() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/notLastLine.kt");
}
@TestMetadata("outerLambda.kt")
public void testOuterLambda() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/outerLambda.kt");
}
@TestMetadata("unit.kt")
public void testUnit() throws Exception {
runTest("../testData/inspectionsLocal/labeledReturnAsLastExpressionInLambda/unit.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("../testData/inspectionsLocal/redundantConstructorKeyword")
public static class RedundantConstructorKeyword extends AbstractSharedK2LocalInspectionTest {

View File

@@ -0,0 +1 @@
org.jetbrains.kotlin.idea.codeInsight.inspections.shared.LabeledReturnAsLastExpressionInLambdaInspection

View File

@@ -6369,8 +6369,6 @@ public abstract class K2IntentionTestGenerated extends AbstractK2IntentionTest {

View File

@@ -1,36 +0,0 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.kotlin.idea.intentions
import com.intellij.codeInsight.intention.LowPriorityAction
import com.intellij.openapi.editor.Editor
import org.jetbrains.kotlin.idea.base.psi.getParentLambdaLabelName
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
import org.jetbrains.kotlin.idea.codeinsight.api.classic.intentions.SelfTargetingIntention
import org.jetbrains.kotlin.psi.KtBlockExpression
import org.jetbrains.kotlin.psi.KtReturnExpression
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
class RemoveLabeledReturnInLambdaIntention : SelfTargetingIntention<KtReturnExpression>(
KtReturnExpression::class.java,
KotlinBundle.lazyMessage("remove.labeled.return.from.last.expression.in.a.lambda")
), LowPriorityAction {
override fun isApplicableTo(element: KtReturnExpression, caretOffset: Int): Boolean {
val labelName = element.getLabelName() ?: return false
val block = element.getStrictParentOfType<KtBlockExpression>() ?: return false
if (block.statements.lastOrNull() != element) return false
val callName = block.getParentLambdaLabelName() ?: return false
if (labelName != callName) return false
setTextGetter { KotlinBundle.message("remove.return.0", labelName) }
return true
}
override fun applyTo(element: KtReturnExpression, editor: Editor?) {
val returnedExpression = element.returnedExpression
if (returnedExpression == null) {
element.delete()
} else {
element.replace(returnedExpression)
}
}
}

View File

@@ -15665,54 +15665,6 @@ public abstract class K1IntentionTestGenerated extends AbstractK1IntentionTest {
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/intentions/removeLabeledReturnInLambda")
public static class RemoveLabeledReturnInLambda extends AbstractK1IntentionTest {
private void runTest(String testDataFilePath) throws Exception {
KotlinTestUtils.runTest(this::doTest, this, testDataFilePath);
}
@TestMetadata("labeledLambda.kt")
public void testLabeledLambda() throws Exception {
runTest("testData/intentions/removeLabeledReturnInLambda/labeledLambda.kt");
}
@TestMetadata("multipleBlocks.kt")
public void testMultipleBlocks() throws Exception {
runTest("testData/intentions/removeLabeledReturnInLambda/multipleBlocks.kt");
}
@TestMetadata("normal.kt")
public void testNormal() throws Exception {
runTest("testData/intentions/removeLabeledReturnInLambda/normal.kt");
}
@TestMetadata("notInsideLabeled.kt")
public void testNotInsideLabeled() throws Exception {
runTest("testData/intentions/removeLabeledReturnInLambda/notInsideLabeled.kt");
}
@TestMetadata("notLabeled.kt")
public void testNotLabeled() throws Exception {
runTest("testData/intentions/removeLabeledReturnInLambda/notLabeled.kt");
}
@TestMetadata("notLastLine.kt")
public void testNotLastLine() throws Exception {
runTest("testData/intentions/removeLabeledReturnInLambda/notLastLine.kt");
}
@TestMetadata("outerLambda.kt")
public void testOuterLambda() throws Exception {
runTest("testData/intentions/removeLabeledReturnInLambda/outerLambda.kt");
}
@TestMetadata("unit.kt")
public void testUnit() throws Exception {
runTest("testData/intentions/removeLabeledReturnInLambda/unit.kt");
}
}
@RunWith(JUnit3RunnerWithInners.class)
@TestMetadata("testData/intentions/removeRedundantCallsOfConversionMethods")
public static class RemoveRedundantCallsOfConversionMethods extends AbstractK1IntentionTest {

View File

@@ -1 +0,0 @@
org.jetbrains.kotlin.idea.intentions.RemoveLabeledReturnInLambdaIntention

View File

@@ -784,13 +784,6 @@
<categoryKey>group.names.kotlin</categoryKey>
</intentionAction>
<intentionAction>
<language>kotlin</language>
<className>org.jetbrains.kotlin.idea.intentions.RemoveLabeledReturnInLambdaIntention</className>
<bundleName>messages.KotlinBundle</bundleName>
<categoryKey>group.names.kotlin</categoryKey>
</intentionAction>
<intentionAction>
<language>kotlin</language>
<className>org.jetbrains.kotlin.idea.intentions.AddLabeledReturnInLambdaIntention</className>

View File

@@ -154,7 +154,6 @@ internal fun MutableTWorkspace.generateK2IntentionTests() {
model("${idea}intentions/indentRawString", pattern = pattern, isIgnored = true)
model("${idea}intentions/replaceAddWithPlusAssign", pattern = pattern, isIgnored = true)
model("${idea}intentions/reconstructTypeInCastOrIs", pattern = pattern, isIgnored = true)
model("${idea}intentions/removeLabeledReturnInLambda", pattern = pattern, isIgnored = true)
model("${idea}intentions/convertParameterToReceiver", pattern = pattern, isIgnored = true)
model("${idea}intentions/convertCollectionConstructorToFunction", pattern = pattern, isIgnored = true)
model("${idea}intentions/replaceMapGetOrDefault", pattern = pattern, isIgnored = true)