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 com.intellij.psi.PsiElementVisitor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
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.inspections.collections.isCalling
|
||||||
import org.jetbrains.kotlin.idea.intentions.ReplaceItWithExplicitFunctionLiteralParamIntention
|
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.idea.refactoring.rename.KotlinVariableInplaceRenameHandler
|
||||||
|
import org.jetbrains.kotlin.name.FqName
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.*
|
import org.jetbrains.kotlin.psi.psiUtil.*
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
|
||||||
|
|
||||||
class NestedLambdaShadowedImplicitParameterInspection : AbstractKotlinInspection() {
|
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 {
|
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
|
||||||
@@ -29,6 +40,13 @@ class NestedLambdaShadowedImplicitParameterInspection : AbstractKotlinInspection
|
|||||||
val implicitParameter = lambda.getImplicitParameter(context) ?: return
|
val implicitParameter = lambda.getImplicitParameter(context) ?: return
|
||||||
if (lambda.getParentImplicitParameterLambda(context) == null) 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
|
val containingFile = lambda.containingFile
|
||||||
lambda.forEachDescendantOfType<KtNameReferenceExpression> {
|
lambda.forEachDescendantOfType<KtNameReferenceExpression> {
|
||||||
if (it.isImplicitParameterReference(lambda, implicitParameter, context)) {
|
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 {
|
fun KtCallExpression.isCalling(fqName: FqName, context: BindingContext = analyze(BodyResolveMode.PARTIAL)): Boolean {
|
||||||
val function = fqName.asString().takeLastWhile { it != '.' }
|
return isCalling(listOf(fqName), context)
|
||||||
if (calleeExpression?.text != function) return false
|
}
|
||||||
|
|
||||||
|
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
|
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 {
|
public void testReceiverParent() throws Exception {
|
||||||
runTest("idea/testData/inspectionsLocal/nestedLambdaShadowedImplicitParameter/receiverParent.kt");
|
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")
|
@TestMetadata("idea/testData/inspectionsLocal/nullChecksToSafeCall")
|
||||||
|
|||||||
Reference in New Issue
Block a user