Nested lambda has shadowed implicit parameter: do not report when lambda is in scope function
#KT-30173 Fixed
This commit is contained in:
committed by
Mikhail Glukhikh
parent
daf8df9e4b
commit
a2adfd0cc0
+19
-1
@@ -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
|
||||
}
|
||||
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun main() {
|
||||
listOf(42).map {
|
||||
it.also {
|
||||
<caret>it == 42
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun main() {
|
||||
listOf(42).map {
|
||||
it.let {
|
||||
<caret>it == 42
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun main() {
|
||||
listOf(42).map {
|
||||
it.takeIf {
|
||||
<caret>it == 42
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// PROBLEM: none
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun main() {
|
||||
listOf(42).map {
|
||||
it.takeUnless {
|
||||
<caret>it == 42
|
||||
}
|
||||
}
|
||||
}
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
// FIX: Replace 'it' with explicit parameter
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun main() {
|
||||
listOf(42).map {
|
||||
it == 42
|
||||
1.also {
|
||||
<caret>it == 42
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
// FIX: Replace 'it' with explicit parameter
|
||||
// WITH_RUNTIME
|
||||
|
||||
fun main() {
|
||||
listOf(42).map {
|
||||
it == 42
|
||||
1.also { it1 ->
|
||||
it1 == 42
|
||||
}
|
||||
}
|
||||
}
|
||||
+25
@@ -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")
|
||||
|
||||
Reference in New Issue
Block a user