diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt
index 0998eb2463f..eb612ca2727 100644
--- a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt
+++ b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt
@@ -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)
}
}
}
diff --git a/idea/testData/slicer/outflow/invokeExtensionLambda.results.txt b/idea/testData/slicer/outflow/invokeExtensionLambda.results.txt
index c7b203a038f..9a3a0e6f401 100644
--- a/idea/testData/slicer/outflow/invokeExtensionLambda.results.txt
+++ b/idea/testData/slicer/outflow/invokeExtensionLambda.results.txt
@@ -2,42 +2,45 @@
4 val v1 = f(p, { this })
11 fun f(receiver: String, lambda: String.() -> String): String {
12 return lambda.invoke(receiver)
-11 [LAMBDA RECEIVER IN] fun f(receiver: String, lambda: String.() -> String): String {
-4 [LAMBDA RECEIVER IN] val v1 = f(p, { this })
-4 val v1 = f(p, { this })
-4 val v1 = f(p, { this })
-4 [LAMBDA CALLS] val v1 = f(p, { this })
-11 [LAMBDA CALLS] fun f(receiver: String, lambda: String.() -> String): String {
-12 [LAMBDA CALLS] return lambda.invoke(receiver)
-12 return lambda.invoke(receiver)
-11 fun f(receiver: String, lambda: String.() -> String): String {
-4 val v1 = f(p, { this })
-4 val v1 = f(p, { this })
+12 [LAMBDA RECEIVER IN] return lambda.invoke(receiver)
+11 [LAMBDA RECEIVER IN] fun f(receiver: String, lambda: String.() -> String): String {
+4 [LAMBDA RECEIVER IN] val v1 = f(p, { this })
+4 val v1 = f(p, { this })
+4 val v1 = f(p, { this })
+4 [LAMBDA CALLS] val v1 = f(p, { this })
+11 [LAMBDA CALLS] fun f(receiver: String, lambda: String.() -> String): String {
+12 [LAMBDA CALLS] return lambda.invoke(receiver)
+12 return lambda.invoke(receiver)
+11 fun f(receiver: String, lambda: String.() -> String): String {
+4 val v1 = f(p, { this })
+4 val v1 = f(p, { this })
6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
15 fun g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {
16 return lambda.invoke(a, b, c)
-15 [LAMBDA ARGUMENT IN] fun g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {
-6 [LAMBDA ARGUMENT IN] val v2 = g("a", "b", p, { p1, p2 -> p2 })
-6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
-6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
-6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
-6 [LAMBDA CALLS] val v2 = g("a", "b", p, { p1, p2 -> p2 })
-15 [LAMBDA CALLS] fun g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {
-16 [LAMBDA CALLS] return lambda.invoke(a, b, c)
-16 return lambda.invoke(a, b, c)
-15 fun g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {
-6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
-6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
+16 [LAMBDA ARGUMENT IN] return lambda.invoke(a, b, c)
+15 [LAMBDA ARGUMENT IN] fun g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {
+6 [LAMBDA ARGUMENT IN] val v2 = g("a", "b", p, { p1, p2 -> p2 })
+6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
+6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
+6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
+6 [LAMBDA CALLS] val v2 = g("a", "b", p, { p1, p2 -> p2 })
+15 [LAMBDA CALLS] fun g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {
+16 [LAMBDA CALLS] return lambda.invoke(a, b, c)
+16 return lambda.invoke(a, b, c)
+15 fun g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String {
+6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
+6 val v2 = g("a", "b", p, { p1, p2 -> p2 })
8 val v3 = inlineF(p, { this })
19 (INLINE CALL inlineF) inline fun inlineF(receiver: String, lambda: String.() -> String): String {
20 (INLINE CALL inlineF) return lambda.invoke(receiver)
-19 (INLINE CALL inlineF) [LAMBDA RECEIVER IN] inline fun inlineF(receiver: String, lambda: String.() -> String): String {
-8 [LAMBDA RECEIVER IN] val v3 = inlineF(p, { this })
-8 val v3 = inlineF(p, { this })
-8 val v3 = inlineF(p, { this })
-8 [LAMBDA CALLS] val v3 = inlineF(p, { this })
-19 (INLINE CALL inlineF) [LAMBDA CALLS] inline fun inlineF(receiver: String, lambda: String.() -> String): String {
-20 (INLINE CALL inlineF) [LAMBDA CALLS] return lambda.invoke(receiver)
-20 (INLINE CALL inlineF) return lambda.invoke(receiver)
-8 val v3 = inlineF(p, { this })
-8 val v3 = inlineF(p, { this })
+20 (INLINE CALL inlineF) [LAMBDA RECEIVER IN] return lambda.invoke(receiver)
+19 (INLINE CALL inlineF) [LAMBDA RECEIVER IN] inline fun inlineF(receiver: String, lambda: String.() -> String): String {
+8 [LAMBDA RECEIVER IN] val v3 = inlineF(p, { this })
+8 val v3 = inlineF(p, { this })
+8 val v3 = inlineF(p, { this })
+8 [LAMBDA CALLS] val v3 = inlineF(p, { this })
+19 (INLINE CALL inlineF) [LAMBDA CALLS] inline fun inlineF(receiver: String, lambda: String.() -> String): String {
+20 (INLINE CALL inlineF) [LAMBDA CALLS] return lambda.invoke(receiver)
+20 (INLINE CALL inlineF) return lambda.invoke(receiver)
+8 val v3 = inlineF(p, { this })
+8 val v3 = inlineF(p, { this })
diff --git a/idea/testData/slicer/outflow/nullableLambda.kt b/idea/testData/slicer/outflow/nullableLambda.kt
new file mode 100644
index 00000000000..b113c897d97
--- /dev/null
+++ b/idea/testData/slicer/outflow/nullableLambda.kt
@@ -0,0 +1,9 @@
+// FLOW: OUT
+
+fun String.foo(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 ""
+}
diff --git a/idea/testData/slicer/outflow/nullableLambda.results.txt b/idea/testData/slicer/outflow/nullableLambda.results.txt
new file mode 100644
index 00000000000..db685ff9802
--- /dev/null
+++ b/idea/testData/slicer/outflow/nullableLambda.results.txt
@@ -0,0 +1,18 @@
+3 fun String.foo(p: String) {
+4 val v = bar(p, true, { this })
+7 fun bar(receiver: String, b: Boolean, lambda: (String.() -> String)?): String {
+8 return if (b) lambda!!.invoke(receiver) else ""
+8 [LAMBDA RECEIVER IN] return if (b) lambda!!.invoke(receiver) else ""
+8 [LAMBDA RECEIVER IN] return if (b) lambda!!.invoke(receiver) else ""
+7 [LAMBDA RECEIVER IN] fun bar(receiver: String, b: Boolean, lambda: (String.() -> String)?): String {
+4 [LAMBDA RECEIVER IN] val v = bar(p, true, { this })
+4 val v = bar(p, true, { this })
+4 val v = bar(p, true, { this })
+4 [LAMBDA CALLS] val v = bar(p, true, { this })
+7 [LAMBDA CALLS] fun bar(receiver: String, b: Boolean, lambda: (String.() -> String)?): String {
+8 [LAMBDA CALLS] return if (b) lambda!!.invoke(receiver) else ""
+8 [LAMBDA CALLS] return if (b) lambda!!.invoke(receiver) else ""
+8 return if (b) lambda!!.invoke(receiver) else ""
+7 fun bar(receiver: String, b: Boolean, lambda: (String.() -> String)?): String {
+4 val v = bar(p, true, { this })
+4 val v = bar(p, true, { this })
diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java
index af21927e34f..851d1b4fe3e 100644
--- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java
+++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java
@@ -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");