From a2adfd0cc0348b904caa88a43c049f5de37e994d Mon Sep 17 00:00:00 2001 From: Toshiaki Kameyama Date: Tue, 19 Mar 2019 16:19:47 +0900 Subject: [PATCH] Nested lambda has shadowed implicit parameter: do not report when lambda is in scope function #KT-30173 Fixed --- ...mbdaShadowedImplicitParameterInspection.kt | 20 ++++++++++++++- .../inspections/collections/FunctionUtils.kt | 8 ++++-- .../scopeFunction.kt | 10 ++++++++ .../scopeFunction2.kt | 10 ++++++++ .../scopeFunction3.kt | 10 ++++++++ .../scopeFunction4.kt | 10 ++++++++ .../scopeFunction5.kt | 11 ++++++++ .../scopeFunction5.kt.after | 11 ++++++++ .../LocalInspectionTestGenerated.java | 25 +++++++++++++++++++ 9 files changed, 112 insertions(+), 3 deletions(-) create mode 100644 idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction.kt create mode 100644 idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction2.kt create mode 100644 idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction3.kt create mode 100644 idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction4.kt create mode 100644 idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt create mode 100644 idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt.after diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/NestedLambdaShadowedImplicitParameterInspection.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/NestedLambdaShadowedImplicitParameterInspection.kt index 280876a0ac1..1469a59d827 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/NestedLambdaShadowedImplicitParameterInspection.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/NestedLambdaShadowedImplicitParameterInspection.kt @@ -11,15 +11,26 @@ 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.inspections.collections.isCalling import org.jetbrains.kotlin.idea.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention +import org.jetbrains.kotlin.idea.intentions.callExpression import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler +import org.jetbrains.kotlin.name.FqName import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall -import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode class NestedLambdaShadowedImplicitParameterInspection : AbstractKotlinInspection() { + companion object { + val scopeFunctions = listOf( + "kotlin.also", + "kotlin.let", + "kotlin.takeIf", + "kotlin.takeUnless" + ).map { FqName(it) } + } + override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor { return lambdaExpressionVisitor(fun(lambda: KtLambdaExpression) { if (lambda.valueParameters.isNotEmpty()) return @@ -29,6 +40,13 @@ class NestedLambdaShadowedImplicitParameterInspection : AbstractKotlinInspection val implicitParameter = lambda.getImplicitParameter(context) ?: return if (lambda.getParentImplicitParameterLambda(context) == null) return + val qualifiedExpression = lambda.getStrictParentOfType() + if (qualifiedExpression != null) { + val receiver = qualifiedExpression.receiverExpression + val call = qualifiedExpression.callExpression + if (receiver.text == "it" && call?.isCalling(scopeFunctions, context) == true) return + } + val containingFile = lambda.containingFile lambda.forEachDescendantOfType { if (it.isImplicitParameterReference(lambda, implicitParameter, context)) { diff --git a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt index d35dec1e307..384debdfe8a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/inspections/collections/FunctionUtils.kt @@ -49,8 +49,12 @@ fun KotlinType?.isIterable(builtIns: KotlinBuiltIns): Boolean { } fun KtCallExpression.isCalling(fqName: FqName, context: BindingContext = analyze(BodyResolveMode.PARTIAL)): Boolean { - val function = fqName.asString().takeLastWhile { it != '.' } - if (calleeExpression?.text != function) return false + return isCalling(listOf(fqName), context) +} + +fun KtCallExpression.isCalling(fqNames: List, context: BindingContext = analyze(BodyResolveMode.PARTIAL)): Boolean { + val calleeText = calleeExpression?.text ?: return false + val fqName = fqNames.firstOrNull { fqName -> fqName.asString().takeLastWhile { it != '.' } == calleeText } ?: return false return getResolvedCall(context)?.isCalling(fqName) == true } diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction.kt new file mode 100644 index 00000000000..526e7b7284c --- /dev/null +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +// WITH_RUNTIME + +fun main() { + listOf(42).map { + it.also { + it == 42 + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction2.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction2.kt new file mode 100644 index 00000000000..6696e1b16dd --- /dev/null +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction2.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +// WITH_RUNTIME + +fun main() { + listOf(42).map { + it.let { + it == 42 + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction3.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction3.kt new file mode 100644 index 00000000000..d994ec8e282 --- /dev/null +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction3.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +// WITH_RUNTIME + +fun main() { + listOf(42).map { + it.takeIf { + it == 42 + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction4.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction4.kt new file mode 100644 index 00000000000..7edd93eb7b1 --- /dev/null +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction4.kt @@ -0,0 +1,10 @@ +// PROBLEM: none +// WITH_RUNTIME + +fun main() { + listOf(42).map { + it.takeUnless { + it == 42 + } + } +} \ No newline at end of file diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt new file mode 100644 index 00000000000..f13fa16fefa --- /dev/null +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt @@ -0,0 +1,11 @@ +// FIX: Replace 'it' with explicit parameter +// WITH_RUNTIME + +fun main() { + listOf(42).map { + it == 42 + 1.also { + it == 42 + } + } +} diff --git a/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt.after b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt.after new file mode 100644 index 00000000000..98cc0c262bb --- /dev/null +++ b/idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt.after @@ -0,0 +1,11 @@ +// FIX: Replace 'it' with explicit parameter +// WITH_RUNTIME + +fun main() { + listOf(42).map { + it == 42 + 1.also { it1 -> + it1 == 42 + } + } +} diff --git a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java index 16691507207..ad3432354f4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/inspections/LocalInspectionTestGenerated.java @@ -4454,6 +4454,31 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest { public void testReceiverParent() throws Exception { runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/receiverParent.kt"); } + + @TestMetadata("scopeFunction.kt") + public void testScopeFunction() throws Exception { + runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction.kt"); + } + + @TestMetadata("scopeFunction2.kt") + public void testScopeFunction2() throws Exception { + runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction2.kt"); + } + + @TestMetadata("scopeFunction3.kt") + public void testScopeFunction3() throws Exception { + runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction3.kt"); + } + + @TestMetadata("scopeFunction4.kt") + public void testScopeFunction4() throws Exception { + runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction4.kt"); + } + + @TestMetadata("scopeFunction5.kt") + public void testScopeFunction5() throws Exception { + runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/scopeFunction5.kt"); + } } @TestMetadata("idea/testData/inspectionsLocal/nullChecksToSafeCall")