RedundantLetInspection: fix false positive with nullable receiver extension call
#KT-31601 Fixed
This commit is contained in:
committed by
Dmitry Gridin
parent
a6139f3635
commit
266149b88c
@@ -9,9 +9,9 @@ import com.intellij.codeInspection.ProblemHighlightType
|
||||
import com.intellij.openapi.editor.Editor
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiElement
|
||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
||||
import org.jetbrains.kotlin.descriptors.impl.ValueParameterDescriptorImpl
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.core.replaced
|
||||
import org.jetbrains.kotlin.idea.intentions.*
|
||||
import org.jetbrains.kotlin.idea.intentions.branchedTransformations.lineCount
|
||||
@@ -23,6 +23,7 @@ import org.jetbrains.kotlin.resolve.bindingContextUtil.getReferenceTargets
|
||||
import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.calls.model.VariableAsFunctionResolvedCall
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
import org.jetbrains.kotlin.types.isNullable
|
||||
|
||||
abstract class RedundantLetInspection : AbstractApplicabilityBasedInspection<KtCallExpression>(
|
||||
KtCallExpression::class.java
|
||||
@@ -196,12 +197,20 @@ private fun KtCallExpression.isApplicable(parameterName: String): Boolean = valu
|
||||
argumentExpression.isApplicable(parameterName)
|
||||
}
|
||||
|
||||
private fun KtDotQualifiedExpression.isApplicable(parameterName: String) =
|
||||
!hasLambdaExpression() && getLeftMostReceiverExpression().let { receiver ->
|
||||
private fun KtDotQualifiedExpression.isApplicable(parameterName: String): Boolean {
|
||||
val context by lazy { analyze(BodyResolveMode.PARTIAL) }
|
||||
return !hasLambdaExpression() && getLeftMostReceiverExpression().let { receiver ->
|
||||
receiver is KtNameReferenceExpression &&
|
||||
receiver.getReferencedName() == parameterName &&
|
||||
!nameUsed(parameterName, except = receiver)
|
||||
} && callExpression?.resolveToCall() !is VariableAsFunctionResolvedCall
|
||||
} && callExpression?.getResolvedCall(context) !is VariableAsFunctionResolvedCall && !hasNullableReceiverExtensionCall(context)
|
||||
}
|
||||
|
||||
private fun KtDotQualifiedExpression.hasNullableReceiverExtensionCall(context: BindingContext): Boolean {
|
||||
val descriptor = selectorExpression?.getResolvedCall(context)?.resultingDescriptor as? CallableMemberDescriptor ?: return false
|
||||
if (descriptor.extensionReceiverParameter?.type?.isNullable() == true) return true
|
||||
return (KtPsiUtil.deparenthesize(receiverExpression) as? KtDotQualifiedExpression)?.hasNullableReceiverExtensionCall(context) == true
|
||||
}
|
||||
|
||||
private fun KtDotQualifiedExpression.hasLambdaExpression() = selectorExpression?.anyDescendantOfType<KtLambdaExpression>() ?: false
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
fun String.foo() {}
|
||||
|
||||
fun test(s: String?) {
|
||||
s?.let<caret> { it.foo() }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
// WITH_RUNTIME
|
||||
fun String.foo() {}
|
||||
|
||||
fun test(s: String?) {
|
||||
s?.foo()
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
// PROBLEM: none
|
||||
class Optional<out T>(val value: T)
|
||||
|
||||
fun Any?.foo() = println("foo: $this")
|
||||
|
||||
fun main() {
|
||||
val b: Optional<Any?>? = Optional(null)
|
||||
b?.let<caret> { it.value.foo() }
|
||||
}
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
// WITH_RUNTIME
|
||||
// PROBLEM: none
|
||||
class Optional<out T>(val value: T)
|
||||
|
||||
val Any?.foo get() = println("foo: $this")
|
||||
|
||||
fun main() {
|
||||
val b: Optional<Any?>? = Optional(null)
|
||||
b?.let<caret> { it.value.foo }
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
// WITH_RUNTIME
|
||||
// PROBLEM: none
|
||||
class A(val b: B?)
|
||||
class B
|
||||
class C(val d: D)
|
||||
class D
|
||||
|
||||
fun B?.getC(): C {
|
||||
return C(D())
|
||||
}
|
||||
|
||||
fun test(a: A?) {
|
||||
a?.let<caret> { it.b.getC().d }
|
||||
}
|
||||
+20
@@ -11879,6 +11879,26 @@ public class LocalInspectionTestGenerated extends AbstractLocalInspectionTest {
|
||||
runTest("idea/testData/inspectionsLocal/simpleRedundantLet/dotWithComparison.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionCall.kt")
|
||||
public void testExtensionCall() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/simpleRedundantLet/extensionCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionWithNullableReceiverCall.kt")
|
||||
public void testExtensionWithNullableReceiverCall() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/simpleRedundantLet/extensionWithNullableReceiverCall.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionWithNullableReceiverCall2.kt")
|
||||
public void testExtensionWithNullableReceiverCall2() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/simpleRedundantLet/extensionWithNullableReceiverCall2.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("extensionWithNullableReceiverCall3.kt")
|
||||
public void testExtensionWithNullableReceiverCall3() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/simpleRedundantLet/extensionWithNullableReceiverCall3.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("functionCall1.kt")
|
||||
public void testFunctionCall1() throws Exception {
|
||||
runTest("idea/testData/inspectionsLocal/simpleRedundantLet/functionCall1.kt");
|
||||
|
||||
Reference in New Issue
Block a user