Nested lambda has shadowed implicit parameter: do not report when outer lambda 'it' is not used

#KT-26710 Fixed
This commit is contained in:
Toshiaki Kameyama
2018-11-26 19:43:46 +09:00
committed by Vyacheslav Gerasimov
parent 7ee13ca353
commit a0162adbf9
9 changed files with 52 additions and 10 deletions
@@ -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<KtLambdaExpression>() == null) return
val context = lambda.analyze()
val implicitParameter = lambda.getImplicitParameter(context) ?: return
if (lambda.getParentImplicitParameterLambda(context) == null) return
val containingFile = lambda.containingFile
lambda.forEachDescendantOfType<KtNameReferenceExpression> {
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<KtNameReferenceExpression> {
it.isImplicitParameterReference(lambda, implicitParameter, context)
}
}
}
private fun KtNameReferenceExpression.isImplicitParameterReference(
lambda: KtLambdaExpression,
implicitParameter: ValueParameterDescriptor,
context: BindingContext
): Boolean {
return text == "it"
&& getStrictParentOfType<KtLambdaExpression>() == lambda
&& getResolvedCall(context)?.resultingDescriptor == implicitParameter
}
@@ -5,6 +5,7 @@ fun bar(s: String) {}
fun test() {
foo {
bar(it)
foo {
bar(it<caret>)
}
@@ -5,6 +5,7 @@ fun bar(s: String) {}
fun test() {
foo { it ->
bar(it)
foo {
bar(it)
}
@@ -5,6 +5,7 @@ fun bar(s: String) {}
fun test() {
foo {
bar(it)
foo {
bar(it)
bar(it)
@@ -5,6 +5,7 @@ fun bar(s: String) {}
fun test() {
foo {
bar(it)
foo { it1 ->
bar(it1)
bar(it1)
@@ -5,6 +5,7 @@ fun bar(s: String) {}
fun test() {
foo {
bar(it)
foo { s ->
foo {
bar(it<caret>)
@@ -5,6 +5,7 @@ fun bar(s: String) {}
fun test() {
foo { it ->
bar(it)
foo { s ->
foo {
bar(it)
@@ -0,0 +1,14 @@
// PROBLEM: none
fun foo(f: (String) -> Unit) {}
fun bar(s: String) {}
fun test() {
foo {
foo {
foo {
bar(it<caret>)
}
}
}
}
@@ -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");