diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt index 021c2e0af74..0e5b8f4f04d 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/VisitorWrappers.kt @@ -402,3 +402,11 @@ fun qualifiedExpressionRecursiveVisitor(block: (KtQualifiedExpression) -> Unit) block(qualifiedExpression) } } + +fun returnExpressionVisitor(block: (KtReturnExpression) -> Unit) = + object : KtVisitorVoid() { + override fun visitReturnExpression(returnExpression: KtReturnExpression) { + super.visitReturnExpression(returnExpression) + block(returnExpression) + } + } diff --git a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt index c8ebcaff7bf..c3004daea23 100644 --- a/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt +++ b/compiler/psi/src/org/jetbrains/kotlin/psi/psiUtil/psiUtils.kt @@ -130,6 +130,10 @@ inline fun PsiElement.getParentOfType(strict: Boolean, vararg stopAt: Class): T? { + return PsiTreeUtil.getParentOfType(this, T::class.java, strict, *stopAt) +} + inline fun PsiElement.getStrictParentOfType(): T? { return PsiTreeUtil.getParentOfType(this, T::class.java, true) } diff --git a/idea/resources/inspectionDescriptions/RedundantReturnLabel.html b/idea/resources/inspectionDescriptions/RedundantReturnLabel.html new file mode 100644 index 00000000000..b4ad30140e8 --- /dev/null +++ b/idea/resources/inspectionDescriptions/RedundantReturnLabel.html @@ -0,0 +1,5 @@ + + +This inspection reports redundant return label outside of lambda. + + \ No newline at end of file diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml index 4708368f36f..22e14d43e09 100644 --- a/idea/src/META-INF/plugin.xml +++ b/idea/src/META-INF/plugin.xml @@ -3011,6 +3011,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.173 b/idea/src/META-INF/plugin.xml.173 index 93adbadeef1..ff73b7e1f8f 100644 --- a/idea/src/META-INF/plugin.xml.173 +++ b/idea/src/META-INF/plugin.xml.173 @@ -3010,6 +3010,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.181 b/idea/src/META-INF/plugin.xml.181 index 69c280d06eb..b0d09d9b2b4 100644 --- a/idea/src/META-INF/plugin.xml.181 +++ b/idea/src/META-INF/plugin.xml.181 @@ -3010,6 +3010,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.183 b/idea/src/META-INF/plugin.xml.183 index d0959fc81c2..13ed4684275 100644 --- a/idea/src/META-INF/plugin.xml.183 +++ b/idea/src/META-INF/plugin.xml.183 @@ -3011,6 +3011,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.as31 b/idea/src/META-INF/plugin.xml.as31 index 6c7db7ca6e7..9e569610a1c 100644 --- a/idea/src/META-INF/plugin.xml.as31 +++ b/idea/src/META-INF/plugin.xml.as31 @@ -3010,6 +3010,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.as32 b/idea/src/META-INF/plugin.xml.as32 index 26146d5d860..c6d5d2c516f 100644 --- a/idea/src/META-INF/plugin.xml.as32 +++ b/idea/src/META-INF/plugin.xml.as32 @@ -3010,6 +3010,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/META-INF/plugin.xml.as33 b/idea/src/META-INF/plugin.xml.as33 index 82c2ff5266d..d9e77a8cfbf 100644 --- a/idea/src/META-INF/plugin.xml.as33 +++ b/idea/src/META-INF/plugin.xml.as33 @@ -3012,6 +3012,15 @@ The Kotlin plugin provides language support in IntelliJ IDEA and Android Studio. language="kotlin" /> + + diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantReturnLabelInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantReturnLabelInspection.kt new file mode 100644 index 00000000000..36df196c39a --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantReturnLabelInspection.kt @@ -0,0 +1,43 @@ +/* + * Copyright 2010-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.inspections + +import com.intellij.codeInspection.LocalQuickFix +import com.intellij.codeInspection.ProblemDescriptor +import com.intellij.codeInspection.ProblemHighlightType +import com.intellij.codeInspection.ProblemsHolder +import com.intellij.openapi.project.Project +import com.intellij.psi.PsiElementVisitor +import org.jetbrains.kotlin.psi.KtLambdaExpression +import org.jetbrains.kotlin.psi.KtNamedFunction +import org.jetbrains.kotlin.psi.psiUtil.getParentOfType +import org.jetbrains.kotlin.psi.returnExpressionVisitor + +class RedundantReturnLabelInspection : AbstractKotlinInspection() { + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor = + returnExpressionVisitor(fun(returnExpression) { + val label = returnExpression.getTargetLabel() ?: return + val function = returnExpression.getParentOfType(true, KtLambdaExpression::class.java) ?: return + if (function.name == null) return + val labelName = label.getReferencedName() + holder.registerProblem( + label, + "Redundant '@$labelName'", + ProblemHighlightType.LIKE_UNUSED_SYMBOL, + RemoveReturnLabelFix(labelName) + ) + }) +} + +private class RemoveReturnLabelFix(private val labelName: String) : LocalQuickFix { + override fun getName() = "Remove redundant '@$labelName'" + + override fun getFamilyName() = name + + override fun applyFix(project: Project, descriptor: ProblemDescriptor) { + descriptor.psiElement?.delete() + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantReturnLabel/.inspection b/idea/testData/inspectionsLocal/redundantReturnLabel/.inspection new file mode 100644 index 00000000000..ded2c2a43ae --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantReturnLabel/.inspection @@ -0,0 +1 @@ +org.jetbrains.kotlin.idea.inspections.RedundantReturnLabelInspection \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantReturnLabel/inAnonymousFunction.kt b/idea/testData/inspectionsLocal/redundantReturnLabel/inAnonymousFunction.kt new file mode 100644 index 00000000000..d714bfd763e --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantReturnLabel/inAnonymousFunction.kt @@ -0,0 +1,9 @@ +// PROBLEM: none +fun foo(f: (String?) -> Int) {} + +fun test() { + foo(fun(it: String?): Int { + if (it != null) return@foo 1 + return 0 + }) +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction.kt b/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction.kt new file mode 100644 index 00000000000..36edc84888b --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction.kt @@ -0,0 +1,3 @@ +fun test() { + return@test +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction.kt.after b/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction.kt.after new file mode 100644 index 00000000000..4d3735975a5 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction.kt.after @@ -0,0 +1,3 @@ +fun test() { + return +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction2.kt b/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction2.kt new file mode 100644 index 00000000000..cf871648cf2 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction2.kt @@ -0,0 +1,6 @@ +fun test(s: String?): Int { + if (s != null) { + return@test 1 + } + return 0 +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction2.kt.after b/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction2.kt.after new file mode 100644 index 00000000000..d7a47c66f7e --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantReturnLabel/inFunction2.kt.after @@ -0,0 +1,6 @@ +fun test(s: String?): Int { + if (s != null) { + return 1 + } + return 0 +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantReturnLabel/inLambda.kt b/idea/testData/inspectionsLocal/redundantReturnLabel/inLambda.kt new file mode 100644 index 00000000000..9da508f8318 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantReturnLabel/inLambda.kt @@ -0,0 +1,9 @@ +// PROBLEM: none +fun foo(f: (String?) -> Int) {} + +fun test() { + foo { + if (it != null) return@foo 1 + 0 + } +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 2c0f6123aea..60a7cb954b8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -3948,6 +3948,39 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { } } + @TestMetadata("idea/testData/inspectionsLocal/redundantReturnLabel") + @TestDataPath("$PROJECT_ROOT") + @RunWith(JUnit3RunnerWithInners.class) + public static class RedundantReturnLabel extends AbstractLocalInspectionTest { + private void runTest(String testDataFilePath) throws Exception { + KotlinTestUtils.runTest(this::doTest, TargetBackend.ANY, testDataFilePath); + } + + public void testAllFilesPresentInRedundantReturnLabel() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantReturnLabel"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); + } + + @TestMetadata("inAnonymousFunction.kt") + public void testInAnonymousFunction() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantReturnLabel/inAnonymousFunction.kt"); + } + + @TestMetadata("inFunction.kt") + public void testInFunction() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantReturnLabel/inFunction.kt"); + } + + @TestMetadata("inFunction2.kt") + public void testInFunction2() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantReturnLabel/inFunction2.kt"); + } + + @TestMetadata("inLambda.kt") + public void testInLambda() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantReturnLabel/inLambda.kt"); + } + } + @TestMetadata("idea/testData/inspectionsLocal/redundantSamConstructor") @TestDataPath("$PROJECT_ROOT") @RunWith(JUnit3RunnerWithInners.class)