Nested lambda has shadowed implicit parameter: do not report when lambda is in scope function

#KT-30173 Fixed
This commit is contained in:
Toshiaki Kameyama
2019-03-19 16:19:47 +09:00
committed by Mikhail Glukhikh
parent daf8df9e4b
commit a2adfd0cc0
9 changed files with 112 additions and 3 deletions
@@ -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<KtQualifiedExpression>()
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<KtNameReferenceExpression> {
if (it.isImplicitParameterReference(lambda, implicitParameter, context)) {
@@ -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<FqName>, 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
}
@@ -0,0 +1,10 @@
// PROBLEM: none
// WITH_RUNTIME
fun main() {
listOf(42).map {
it.also {
<caret>it == 42
}
}
}
@@ -0,0 +1,10 @@
// PROBLEM: none
// WITH_RUNTIME
fun main() {
listOf(42).map {
it.let {
<caret>it == 42
}
}
}
@@ -0,0 +1,10 @@
// PROBLEM: none
// WITH_RUNTIME
fun main() {
listOf(42).map {
it.takeIf {
<caret>it == 42
}
}
}
@@ -0,0 +1,10 @@
// PROBLEM: none
// WITH_RUNTIME
fun main() {
listOf(42).map {
it.takeUnless {
<caret>it == 42
}
}
}
@@ -0,0 +1,11 @@
// FIX: Replace 'it' with explicit parameter
// WITH_RUNTIME
fun main() {
listOf(42).map {
it == 42
1.also {
<caret>it == 42
}
}
}
@@ -0,0 +1,11 @@
// FIX: Replace 'it' with explicit parameter
// WITH_RUNTIME
fun main() {
listOf(42).map {
it == 42
1.also { it1 ->
it1 == 42
}
}
}
@@ -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")