diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantLambdaArrowInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantLambdaArrowInspection.kt index 8712fea2408..5cf793380c5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantLambdaArrowInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/RedundantLambdaArrowInspection.kt @@ -14,10 +14,7 @@ import com.intellij.openapi.project.Project import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiElementVisitor import org.jetbrains.kotlin.KtNodeTypes -import org.jetbrains.kotlin.psi.KtContainerNodeForControlStructureBody -import org.jetbrains.kotlin.psi.KtFunctionLiteral -import org.jetbrains.kotlin.psi.KtWhenEntry -import org.jetbrains.kotlin.psi.lambdaExpressionVisitor +import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.endOffset import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType import org.jetbrains.kotlin.psi.psiUtil.startOffset @@ -25,19 +22,25 @@ import org.jetbrains.kotlin.resolve.calls.util.isSingleUnderscore class RedundantLambdaArrowInspection : AbstractKotlinInspection() { override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { - return lambdaExpressionVisitor { lambdaExpression -> + return lambdaExpressionVisitor(fun(lambdaExpression: KtLambdaExpression) { val functionLiteral = lambdaExpression.functionLiteral - val arrow = functionLiteral.arrow ?: return@lambdaExpressionVisitor + val arrow = functionLiteral.arrow ?: return val parameters = functionLiteral.valueParameters val singleParameter = parameters.singleOrNull() if (parameters.isNotEmpty() && singleParameter?.isSingleUnderscore != true && singleParameter?.name != "it") { - return@lambdaExpressionVisitor + return } - if (lambdaExpression.getStrictParentOfType()?.expression == lambdaExpression) return@lambdaExpressionVisitor + if (lambdaExpression.getStrictParentOfType()?.expression == lambdaExpression) return if (lambdaExpression.getStrictParentOfType()?.let { it.node.elementType in listOf(KtNodeTypes.THEN, KtNodeTypes.ELSE) && it.expression == lambdaExpression - } == true) return@lambdaExpressionVisitor + } == true) return + + val callExpression = lambdaExpression.parent?.parent as? KtCallExpression + if (callExpression != null) { + val callee = callExpression.calleeExpression as? KtNameReferenceExpression + if (callee != null && callee.getReferencedName() == "forEach" && singleParameter?.name != "it") return + } val startOffset = functionLiteral.startOffset holder.registerProblem( @@ -50,7 +53,7 @@ class RedundantLambdaArrowInspection : AbstractKotlinInspection() { DeleteFix() ) ) - } + }) } class DeleteFix : LocalQuickFix { diff --git a/idea/testData/inspectionsLocal/redundantLambdaArrow/forEach.kt b/idea/testData/inspectionsLocal/redundantLambdaArrow/forEach.kt new file mode 100644 index 00000000000..8c7bb378e05 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantLambdaArrow/forEach.kt @@ -0,0 +1,8 @@ +// PROBLEM: none +// WITH_RUNTIME + +fun test() { + // We do not report "redundant arrow" here, + // because it's used to explicitly call forEach with unused lambda parameter + listOf(1, 2, 3).forEach { _ -> } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantLambdaArrow/forEachWithIt.kt b/idea/testData/inspectionsLocal/redundantLambdaArrow/forEachWithIt.kt new file mode 100644 index 00000000000..cf785dec4ff --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantLambdaArrow/forEachWithIt.kt @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun println(s: String) {} + +fun test() { + listOf(1, 2, 3).forEach { it -> println(it) } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/redundantLambdaArrow/forEachWithIt.kt.after b/idea/testData/inspectionsLocal/redundantLambdaArrow/forEachWithIt.kt.after new file mode 100644 index 00000000000..36fdfe2dbb5 --- /dev/null +++ b/idea/testData/inspectionsLocal/redundantLambdaArrow/forEachWithIt.kt.after @@ -0,0 +1,7 @@ +// WITH_RUNTIME + +fun println(s: String) {} + +fun test() { + listOf(1, 2, 3).forEach { println(it) } +} \ 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 92a6630d32a..1f77a160ae8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -4447,6 +4447,16 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/inspectionsLocal/redundantLambdaArrow"), Pattern.compile("^([\\w\\-_]+)\\.(kt|kts)$"), TargetBackend.ANY, true); } + @TestMetadata("forEach.kt") + public void testForEach() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantLambdaArrow/forEach.kt"); + } + + @TestMetadata("forEachWithIt.kt") + public void testForEachWithIt() throws Exception { + runTest("idea/testData/inspectionsLocal/redundantLambdaArrow/forEachWithIt.kt"); + } + @TestMetadata("hasArguments.kt") public void testHasArguments() throws Exception { runTest("idea/testData/inspectionsLocal/redundantLambdaArrow/hasArguments.kt");