Nested lambda has shadowed implicit parameter: do not report when outer lambda 'it' is not used
#KT-26710 Fixed
This commit is contained in:
committed by
Vyacheslav Gerasimov
parent
7ee13ca353
commit
a0162adbf9
+27
-10
@@ -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
|
||||
}
|
||||
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
||||
|
||||
fun test() {
|
||||
foo {
|
||||
bar(it)
|
||||
foo {
|
||||
bar(it<caret>)
|
||||
}
|
||||
|
||||
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
||||
|
||||
fun test() {
|
||||
foo { it ->
|
||||
bar(it)
|
||||
foo {
|
||||
bar(it)
|
||||
}
|
||||
|
||||
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
||||
|
||||
fun test() {
|
||||
foo {
|
||||
bar(it)
|
||||
foo {
|
||||
bar(it)
|
||||
bar(it)
|
||||
|
||||
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
||||
|
||||
fun test() {
|
||||
foo {
|
||||
bar(it)
|
||||
foo { it1 ->
|
||||
bar(it1)
|
||||
bar(it1)
|
||||
|
||||
Vendored
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
||||
|
||||
fun test() {
|
||||
foo {
|
||||
bar(it)
|
||||
foo { s ->
|
||||
foo {
|
||||
bar(it<caret>)
|
||||
|
||||
Vendored
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
||||
|
||||
fun test() {
|
||||
foo { it ->
|
||||
bar(it)
|
||||
foo { s ->
|
||||
foo {
|
||||
bar(it)
|
||||
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
// PROBLEM: none
|
||||
|
||||
fun foo(f: (String) -> Unit) {}
|
||||
fun bar(s: String) {}
|
||||
|
||||
fun test() {
|
||||
foo {
|
||||
foo {
|
||||
foo {
|
||||
bar(it<caret>)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
+5
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user