diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt index 4caee25b044..3bbcec8cfe8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt @@ -8,6 +8,7 @@ package org.jetbrains.kotlin.idea.slicer import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access import com.intellij.psi.PsiMethod import com.intellij.usageView.UsageInfo +import org.jetbrains.kotlin.builtins.isExtensionFunctionType import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* @@ -180,13 +181,16 @@ class OutflowSlicer( when (val createdAt = receiverPseudoValue.createdAt) { is ReadValueInstruction -> { val accessedDescriptor = createdAt.target.accessedDescriptor ?: return@processPseudocodeUsages - val accessedDeclaration = accessedDescriptor.originalSource.getPsi() ?: return@processPseudocodeUsages - when (accessedDescriptor) { - is ValueParameterDescriptor -> { - //TODO: argument index is not always correct - first argument can be receiver - val newMode = mode.withBehaviour(LambdaArgumentInflowBehaviour(accessedDescriptor.index)) - accessedDeclaration.passToProcessor(newMode) - } + 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) } } } diff --git a/idea/testData/slicer/outflow/invokeExtensionLambda.kt b/idea/testData/slicer/outflow/invokeExtensionLambda.kt new file mode 100644 index 00000000000..bd403c11cd0 --- /dev/null +++ b/idea/testData/slicer/outflow/invokeExtensionLambda.kt @@ -0,0 +1,21 @@ +// FLOW: OUT + +fun String.foo(p: String) { + val v1 = f(p, { this }) + + val v2 = g("a", "b", p, { p1, p2 -> p2 }) + + val v3 = inlineF(p, { this }) +} + +fun f(receiver: String, lambda: String.() -> String): String { + return lambda.invoke(receiver) +} + +fun g(a: String, b: String, c: String, lambda: String.(String, String) -> String): String { + return lambda.invoke(a, b, c) +} + +inline fun inlineF(receiver: String, lambda: String.() -> String): String { + return lambda.invoke(receiver) +} diff --git a/idea/testData/slicer/outflow/invokeExtensionLambda.results.txt b/idea/testData/slicer/outflow/invokeExtensionLambda.results.txt new file mode 100644 index 00000000000..c7b203a038f --- /dev/null +++ b/idea/testData/slicer/outflow/invokeExtensionLambda.results.txt @@ -0,0 +1,43 @@ +3 fun String.foo(p: String) { +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 }) +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 }) +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 }) diff --git a/idea/testData/slicer/outflow/invokeLambdaSecondParam.kt b/idea/testData/slicer/outflow/invokeLambdaSecondParam.kt new file mode 100644 index 00000000000..018ee86e7a4 --- /dev/null +++ b/idea/testData/slicer/outflow/invokeLambdaSecondParam.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +fun String.foo(p: String) { + val v = f({ p1, p2 -> p2 }, p) +} + +fun f(lambda: (String, String) -> String, receiver: String): String { + return lambda("a", receiver) +} diff --git a/idea/testData/slicer/outflow/invokeLambdaSecondParam.results.txt b/idea/testData/slicer/outflow/invokeLambdaSecondParam.results.txt new file mode 100644 index 00000000000..d5be4c76ed6 --- /dev/null +++ b/idea/testData/slicer/outflow/invokeLambdaSecondParam.results.txt @@ -0,0 +1,15 @@ +3 fun String.foo(p: String) { +4 val v = f({ p1, p2 -> p2 }, p) +7 fun f(lambda: (String, String) -> String, receiver: String): String { +8 return lambda("a", receiver) +7 [LAMBDA ARGUMENT IN] fun f(lambda: (String, String) -> String, receiver: String): String { +4 [LAMBDA ARGUMENT IN] val v = f({ p1, p2 -> p2 }, p) +4 val v = f({ p1, p2 -> p2 }, p) +4 val v = f({ p1, p2 -> p2 }, p) +4 val v = f({ p1, p2 -> p2 }, p) +4 [LAMBDA CALLS] val v = f({ p1, p2 -> p2 }, p) +7 [LAMBDA CALLS] fun f(lambda: (String, String) -> String, receiver: String): String { +8 return lambda("a", receiver) +7 fun f(lambda: (String, String) -> String, receiver: String): String { +4 val v = f({ p1, p2 -> p2 }, p) +4 val v = f({ p1, p2 -> p2 }, p) diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java index 8b4acc82fc4..af21927e34f 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java @@ -598,6 +598,16 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest { runTest("idea/testData/slicer/outflow/indexingDereferences.kt"); } + @TestMetadata("invokeExtensionLambda.kt") + public void testInvokeExtensionLambda() throws Exception { + runTest("idea/testData/slicer/outflow/invokeExtensionLambda.kt"); + } + + @TestMetadata("invokeLambdaSecondParam.kt") + public void testInvokeLambdaSecondParam() throws Exception { + runTest("idea/testData/slicer/outflow/invokeLambdaSecondParam.kt"); + } + @TestMetadata("jvmFieldMemberPropertyJavaUsages.kt") public void testJvmFieldMemberPropertyJavaUsages() throws Exception { runTest("idea/testData/slicer/outflow/jvmFieldMemberPropertyJavaUsages.kt");