Handling other types of expressions as invoke receiver

This commit is contained in:
Valentin Kipyatkov
2020-04-13 08:59:29 +03:00
parent bf79c3f279
commit cc6090347a
5 changed files with 91 additions and 48 deletions
@@ -6,16 +6,19 @@
package org.jetbrains.kotlin.idea.slicer
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.builtins.isExtensionFunctionType
import org.jetbrains.kotlin.builtins.isFunctionType
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingElement
@@ -24,6 +27,7 @@ import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import org.jetbrains.kotlin.resolve.BindingContext
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.getPsi
@@ -177,22 +181,26 @@ class OutflowSlicer(
if (function.isImplicitInvokeFunction()) {
val receiverPseudoValue = instruction.receiverValues.entries.singleOrNull()?.key
?: return@processPseudocodeUsages
when (val createdAt = receiverPseudoValue.createdAt) {
is ReadValueInstruction -> {
val accessedDescriptor = createdAt.target.accessedDescriptor ?: return@processPseudocodeUsages
if (accessedDescriptor is ValueParameterDescriptor) {
val accessedDeclaration = accessedDescriptor.originalSource.getPsi() ?: return@processPseudocodeUsages
val isExtension = accessedDescriptor.type.isExtensionFunctionType
val shift = if (isExtension) 1 else 0
val argumentIndex = parameterDescriptor.index - shift
val newMode = if (argumentIndex >= 0)
mode.withBehaviour(LambdaArgumentInflowBehaviour(argumentIndex))
else
mode.withBehaviour(LambdaReceiverInflowBehaviour)
accessedDeclaration.passToProcessor(newMode)
}
val receiverExpression = receiverPseudoValue.element as? KtExpression ?: return@processPseudocodeUsages
val bindingContext = receiverExpression.analyze(BodyResolveMode.PARTIAL)
var receiverType = bindingContext.getType(receiverExpression)
var receiver: PsiElement = receiverExpression
if (receiverType == null && receiverExpression is KtReferenceExpression) {
val targetDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, receiverExpression]
if (targetDescriptor is CallableDescriptor) {
receiverType = targetDescriptor.returnType
receiver = targetDescriptor.originalSource.getPsi() ?: return@processPseudocodeUsages
}
}
if (receiverType == null || !receiverType.isFunctionType) return@processPseudocodeUsages
val isExtension = receiverType.isExtensionFunctionType
val shift = if (isExtension) 1 else 0
val argumentIndex = parameterDescriptor.index - shift
val newMode = if (argumentIndex >= 0)
mode.withBehaviour(LambdaArgumentInflowBehaviour(argumentIndex))
else
mode.withBehaviour(LambdaReceiverInflowBehaviour)
receiver.passToProcessor(newMode)
}
}
}
@@ -2,42 +2,45 @@
4 val v1 = f(<bold>p</bold>, { this })
11 fun f(<bold>receiver: String</bold>, lambda: String.() -> String): String {
12 return lambda.invoke(<bold>receiver</bold>)
11 [LAMBDA RECEIVER IN] fun f(receiver: String, <bold>lambda: String.() -> String</bold>): String {
4 [LAMBDA RECEIVER IN] val v1 = f(p, <bold>{ this }</bold>)
4 val v1 = f(p, { <bold>this</bold> })
4 val v1 = f(p, <bold>{ this }</bold>)
4 [LAMBDA CALLS] val v1 = f(p, <bold>{ this }</bold>)
11 [LAMBDA CALLS] fun f(receiver: String, <bold>lambda: String.() -> String</bold>): String {
12 [LAMBDA CALLS] return <bold>lambda</bold>.invoke(receiver)
12 return lambda.<bold>invoke(receiver)</bold>
11 fun <bold>f(receiver: String, lambda: String.() -> String): String {</bold>
4 val v1 = <bold>f(p, { this })</bold>
4 val <bold>v1 = f(p, { this })</bold>
12 [LAMBDA RECEIVER IN] return <bold>lambda</bold>.invoke(receiver)
11 [LAMBDA RECEIVER IN] fun f(receiver: String, <bold>lambda: String.() -> String</bold>): String {
4 [LAMBDA RECEIVER IN] val v1 = f(p, <bold>{ this }</bold>)
4 val v1 = f(p, { <bold>this</bold> })
4 val v1 = f(p, <bold>{ this }</bold>)
4 [LAMBDA CALLS] val v1 = f(p, <bold>{ this }</bold>)
11 [LAMBDA CALLS] fun f(receiver: String, <bold>lambda: String.() -> String</bold>): String {
12 [LAMBDA CALLS] return <bold>lambda</bold>.invoke(receiver)
12 return lambda.<bold>invoke(receiver)</bold>
11 fun <bold>f(receiver: String, lambda: String.() -> String): String {</bold>
4 val v1 = <bold>f(p, { this })</bold>
4 val <bold>v1 = f(p, { this })</bold>
6 val v2 = g("a", "b", <bold>p</bold>, { p1, p2 -> p2 })
15 fun g(a: String, b: String, <bold>c: String</bold>, lambda: String.(String, String) -> String): String {
16 return lambda.invoke(a, b, <bold>c</bold>)
15 [LAMBDA ARGUMENT IN] fun g(a: String, b: String, c: String, <bold>lambda: String.(String, String) -> String</bold>): String {
6 [LAMBDA ARGUMENT IN] val v2 = g("a", "b", p, <bold>{ p1, p2 -> p2 }</bold>)
6 val v2 = g("a", "b", p, { p1, <bold>p2</bold> -> p2 })
6 val v2 = g("a", "b", p, { p1, p2 -> <bold>p2</bold> })
6 val v2 = g("a", "b", p, <bold>{ p1, p2 -> p2 }</bold>)
6 [LAMBDA CALLS] val v2 = g("a", "b", p, <bold>{ p1, p2 -> p2 }</bold>)
15 [LAMBDA CALLS] fun g(a: String, b: String, c: String, <bold>lambda: String.(String, String) -> String</bold>): String {
16 [LAMBDA CALLS] return <bold>lambda</bold>.invoke(a, b, c)
16 return lambda.<bold>invoke(a, b, c)</bold>
15 fun <bold>g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {</bold>
6 val v2 = <bold>g("a", "b", p, { p1, p2 -> p2 })</bold>
6 val <bold>v2 = g("a", "b", p, { p1, p2 -> p2 })</bold>
16 [LAMBDA ARGUMENT IN] return <bold>lambda</bold>.invoke(a, b, c)
15 [LAMBDA ARGUMENT IN] fun g(a: String, b: String, c: String, <bold>lambda: String.(String, String) -> String</bold>): String {
6 [LAMBDA ARGUMENT IN] val v2 = g("a", "b", p, <bold>{ p1, p2 -> p2 }</bold>)
6 val v2 = g("a", "b", p, { p1, <bold>p2</bold> -> p2 })
6 val v2 = g("a", "b", p, { p1, p2 -> <bold>p2</bold> })
6 val v2 = g("a", "b", p, <bold>{ p1, p2 -> p2 }</bold>)
6 [LAMBDA CALLS] val v2 = g("a", "b", p, <bold>{ p1, p2 -> p2 }</bold>)
15 [LAMBDA CALLS] fun g(a: String, b: String, c: String, <bold>lambda: String.(String, String) -> String</bold>): String {
16 [LAMBDA CALLS] return <bold>lambda</bold>.invoke(a, b, c)
16 return lambda.<bold>invoke(a, b, c)</bold>
15 fun <bold>g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {</bold>
6 val v2 = <bold>g("a", "b", p, { p1, p2 -> p2 })</bold>
6 val <bold>v2 = g("a", "b", p, { p1, p2 -> p2 })</bold>
8 val v3 = inlineF(<bold>p</bold>, { this })
19 (INLINE CALL inlineF) inline fun inlineF(<bold>receiver: String</bold>, lambda: String.() -> String): String {
20 (INLINE CALL inlineF) return lambda.invoke(<bold>receiver</bold>)
19 (INLINE CALL inlineF) [LAMBDA RECEIVER IN] inline fun inlineF(receiver: String, <bold>lambda: String.() -> String</bold>): String {
8 [LAMBDA RECEIVER IN] val v3 = inlineF(p, <bold>{ this }</bold>)
8 val v3 = inlineF(p, { <bold>this</bold> })
8 val v3 = inlineF(p, <bold>{ this }</bold>)
8 [LAMBDA CALLS] val v3 = inlineF(p, <bold>{ this }</bold>)
19 (INLINE CALL inlineF) [LAMBDA CALLS] inline fun inlineF(receiver: String, <bold>lambda: String.() -> String</bold>): String {
20 (INLINE CALL inlineF) [LAMBDA CALLS] return <bold>lambda</bold>.invoke(receiver)
20 (INLINE CALL inlineF) return lambda.<bold>invoke(receiver)</bold>
8 val v3 = <bold>inlineF(p, { this })</bold>
8 val <bold>v3 = inlineF(p, { this })</bold>
20 (INLINE CALL inlineF) [LAMBDA RECEIVER IN] return <bold>lambda</bold>.invoke(receiver)
19 (INLINE CALL inlineF) [LAMBDA RECEIVER IN] inline fun inlineF(receiver: String, <bold>lambda: String.() -> String</bold>): String {
8 [LAMBDA RECEIVER IN] val v3 = inlineF(p, <bold>{ this }</bold>)
8 val v3 = inlineF(p, { <bold>this</bold> })
8 val v3 = inlineF(p, <bold>{ this }</bold>)
8 [LAMBDA CALLS] val v3 = inlineF(p, <bold>{ this }</bold>)
19 (INLINE CALL inlineF) [LAMBDA CALLS] inline fun inlineF(receiver: String, <bold>lambda: String.() -> String</bold>): String {
20 (INLINE CALL inlineF) [LAMBDA CALLS] return <bold>lambda</bold>.invoke(receiver)
20 (INLINE CALL inlineF) return lambda.<bold>invoke(receiver)</bold>
8 val v3 = <bold>inlineF(p, { this })</bold>
8 val <bold>v3 = inlineF(p, { this })</bold>
+9
View File
@@ -0,0 +1,9 @@
// FLOW: OUT
fun String.foo(<caret>p: String) {
val v = bar(p, true, { this })
}
fun bar(receiver: String, b: Boolean, lambda: (String.() -> String)?): String {
return if (b) lambda!!.invoke(receiver) else ""
}
+18
View File
@@ -0,0 +1,18 @@
3 fun String.foo(<bold>p: String</bold>) {
4 val v = bar(<bold>p</bold>, true, { this })
7 fun bar(<bold>receiver: String</bold>, b: Boolean, lambda: (String.() -> String)?): String {
8 return if (b) lambda!!.invoke(<bold>receiver</bold>) else ""
8 [LAMBDA RECEIVER IN] return if (b) <bold>lambda!!</bold>.invoke(receiver) else ""
8 [LAMBDA RECEIVER IN] return if (b) <bold>lambda</bold>!!.invoke(receiver) else ""
7 [LAMBDA RECEIVER IN] fun bar(receiver: String, b: Boolean, <bold>lambda: (String.() -> String)?</bold>): String {
4 [LAMBDA RECEIVER IN] val v = bar(p, true, <bold>{ this }</bold>)
4 val v = bar(p, true, { <bold>this</bold> })
4 val v = bar(p, true, <bold>{ this }</bold>)
4 [LAMBDA CALLS] val v = bar(p, true, <bold>{ this }</bold>)
7 [LAMBDA CALLS] fun bar(receiver: String, b: Boolean, <bold>lambda: (String.() -> String)?</bold>): String {
8 [LAMBDA CALLS] return if (b) <bold>lambda</bold>!!.invoke(receiver) else ""
8 [LAMBDA CALLS] return if (b) <bold>lambda!!</bold>.invoke(receiver) else ""
8 return if (b) lambda!!.<bold>invoke(receiver)</bold> else ""
7 fun <bold>bar(receiver: String, b: Boolean, lambda: (String.() -> String)?): String {</bold>
4 val v = <bold>bar(p, true, { this })</bold>
4 val <bold>v = bar(p, true, { this })</bold>
@@ -668,6 +668,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
runTest("idea/testData/slicer/outflow/notNullAssertion.kt");
}
@TestMetadata("nullableLambda.kt")
public void testNullableLambda() throws Exception {
runTest("idea/testData/slicer/outflow/nullableLambda.kt");
}
@TestMetadata("openMethodParameter.kt")
public void testOpenMethodParameter() throws Exception {
runTest("idea/testData/slicer/outflow/openMethodParameter.kt");