diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/NestedLambdaShadowedImplicitParameterInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/NestedLambdaShadowedImplicitParameterInspection.kt index f1beb1dc6f4..280876a0ac1 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/NestedLambdaShadowedImplicitParameterInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/NestedLambdaShadowedImplicitParameterInspection.kt @@ -9,6 +9,7 @@ import com.intellij.codeInspection.* import com.intellij.openapi.project.Project import com.intellij.psi.PsiDocumentManager import com.intellij.psi.PsiElementVisitor +import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze import org.jetbrains.kotlin.idea.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler @@ -19,16 +20,18 @@ import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode class NestedLambdaShadowedImplicitParameterInspection : AbstractKotlinInspection() { - override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { return lambdaExpressionVisitor(fun(lambda: KtLambdaExpression) { if (lambda.valueParameters.isNotEmpty()) return - val context = lambda.analyze(BodyResolveMode.PARTIAL) - val implicitParameter = lambda.functionDescriptor(context)?.valueParameters?.singleOrNull() ?: return + if (lambda.getStrictParentOfType() == null) return + + val context = lambda.analyze() + val implicitParameter = lambda.getImplicitParameter(context) ?: return if (lambda.getParentImplicitParameterLambda(context) == null) return + val containingFile = lambda.containingFile lambda.forEachDescendantOfType { - if (it.text == "it" && it.getResolvedCall(context)?.resultingDescriptor == implicitParameter) { + if (it.isImplicitParameterReference(lambda, implicitParameter, context)) { holder.registerProblem( it, "Implicit parameter 'it' of enclosing lambda is shadowed", @@ -61,12 +64,26 @@ class NestedLambdaShadowedImplicitParameterInspection : AbstractKotlinInspection } } -private fun KtLambdaExpression.functionDescriptor(context: BindingContext) = context[BindingContext.FUNCTION, functionLiteral] +private fun KtLambdaExpression.getImplicitParameter(context: BindingContext): ValueParameterDescriptor? { + return context[BindingContext.FUNCTION, functionLiteral]?.valueParameters?.singleOrNull() +} -private fun KtExpression.getParentImplicitParameterLambda( - context: BindingContext = this.analyze(BodyResolveMode.PARTIAL) -): KtLambdaExpression? { - return getParentOfTypesAndPredicate(true, KtLambdaExpression::class.java) { - it.valueParameters.isEmpty() && it.functionDescriptor(context)?.valueParameters?.size == 1 +private fun KtLambdaExpression.getParentImplicitParameterLambda(context: BindingContext = this.analyze()): KtLambdaExpression? { + return getParentOfTypesAndPredicate(true, KtLambdaExpression::class.java) { lambda -> + if (lambda.valueParameters.isNotEmpty()) return@getParentOfTypesAndPredicate false + val implicitParameter = lambda.getImplicitParameter(context) ?: return@getParentOfTypesAndPredicate false + lambda.anyDescendantOfType { + it.isImplicitParameterReference(lambda, implicitParameter, context) + } } } + +private fun KtNameReferenceExpression.isImplicitParameterReference( + lambda: KtLambdaExpression, + implicitParameter: ValueParameterDescriptor, + context: BindingContext +): Boolean { + return text == "it" + && getStrictParentOfType() == lambda + && getResolvedCall(context)?.resultingDescriptor == implicitParameter +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit.kt index e2cf50fa0aa..afe7a70cfa9 100644 --- a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit.kt +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit.kt @@ -5,6 +5,7 @@ fun bar(s: String) {} fun test() { foo { + bar(it) foo { bar(it) } diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit.kt.after b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit.kt.after index 87f0b18cc34..121c97828c6 100644 --- a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit.kt.after +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit.kt.after @@ -5,6 +5,7 @@ fun bar(s: String) {} fun test() { foo { it -> + bar(it) foo { bar(it) } diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit3.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit3.kt index 2df390be8fa..e6a21caf258 100644 --- a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit3.kt +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit3.kt @@ -5,6 +5,7 @@ fun bar(s: String) {} fun test() { foo { + bar(it) foo { bar(it) bar(it) diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit3.kt.after b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit3.kt.after index e487afac30e..3567af49104 100644 --- a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit3.kt.after +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicit3.kt.after @@ -5,6 +5,7 @@ fun bar(s: String) {} fun test() { foo { + bar(it) foo { it1 -> bar(it1) bar(it1) diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicitGrandParent.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicitGrandParent.kt index b1fb5240806..4906759bc15 100644 --- a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicitGrandParent.kt +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicitGrandParent.kt @@ -5,6 +5,7 @@ fun bar(s: String) {} fun test() { foo { + bar(it) foo { s -> foo { bar(it) diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicitGrandParent.kt.after b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicitGrandParent.kt.after index 25c9b6e361c..bc15cd9edbb 100644 --- a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicitGrandParent.kt.after +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/implicitGrandParent.kt.after @@ -5,6 +5,7 @@ fun bar(s: String) {} fun test() { foo { it -> + bar(it) foo { s -> foo { bar(it) diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/notUsedItInParentLambda.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/notUsedItInParentLambda.kt new file mode 100644 index 00000000000..e87e0724dbf --- /dev/null +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/notUsedItInParentLambda.kt @@ -0,0 +1,14 @@ +// PROBLEM: none + +fun foo(f: (String) -> Unit) {} +fun bar(s: String) {} + +fun test() { + foo { + foo { + foo { + bar(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 09d94cac49d..b7a0f8a0f72 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -3800,6 +3800,11 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/notUseParameter.kt"); } + @TestMetadata("notUsedItInParentLambda.kt") + public void testNotUsedItInParentLambda() throws Exception { + runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/notUsedItInParentLambda.kt"); + } + @TestMetadata("receiver.kt") public void testReceiver() throws Exception { runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/receiver.kt");