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");