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.openapi.project.Project
|
||||||
import com.intellij.psi.PsiDocumentManager
|
import com.intellij.psi.PsiDocumentManager
|
||||||
import com.intellij.psi.PsiElementVisitor
|
import com.intellij.psi.PsiElementVisitor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||||
import org.jetbrains.kotlin.idea.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention
|
import org.jetbrains.kotlin.idea.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention
|
||||||
import org.jetbrains.kotlin.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
|
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
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
|
|
||||||
class NestedLambdaShadowedImplicitParameterInspection : AbstractKotlinInspection() {
|
class NestedLambdaShadowedImplicitParameterInspection : AbstractKotlinInspection() {
|
||||||
|
|
||||||
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
|
||||||
return lambdaExpressionVisitor(fun(lambda: KtLambdaExpression) {
|
return lambdaExpressionVisitor(fun(lambda: KtLambdaExpression) {
|
||||||
if (lambda.valueParameters.isNotEmpty()) return
|
if (lambda.valueParameters.isNotEmpty()) return
|
||||||
val context = lambda.analyze(BodyResolveMode.PARTIAL)
|
if (lambda.getStrictParentOfType<KtLambdaExpression>() == null) return
|
||||||
val implicitParameter = lambda.functionDescriptor(context)?.valueParameters?.singleOrNull() ?: return
|
|
||||||
|
val context = lambda.analyze()
|
||||||
|
val implicitParameter = lambda.getImplicitParameter(context) ?: return
|
||||||
if (lambda.getParentImplicitParameterLambda(context) == null) return
|
if (lambda.getParentImplicitParameterLambda(context) == null) return
|
||||||
|
|
||||||
val containingFile = lambda.containingFile
|
val containingFile = lambda.containingFile
|
||||||
lambda.forEachDescendantOfType<KtNameReferenceExpression> {
|
lambda.forEachDescendantOfType<KtNameReferenceExpression> {
|
||||||
if (it.text == "it" && it.getResolvedCall(context)?.resultingDescriptor == implicitParameter) {
|
if (it.isImplicitParameterReference(lambda, implicitParameter, context)) {
|
||||||
holder.registerProblem(
|
holder.registerProblem(
|
||||||
it,
|
it,
|
||||||
"Implicit parameter 'it' of enclosing lambda is shadowed",
|
"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(
|
private fun KtLambdaExpression.getParentImplicitParameterLambda(context: BindingContext = this.analyze()): KtLambdaExpression? {
|
||||||
context: BindingContext = this.analyze(BodyResolveMode.PARTIAL)
|
return getParentOfTypesAndPredicate(true, KtLambdaExpression::class.java) { lambda ->
|
||||||
): KtLambdaExpression? {
|
if (lambda.valueParameters.isNotEmpty()) return@getParentOfTypesAndPredicate false
|
||||||
return getParentOfTypesAndPredicate(true, KtLambdaExpression::class.java) {
|
val implicitParameter = lambda.getImplicitParameter(context) ?: return@getParentOfTypesAndPredicate false
|
||||||
it.valueParameters.isEmpty() && it.functionDescriptor(context)?.valueParameters?.size == 1
|
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() {
|
fun test() {
|
||||||
foo {
|
foo {
|
||||||
|
bar(it)
|
||||||
foo {
|
foo {
|
||||||
bar(it<caret>)
|
bar(it<caret>)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
foo { it ->
|
foo { it ->
|
||||||
|
bar(it)
|
||||||
foo {
|
foo {
|
||||||
bar(it)
|
bar(it)
|
||||||
}
|
}
|
||||||
|
|||||||
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
foo {
|
foo {
|
||||||
|
bar(it)
|
||||||
foo {
|
foo {
|
||||||
bar(it)
|
bar(it)
|
||||||
bar(it)
|
bar(it)
|
||||||
|
|||||||
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
foo {
|
foo {
|
||||||
|
bar(it)
|
||||||
foo { it1 ->
|
foo { it1 ->
|
||||||
bar(it1)
|
bar(it1)
|
||||||
bar(it1)
|
bar(it1)
|
||||||
|
|||||||
Vendored
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
foo {
|
foo {
|
||||||
|
bar(it)
|
||||||
foo { s ->
|
foo { s ->
|
||||||
foo {
|
foo {
|
||||||
bar(it<caret>)
|
bar(it<caret>)
|
||||||
|
|||||||
Vendored
+1
@@ -5,6 +5,7 @@ fun bar(s: String) {}
|
|||||||
|
|
||||||
fun test() {
|
fun test() {
|
||||||
foo { it ->
|
foo { it ->
|
||||||
|
bar(it)
|
||||||
foo { s ->
|
foo { s ->
|
||||||
foo {
|
foo {
|
||||||
bar(it)
|
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");
|
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")
|
@TestMetadata("receiver.kt")
|
||||||
public void testReceiver() throws Exception {
|
public void testReceiver() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/receiver.kt");
|
runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/receiver.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user