From 3f411fc93beab4c44a0c4714682675b6b3e61ca2 Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 31 May 2017 21:31:23 +0300 Subject: [PATCH] Analyze Data Flow: Support lambdas/anonymous functions #KT-11994 In Progress --- .../slicer/KotlinSliceDereferenceUsage.kt | 5 +- .../kotlin/idea/slicer/KotlinSliceUsage.kt | 16 ++- .../slicer/KotlinSliceUsageCellRenderer.kt | 2 + .../jetbrains/kotlin/idea/slicer/Slicer.kt | 94 +++++++++++++--- .../inflow/anonymousFunBodyExpression.kt | 9 ++ .../anonymousFunBodyExpression.results.txt | 10 ++ .../inflow/anonymousFunReturnExpression.kt | 9 ++ .../anonymousFunReturnExpression.results.txt | 10 ++ .../slicer/inflow/doubleLambdaResult.kt | 9 ++ .../inflow/doubleLambdaResult.results.txt | 12 +++ idea/testData/slicer/inflow/lambdaResult.kt | 9 ++ .../slicer/inflow/lambdaResult.results.txt | 9 ++ .../inflow/lambdaResultWithAssignments.kt | 10 ++ .../lambdaResultWithAssignments.results.txt | 11 ++ .../inflow/lambdaResultWithDirectCall.kt | 5 + .../lambdaResultWithDirectCall.results.txt | 5 + ...lambdaResultWithDirectCallViaAssignment.kt | 6 ++ ...ultWithDirectCallViaAssignment.results.txt | 7 ++ idea/testData/slicer/inflow/nonLocalReturn.kt | 5 + .../slicer/inflow/nonLocalReturn.results.txt | 11 ++ .../outflow/anonymousFunBodyExpression.kt | 9 ++ .../anonymousFunBodyExpression.results.txt | 8 ++ .../outflow/anonymousFunReturnExpression.kt | 9 ++ .../anonymousFunReturnExpression.results.txt | 8 ++ .../slicer/outflow/doubleLambdaResult.kt | 9 ++ .../outflow/doubleLambdaResult.results.txt | 11 ++ .../outflow/explicitLambdaReturnExpression.kt | 12 +++ ...explicitLambdaReturnExpression.results.txt | 8 ++ idea/testData/slicer/outflow/lambdaResult.kt | 9 ++ .../slicer/outflow/lambdaResult.results.txt | 8 ++ .../outflow/lambdaResultWithAssignments.kt | 10 ++ .../lambdaResultWithAssignments.results.txt | 9 ++ .../outflow/lambdaResultWithDirectCall.kt | 5 + .../lambdaResultWithDirectCall.results.txt | 5 + ...lambdaResultWithDirectCallViaAssignment.kt | 6 ++ ...ultWithDirectCallViaAssignment.results.txt | 6 ++ .../testData/slicer/outflow/nonLocalReturn.kt | 9 ++ .../slicer/outflow/nonLocalReturn.results.txt | 4 + .../kotlin/idea/slicer/AbstractSlicerTest.kt | 22 ++-- .../idea/slicer/SlicerTestGenerated.java | 102 ++++++++++++++++++ 40 files changed, 494 insertions(+), 29 deletions(-) create mode 100644 idea/testData/slicer/inflow/anonymousFunBodyExpression.kt create mode 100644 idea/testData/slicer/inflow/anonymousFunBodyExpression.results.txt create mode 100644 idea/testData/slicer/inflow/anonymousFunReturnExpression.kt create mode 100644 idea/testData/slicer/inflow/anonymousFunReturnExpression.results.txt create mode 100644 idea/testData/slicer/inflow/doubleLambdaResult.kt create mode 100644 idea/testData/slicer/inflow/doubleLambdaResult.results.txt create mode 100644 idea/testData/slicer/inflow/lambdaResult.kt create mode 100644 idea/testData/slicer/inflow/lambdaResult.results.txt create mode 100644 idea/testData/slicer/inflow/lambdaResultWithAssignments.kt create mode 100644 idea/testData/slicer/inflow/lambdaResultWithAssignments.results.txt create mode 100644 idea/testData/slicer/inflow/lambdaResultWithDirectCall.kt create mode 100644 idea/testData/slicer/inflow/lambdaResultWithDirectCall.results.txt create mode 100644 idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.kt create mode 100644 idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.results.txt create mode 100644 idea/testData/slicer/inflow/nonLocalReturn.kt create mode 100644 idea/testData/slicer/inflow/nonLocalReturn.results.txt create mode 100644 idea/testData/slicer/outflow/anonymousFunBodyExpression.kt create mode 100644 idea/testData/slicer/outflow/anonymousFunBodyExpression.results.txt create mode 100644 idea/testData/slicer/outflow/anonymousFunReturnExpression.kt create mode 100644 idea/testData/slicer/outflow/anonymousFunReturnExpression.results.txt create mode 100644 idea/testData/slicer/outflow/doubleLambdaResult.kt create mode 100644 idea/testData/slicer/outflow/doubleLambdaResult.results.txt create mode 100644 idea/testData/slicer/outflow/explicitLambdaReturnExpression.kt create mode 100644 idea/testData/slicer/outflow/explicitLambdaReturnExpression.results.txt create mode 100644 idea/testData/slicer/outflow/lambdaResult.kt create mode 100644 idea/testData/slicer/outflow/lambdaResult.results.txt create mode 100644 idea/testData/slicer/outflow/lambdaResultWithAssignments.kt create mode 100644 idea/testData/slicer/outflow/lambdaResultWithAssignments.results.txt create mode 100644 idea/testData/slicer/outflow/lambdaResultWithDirectCall.kt create mode 100644 idea/testData/slicer/outflow/lambdaResultWithDirectCall.results.txt create mode 100644 idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt create mode 100644 idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.results.txt create mode 100644 idea/testData/slicer/outflow/nonLocalReturn.kt create mode 100644 idea/testData/slicer/outflow/nonLocalReturn.results.txt diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt index d8390b69b1f..d85372c8b0c 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt @@ -23,8 +23,9 @@ import com.intellij.util.Processor class KotlinSliceDereferenceUsage( element: PsiElement, - parent: KotlinSliceUsage -) : KotlinSliceUsage(element, parent) { + parent: KotlinSliceUsage, + lambdaLevel: Int +) : KotlinSliceUsage(element, parent, lambdaLevel, false) { override fun processChildren(processor: Processor) { // no children } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt index 9146dc93f68..5dc7fd131c5 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt @@ -23,13 +23,23 @@ import com.intellij.util.Processor import org.jetbrains.kotlin.psi.KtExpression open class KotlinSliceUsage : SliceUsage { - constructor(element: PsiElement, parent: SliceUsage) : super(element, parent) - constructor(element: PsiElement, params: SliceAnalysisParams) : super(element, params) + val lambdaLevel: Int + val forcedExpressionMode: Boolean + + constructor(element: PsiElement, parent: SliceUsage, lambdaLevel: Int, forcedExpressionMode: Boolean) : super(element, parent) { + this.lambdaLevel = lambdaLevel + this.forcedExpressionMode = forcedExpressionMode + } + + constructor(element: PsiElement, params: SliceAnalysisParams) : super(element, params) { + this.lambdaLevel = 0 + this.forcedExpressionMode = false + } override fun copy(): KotlinSliceUsage { val element = usageInfo.element!! if (parent == null) return KotlinSliceUsage(element, params) - return KotlinSliceUsage(element, parent) + return KotlinSliceUsage(element, parent, lambdaLevel, forcedExpressionMode) } public override fun processUsagesFlownDownTo(element: PsiElement, uniqueProcessor: Processor) { diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt index 878ae90b268..bb60777c555 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt @@ -53,6 +53,8 @@ object KotlinSliceUsageCellRenderer : SliceUsageCellRendererBase() { } } + append(" (Tracking enclosing lambda)".repeat(sliceUsage.lambdaLevel), SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES) + val declaration = sliceUsage.element?.parents?.firstOrNull { it is KtClass || it is KtObjectDeclaration && !it.isObjectLiteral() || diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt index ae190319a96..265836d983f 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -16,13 +16,13 @@ package org.jetbrains.kotlin.idea.slicer -import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access import com.intellij.psi.PsiElement import com.intellij.psi.search.SearchScope import com.intellij.slicer.SliceUsage import com.intellij.usageView.UsageInfo import com.intellij.util.Processor +import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode @@ -46,6 +46,7 @@ import org.jetbrains.kotlin.psi.psiUtil.* import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument +import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.source.getPsi import java.util.* @@ -111,8 +112,11 @@ abstract class Slicer( protected val pseudocodeCache = PseudocodeCache() - protected fun PsiElement.passToProcessor() { - processor.process(KotlinSliceUsage(this, parentUsage)) + protected fun PsiElement.passToProcessor( + lambdaLevel: Int = parentUsage.lambdaLevel, + forcedExpressionMode: Boolean = false + ) { + processor.process(KotlinSliceUsage(this, parentUsage, lambdaLevel, forcedExpressionMode)) } abstract fun processChildren() @@ -123,14 +127,16 @@ class InflowSlicer( processor: Processor, parentUsage: KotlinSliceUsage ) : Slicer(element, processor, parentUsage) { + private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true) + private fun KtProperty.processProperty() { if (!canProcess()) return - initializer?.passToProcessor() + initializer?.passToProcessorAsValue() processVariableAccesses(parentUsage.scope.toSearchScope(), Access.Write) body@ { val refExpression = it.element as? KtExpression ?: return@body val rhs = KtPsiUtil.safeDeparenthesize(refExpression).getAssignmentByLHS()?.right ?: return@body - rhs.passToProcessor() + rhs.passToProcessorAsValue() } } @@ -152,7 +158,7 @@ class InflowSlicer( is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression() else -> null } ?: return@body - flownExpression.passToProcessor() + flownExpression.passToProcessorAsValue() } } @@ -160,21 +166,46 @@ class InflowSlicer( val pseudocode = pseudocodeCache[bodyExpression ?: return] ?: return pseudocode.traverse(TraversalOrder.FORWARD) { instr -> if (instr is ReturnValueInstruction && instr.subroutine == this) { - (instr.returnExpressionIfAny?.returnedExpression ?: instr.element as? KtExpression)?.passToProcessor() + (instr.returnExpressionIfAny?.returnedExpression ?: instr.element as? KtExpression)?.passToProcessorAsValue() } } } private fun Instruction.passInputsToProcessor() { - inputValues.forEach { it.element?.passToProcessor() } + inputValues.forEach { + if (it.createdAt != null) { + it.element?.passToProcessorAsValue() + } + } } private fun KtExpression.processExpression() { + val lambda = when (this) { + is KtLambdaExpression -> functionLiteral + is KtNamedFunction -> if (name == null) this else null + else -> null + } + if (lambda != null) { + if (parentUsage.lambdaLevel > 0) { + lambda.passToProcessor(parentUsage.lambdaLevel - 1) + } + return + } + val pseudocode = pseudocodeCache[this] ?: return val expressionValue = pseudocode.getElementValue(this) ?: return val createdAt = expressionValue.createdAt when (createdAt) { - is ReadValueInstruction -> (createdAt.target.accessedDescriptor?.source?.getPsi() as? KtDeclaration)?.passToProcessor() + is ReadValueInstruction -> { + if (createdAt.target == AccessTarget.BlackBox) { + val originalElement = expressionValue.element as? KtExpression ?: return + if (originalElement != this) { + originalElement.processExpression() + } + return + } + (createdAt.target.accessedDescriptor?.source?.getPsi() as? KtDeclaration)?.passToProcessor() + } is MergeInstruction -> createdAt.passInputsToProcessor() @@ -183,11 +214,22 @@ class InflowSlicer( else -> return } - is CallInstruction -> (createdAt.resolvedCall.resultingDescriptor.source.getPsi() as? KtDeclarationWithBody)?.passToProcessor() + is CallInstruction -> { + val resolvedCall = createdAt.resolvedCall + val resultingDescriptor = resolvedCall.resultingDescriptor + if (resultingDescriptor is FunctionInvokeDescriptor) { + (resolvedCall.dispatchReceiver as? ExpressionReceiver)?.expression?.passToProcessorAsValue(parentUsage.lambdaLevel + 1) + } + else { + resultingDescriptor.source.getPsi()?.passToProcessor() + } + } } } override fun processChildren() { + if (parentUsage.forcedExpressionMode) return element.processExpression() + when (element) { is KtProperty -> element.processProperty() is KtParameter -> element.processParameter() @@ -235,10 +277,19 @@ class OutflowSlicer( return null } - private fun KtFunction.processFunctionCalls() { - processCalls(parentUsage.scope.toSearchScope()) { - it.element?.getCallElementForExactCallee()?.passToProcessor() + private fun KtFunction.processFunction() { + if (this is KtConstructor<*> || this is KtNamedFunction && name != null) { + processCalls(parentUsage.scope.toSearchScope()) { + it.element?.getCallElementForExactCallee()?.passToProcessor() + } + return } + val funExpression = when (this) { + is KtFunctionLiteral -> parent as? KtLambdaExpression + is KtNamedFunction -> this + else -> null + } ?: return + (funExpression as PsiElement).passToProcessor(parentUsage.lambdaLevel + 1, true) } private fun processDereferenceIsNeeded( @@ -256,7 +307,7 @@ class OutflowSlicer( } ?: return if (receiver != null && resolvedCall.dispatchReceiver == receiver) { - processor.process(KotlinSliceDereferenceUsage(expression, parentUsage)) + processor.process(KotlinSliceDereferenceUsage(expression, parentUsage, parentUsage.lambdaLevel)) } } @@ -279,8 +330,15 @@ class OutflowSlicer( processPseudocodeUsages { pseudoValue, instr -> when (instr) { is WriteValueInstruction -> (instr.target.accessedDescriptor?.source?.getPsi() as? KtDeclaration)?.passToProcessor() - is CallInstruction -> instr.arguments[pseudoValue]?.source?.getPsi()?.passToProcessor() - is ReturnValueInstruction -> (instr.owner.correspondingElement as? KtFunction)?.passToProcessor() + is CallInstruction -> { + if (parentUsage.lambdaLevel > 0 && instr.receiverValues[pseudoValue] != null) { + instr.element.passToProcessor(parentUsage.lambdaLevel - 1) + } + else { + instr.arguments[pseudoValue]?.source?.getPsi()?.passToProcessor() + } + } + is ReturnValueInstruction -> instr.subroutine.passToProcessor() is MagicInstruction -> when (instr.kind) { MagicKind.NOT_NULL_ASSERTION, MagicKind.CAST -> instr.outputValue.element?.passToProcessor() else -> {} @@ -290,9 +348,11 @@ class OutflowSlicer( } override fun processChildren() { + if (parentUsage.forcedExpressionMode) return element.processExpression() + when (element) { is KtProperty, is KtParameter -> (element as KtDeclaration).processVariable() - is KtFunction -> element.processFunctionCalls() + is KtFunction -> element.processFunction() else -> element.processExpression() } } diff --git a/idea/testData/slicer/inflow/anonymousFunBodyExpression.kt b/idea/testData/slicer/inflow/anonymousFunBodyExpression.kt new file mode 100644 index 00000000000..da0d2c70c3a --- /dev/null +++ b/idea/testData/slicer/inflow/anonymousFunBodyExpression.kt @@ -0,0 +1,9 @@ +// FLOW: IN + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + val x = foo(fun(n: Int) = n) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/anonymousFunBodyExpression.results.txt b/idea/testData/slicer/inflow/anonymousFunBodyExpression.results.txt new file mode 100644 index 00000000000..63de5c80af4 --- /dev/null +++ b/idea/testData/slicer/inflow/anonymousFunBodyExpression.results.txt @@ -0,0 +1,10 @@ +8 val x = foo(fun(n: Int) = n) +8 val x = foo(fun(n: Int) = n) +3 fun foo(f: (Int) -> Int): Int { +4 return f(1) +4 [LAMBDA] return f(1) +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +8 [LAMBDA] val x = foo(fun(n: Int) = n) +8 val x = foo(fun(n: Int) = n) +8 val x = foo(fun(n: Int) = n) +8 val x = foo(fun(n: Int) = n) diff --git a/idea/testData/slicer/inflow/anonymousFunReturnExpression.kt b/idea/testData/slicer/inflow/anonymousFunReturnExpression.kt new file mode 100644 index 00000000000..9625e4b281d --- /dev/null +++ b/idea/testData/slicer/inflow/anonymousFunReturnExpression.kt @@ -0,0 +1,9 @@ +// FLOW: IN + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + val x = foo(fun(n: Int): Int { return n }) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/anonymousFunReturnExpression.results.txt b/idea/testData/slicer/inflow/anonymousFunReturnExpression.results.txt new file mode 100644 index 00000000000..f0685842c65 --- /dev/null +++ b/idea/testData/slicer/inflow/anonymousFunReturnExpression.results.txt @@ -0,0 +1,10 @@ +8 val x = foo(fun(n: Int): Int { return n }) +8 val x = foo(fun(n: Int): Int { return n }) +3 fun foo(f: (Int) -> Int): Int { +4 return f(1) +4 [LAMBDA] return f(1) +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +8 [LAMBDA] val x = foo(fun(n: Int): Int { return n }) +8 val x = foo(fun(n: Int): Int { return n }) +8 val x = foo(fun(n: Int): Int { return n }) +8 val x = foo(fun(n: Int): Int { return n }) diff --git a/idea/testData/slicer/inflow/doubleLambdaResult.kt b/idea/testData/slicer/inflow/doubleLambdaResult.kt new file mode 100644 index 00000000000..6e69c01f221 --- /dev/null +++ b/idea/testData/slicer/inflow/doubleLambdaResult.kt @@ -0,0 +1,9 @@ +// FLOW: IN + +fun foo(a: Int, b: Int, f: (Int) -> (Int) -> Int): Int { + return f(a)(b) +} + +fun test() { + val x = foo(1, 2) { { it } } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/doubleLambdaResult.results.txt b/idea/testData/slicer/inflow/doubleLambdaResult.results.txt new file mode 100644 index 00000000000..fe9b9ba6d71 --- /dev/null +++ b/idea/testData/slicer/inflow/doubleLambdaResult.results.txt @@ -0,0 +1,12 @@ +8 val x = foo(1, 2) { { it } } +8 val x = foo(1, 2) { { it } } +3 fun foo(a: Int, b: Int, f: (Int) -> (Int) -> Int): Int { +4 return f(a)(b) +4 [LAMBDA] return f(a)(b) +4 [LAMBDA] [LAMBDA] return f(a)(b) +3 [LAMBDA] [LAMBDA] fun foo(a: Int, b: Int, f: (Int) -> (Int) -> Int): Int { +8 [LAMBDA] [LAMBDA] val x = foo(1, 2) { { it } } +8 [LAMBDA] val x = foo(1, 2) { { it } } +8 [LAMBDA] val x = foo(1, 2) { { it } } +8 val x = foo(1, 2) { { it } } +8 val x = foo(1, 2) { { it } } diff --git a/idea/testData/slicer/inflow/lambdaResult.kt b/idea/testData/slicer/inflow/lambdaResult.kt new file mode 100644 index 00000000000..bb89c72b27c --- /dev/null +++ b/idea/testData/slicer/inflow/lambdaResult.kt @@ -0,0 +1,9 @@ +// FLOW: IN + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + val x = foo { it } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/lambdaResult.results.txt b/idea/testData/slicer/inflow/lambdaResult.results.txt new file mode 100644 index 00000000000..9799ce293d7 --- /dev/null +++ b/idea/testData/slicer/inflow/lambdaResult.results.txt @@ -0,0 +1,9 @@ +8 val x = foo { it } +8 val x = foo { it } +3 fun foo(f: (Int) -> Int): Int { +4 return f(1) +4 [LAMBDA] return f(1) +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +8 [LAMBDA] val x = foo { it } +8 val x = foo { it } +8 val x = foo { it } diff --git a/idea/testData/slicer/inflow/lambdaResultWithAssignments.kt b/idea/testData/slicer/inflow/lambdaResultWithAssignments.kt new file mode 100644 index 00000000000..bbbd820a104 --- /dev/null +++ b/idea/testData/slicer/inflow/lambdaResultWithAssignments.kt @@ -0,0 +1,10 @@ +// FLOW: IN + +fun foo(f: (Int) -> Int): Int { + val x = f + return x(1) +} + +fun test() { + val y = foo { it } +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/lambdaResultWithAssignments.results.txt b/idea/testData/slicer/inflow/lambdaResultWithAssignments.results.txt new file mode 100644 index 00000000000..8b7bc172dae --- /dev/null +++ b/idea/testData/slicer/inflow/lambdaResultWithAssignments.results.txt @@ -0,0 +1,11 @@ +9 val y = foo { it } +9 val y = foo { it } +3 fun foo(f: (Int) -> Int): Int { +5 return x(1) +5 [LAMBDA] return x(1) +4 [LAMBDA] val x = f +4 [LAMBDA] val x = f +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +9 [LAMBDA] val y = foo { it } +9 val y = foo { it } +9 val y = foo { it } diff --git a/idea/testData/slicer/inflow/lambdaResultWithDirectCall.kt b/idea/testData/slicer/inflow/lambdaResultWithDirectCall.kt new file mode 100644 index 00000000000..721afca5e26 --- /dev/null +++ b/idea/testData/slicer/inflow/lambdaResultWithDirectCall.kt @@ -0,0 +1,5 @@ +// FLOW: IN + +fun test() { + val x = { 1 }() +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/lambdaResultWithDirectCall.results.txt b/idea/testData/slicer/inflow/lambdaResultWithDirectCall.results.txt new file mode 100644 index 00000000000..bcd8bf85b7c --- /dev/null +++ b/idea/testData/slicer/inflow/lambdaResultWithDirectCall.results.txt @@ -0,0 +1,5 @@ +4 val x = { 1 }() +4 val x = { 1 }() +4 [LAMBDA] val x = { 1 }() +4 val x = { 1 }() +4 val x = { 1 }() diff --git a/idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.kt b/idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.kt new file mode 100644 index 00000000000..9037bc9f3e3 --- /dev/null +++ b/idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.kt @@ -0,0 +1,6 @@ +// FLOW: IN + +fun test() { + val f = { 1 } + val x = f() +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.results.txt b/idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.results.txt new file mode 100644 index 00000000000..459bfaf147c --- /dev/null +++ b/idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.results.txt @@ -0,0 +1,7 @@ +5 val x = f() +5 val x = f() +5 [LAMBDA] val x = f() +4 [LAMBDA] val f = { 1 } +4 [LAMBDA] val f = { 1 } +4 val f = { 1 } +4 val f = { 1 } diff --git a/idea/testData/slicer/inflow/nonLocalReturn.kt b/idea/testData/slicer/inflow/nonLocalReturn.kt new file mode 100644 index 00000000000..c0b06949edf --- /dev/null +++ b/idea/testData/slicer/inflow/nonLocalReturn.kt @@ -0,0 +1,5 @@ +// FLOW: IN + +inline fun foo(a: Int, f: (Int) -> Int) = f(a) + +fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } \ No newline at end of file diff --git a/idea/testData/slicer/inflow/nonLocalReturn.results.txt b/idea/testData/slicer/inflow/nonLocalReturn.results.txt new file mode 100644 index 00000000000..2eed9483cc8 --- /dev/null +++ b/idea/testData/slicer/inflow/nonLocalReturn.results.txt @@ -0,0 +1,11 @@ +5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } +5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } +5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } +3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 [LAMBDA] inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 [LAMBDA] inline fun foo(a: Int, f: (Int) -> Int) = f(a) +5 [LAMBDA] fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } +5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } +5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } +5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } diff --git a/idea/testData/slicer/outflow/anonymousFunBodyExpression.kt b/idea/testData/slicer/outflow/anonymousFunBodyExpression.kt new file mode 100644 index 00000000000..2ab659f9617 --- /dev/null +++ b/idea/testData/slicer/outflow/anonymousFunBodyExpression.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + val x = foo(fun(n: Int) = n) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/anonymousFunBodyExpression.results.txt b/idea/testData/slicer/outflow/anonymousFunBodyExpression.results.txt new file mode 100644 index 00000000000..0754ab6f7b1 --- /dev/null +++ b/idea/testData/slicer/outflow/anonymousFunBodyExpression.results.txt @@ -0,0 +1,8 @@ +8 val x = foo(fun(n: Int) = n) +8 val x = foo(fun(n: Int) = n) +8 [LAMBDA] val x = foo(fun(n: Int) = n) +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +4 return f(1) +3 fun foo(f: (Int) -> Int): Int { +8 val x = foo(fun(n: Int) = n) +8 val x = foo(fun(n: Int) = n) diff --git a/idea/testData/slicer/outflow/anonymousFunReturnExpression.kt b/idea/testData/slicer/outflow/anonymousFunReturnExpression.kt new file mode 100644 index 00000000000..8931ad94d97 --- /dev/null +++ b/idea/testData/slicer/outflow/anonymousFunReturnExpression.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + val x = foo(fun(n: Int): Int { return n }) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/anonymousFunReturnExpression.results.txt b/idea/testData/slicer/outflow/anonymousFunReturnExpression.results.txt new file mode 100644 index 00000000000..b3c0b1697ca --- /dev/null +++ b/idea/testData/slicer/outflow/anonymousFunReturnExpression.results.txt @@ -0,0 +1,8 @@ +8 val x = foo(fun(n: Int): Int { return n }) +8 val x = foo(fun(n: Int): Int { return n }) +8 [LAMBDA] val x = foo(fun(n: Int): Int { return n }) +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +4 return f(1) +3 fun foo(f: (Int) -> Int): Int { +8 val x = foo(fun(n: Int): Int { return n }) +8 val x = foo(fun(n: Int): Int { return n }) diff --git a/idea/testData/slicer/outflow/doubleLambdaResult.kt b/idea/testData/slicer/outflow/doubleLambdaResult.kt new file mode 100644 index 00000000000..13fee1bcc9d --- /dev/null +++ b/idea/testData/slicer/outflow/doubleLambdaResult.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +fun foo(a: Int, b: Int, f: (Int) -> (Int) -> Int): Int { + return f(a)(b) +} + +fun test() { + val x = foo(1, 2) { { it } } +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/doubleLambdaResult.results.txt b/idea/testData/slicer/outflow/doubleLambdaResult.results.txt new file mode 100644 index 00000000000..2783101725f --- /dev/null +++ b/idea/testData/slicer/outflow/doubleLambdaResult.results.txt @@ -0,0 +1,11 @@ +8 val x = foo(1, 2) { { it } } +8 val x = foo(1, 2) { { it } } +8 [LAMBDA] val x = foo(1, 2) { { it } } +8 [LAMBDA] val x = foo(1, 2) { { it } } +8 [LAMBDA] [LAMBDA] val x = foo(1, 2) { { it } } +3 [LAMBDA] [LAMBDA] fun foo(a: Int, b: Int, f: (Int) -> (Int) -> Int): Int { +4 [LAMBDA] return f(a)(b) +4 return f(a)(b) +3 fun foo(a: Int, b: Int, f: (Int) -> (Int) -> Int): Int { +8 val x = foo(1, 2) { { it } } +8 val x = foo(1, 2) { { it } } diff --git a/idea/testData/slicer/outflow/explicitLambdaReturnExpression.kt b/idea/testData/slicer/outflow/explicitLambdaReturnExpression.kt new file mode 100644 index 00000000000..3745677057d --- /dev/null +++ b/idea/testData/slicer/outflow/explicitLambdaReturnExpression.kt @@ -0,0 +1,12 @@ +// FLOW: OUT + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + val x = foo { + if (it < 0) return@foo 0 + it + } +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/explicitLambdaReturnExpression.results.txt b/idea/testData/slicer/outflow/explicitLambdaReturnExpression.results.txt new file mode 100644 index 00000000000..76aac42dbd6 --- /dev/null +++ b/idea/testData/slicer/outflow/explicitLambdaReturnExpression.results.txt @@ -0,0 +1,8 @@ +9 if (it < 0) return@foo 0 +8 val x = foo { +8 [LAMBDA] val x = foo { +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +4 return f(1) +3 fun foo(f: (Int) -> Int): Int { +8 val x = foo { +8 val x = foo { diff --git a/idea/testData/slicer/outflow/lambdaResult.kt b/idea/testData/slicer/outflow/lambdaResult.kt new file mode 100644 index 00000000000..315580c9683 --- /dev/null +++ b/idea/testData/slicer/outflow/lambdaResult.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + val x = foo { it } +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/lambdaResult.results.txt b/idea/testData/slicer/outflow/lambdaResult.results.txt new file mode 100644 index 00000000000..5ec5ccae879 --- /dev/null +++ b/idea/testData/slicer/outflow/lambdaResult.results.txt @@ -0,0 +1,8 @@ +8 val x = foo { it } +8 val x = foo { it } +8 [LAMBDA] val x = foo { it } +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +4 return f(1) +3 fun foo(f: (Int) -> Int): Int { +8 val x = foo { it } +8 val x = foo { it } diff --git a/idea/testData/slicer/outflow/lambdaResultWithAssignments.kt b/idea/testData/slicer/outflow/lambdaResultWithAssignments.kt new file mode 100644 index 00000000000..661e6da2d57 --- /dev/null +++ b/idea/testData/slicer/outflow/lambdaResultWithAssignments.kt @@ -0,0 +1,10 @@ +// FLOW: OUT + +fun foo(f: (Int) -> Int): Int { + val x = f + return x(1) +} + +fun test() { + val y = foo { it } +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/lambdaResultWithAssignments.results.txt b/idea/testData/slicer/outflow/lambdaResultWithAssignments.results.txt new file mode 100644 index 00000000000..dda6b1052d8 --- /dev/null +++ b/idea/testData/slicer/outflow/lambdaResultWithAssignments.results.txt @@ -0,0 +1,9 @@ +9 val y = foo { it } +9 val y = foo { it } +9 [LAMBDA] val y = foo { it } +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +4 [LAMBDA] val x = f +5 return x(1) +3 fun foo(f: (Int) -> Int): Int { +9 val y = foo { it } +9 val y = foo { it } diff --git a/idea/testData/slicer/outflow/lambdaResultWithDirectCall.kt b/idea/testData/slicer/outflow/lambdaResultWithDirectCall.kt new file mode 100644 index 00000000000..c4f277d5ca8 --- /dev/null +++ b/idea/testData/slicer/outflow/lambdaResultWithDirectCall.kt @@ -0,0 +1,5 @@ +// FLOW: OUT + +fun test() { + val x = { 1 }() +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/lambdaResultWithDirectCall.results.txt b/idea/testData/slicer/outflow/lambdaResultWithDirectCall.results.txt new file mode 100644 index 00000000000..aaef78bb048 --- /dev/null +++ b/idea/testData/slicer/outflow/lambdaResultWithDirectCall.results.txt @@ -0,0 +1,5 @@ +4 val x = { 1 }() +4 val x = { 1 }() +4 [LAMBDA] val x = { 1 }() +4 val x = { 1 }() +4 val x = { 1 }() diff --git a/idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt b/idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt new file mode 100644 index 00000000000..b40e7b56efb --- /dev/null +++ b/idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt @@ -0,0 +1,6 @@ +// FLOW: OUT + +fun test() { + val f = { 1 } + val x = f() +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.results.txt b/idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.results.txt new file mode 100644 index 00000000000..7f091ec8911 --- /dev/null +++ b/idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.results.txt @@ -0,0 +1,6 @@ +4 val f = { 1 } +4 val f = { 1 } +4 [LAMBDA] val f = { 1 } +4 [LAMBDA] val f = { 1 } +5 val x = f() +5 val x = f() diff --git a/idea/testData/slicer/outflow/nonLocalReturn.kt b/idea/testData/slicer/outflow/nonLocalReturn.kt new file mode 100644 index 00000000000..2919edab8b7 --- /dev/null +++ b/idea/testData/slicer/outflow/nonLocalReturn.kt @@ -0,0 +1,9 @@ +// FLOW: OUT + +inline fun foo(a: Int, f: (Int) -> Int) = f(a) + +fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } + +fun test() { + val x = bar(1) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/nonLocalReturn.results.txt b/idea/testData/slicer/outflow/nonLocalReturn.results.txt new file mode 100644 index 00000000000..37fcd8ce0cc --- /dev/null +++ b/idea/testData/slicer/outflow/nonLocalReturn.results.txt @@ -0,0 +1,4 @@ +5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } +5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } +8 val x = bar(1) +8 val x = bar(1) diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/AbstractSlicerTest.kt b/idea/tests/org/jetbrains/kotlin/idea/slicer/AbstractSlicerTest.kt index 37db8bf7b8a..f1c26f1d9b4 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/AbstractSlicerTest.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/AbstractSlicerTest.kt @@ -18,11 +18,12 @@ package org.jetbrains.kotlin.idea.slicer import com.intellij.analysis.AnalysisScope import com.intellij.openapi.util.io.FileUtil -import com.intellij.psi.PsiElement import com.intellij.slicer.SliceAnalysisParams import com.intellij.slicer.SliceUsage import com.intellij.usages.TextChunk import com.intellij.util.CommonProcessors +import com.intellij.util.Processor +import gnu.trove.THashSet import gnu.trove.TObjectHashingStrategy import org.jetbrains.kotlin.idea.test.KotlinLightCodeInsightFixtureTestCase import org.jetbrains.kotlin.idea.util.application.runReadAction @@ -33,9 +34,14 @@ import java.awt.Font import java.io.File abstract class AbstractSlicerTest : KotlinLightCodeInsightFixtureTestCase() { - object SliceUsageHashingStrategy : TObjectHashingStrategy { - override fun computeHashCode(`object`: SliceUsage) = `object`.usageInfo.hashCode() - override fun equals(o1: SliceUsage, o2: SliceUsage) = o1.usageInfo == o2.usageInfo + object SliceUsageHashingStrategy : TObjectHashingStrategy { + override fun computeHashCode(`object`: KotlinSliceUsage): Int { + return `object`.usageInfo.hashCode() * 31 + `object`.forcedExpressionMode.hashCode() + } + + override fun equals(o1: KotlinSliceUsage, o2: KotlinSliceUsage): Boolean { + return o1.usageInfo == o2.usageInfo && o1.forcedExpressionMode == o2.forcedExpressionMode + } } // Based on SliceUsage.processChildren @@ -44,13 +50,14 @@ abstract class AbstractSlicerTest : KotlinLightCodeInsightFixtureTestCase() { val element = runReadAction { element } + @Suppress("UNCHECKED_CAST") val uniqueProcessor = CommonProcessors.UniqueProcessor( { processor(it as KotlinSliceUsage) true }, SliceUsageHashingStrategy - ) + ) as Processor runReadAction { if (params.dataFlowToThis) { @@ -63,7 +70,7 @@ abstract class AbstractSlicerTest : KotlinLightCodeInsightFixtureTestCase() { } private fun buildTreeRepresentation(rootUsage: KotlinSliceUsage): String { - val visitedElements = HashSet() + val visitedUsages = THashSet(SliceUsageHashingStrategy) fun TextChunk.render(): String { var text = text @@ -74,7 +81,7 @@ abstract class AbstractSlicerTest : KotlinLightCodeInsightFixtureTestCase() { } fun process(usage: KotlinSliceUsage, indent: Int): String { - val isDuplicated = !visitedElements.add(usage.element) + val isDuplicated = !visitedUsages.add(usage) return buildString { val chunks = usage.text @@ -83,6 +90,7 @@ abstract class AbstractSlicerTest : KotlinLightCodeInsightFixtureTestCase() { if (usage is KotlinSliceDereferenceUsage) { append("DEREFERENCE: ") } + append("[LAMBDA] ".repeat(usage.lambdaLevel)) chunks.slice(1..chunks.size - 1).joinTo( this, separator = "", diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java index 32e7c0a56f9..7449b537056 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java @@ -36,12 +36,30 @@ public class SlicerTestGenerated extends AbstractSlicerTest { KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClass(this.getClass(), new File("idea/testData/slicer"), Pattern.compile("^(.+)\\.kt$"), TargetBackend.ANY); } + @TestMetadata("inflow/anonymousFunBodyExpression.kt") + public void testInflow_AnonymousFunBodyExpression() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/anonymousFunBodyExpression.kt"); + doTest(fileName); + } + + @TestMetadata("inflow/anonymousFunReturnExpression.kt") + public void testInflow_AnonymousFunReturnExpression() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/anonymousFunReturnExpression.kt"); + doTest(fileName); + } + @TestMetadata("inflow/cast.kt") public void testInflow_Cast() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/cast.kt"); doTest(fileName); } + @TestMetadata("inflow/doubleLambdaResult.kt") + public void testInflow_DoubleLambdaResult() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/doubleLambdaResult.kt"); + doTest(fileName); + } + @TestMetadata("inflow/funParamerer.kt") public void testInflow_FunParamerer() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funParamerer.kt"); @@ -72,6 +90,30 @@ public class SlicerTestGenerated extends AbstractSlicerTest { doTest(fileName); } + @TestMetadata("inflow/lambdaResult.kt") + public void testInflow_LambdaResult() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/lambdaResult.kt"); + doTest(fileName); + } + + @TestMetadata("inflow/lambdaResultWithAssignments.kt") + public void testInflow_LambdaResultWithAssignments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/lambdaResultWithAssignments.kt"); + doTest(fileName); + } + + @TestMetadata("inflow/lambdaResultWithDirectCall.kt") + public void testInflow_LambdaResultWithDirectCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/lambdaResultWithDirectCall.kt"); + doTest(fileName); + } + + @TestMetadata("inflow/lambdaResultWithDirectCallViaAssignment.kt") + public void testInflow_LambdaResultWithDirectCallViaAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/lambdaResultWithDirectCallViaAssignment.kt"); + doTest(fileName); + } + @TestMetadata("inflow/localVal.kt") public void testInflow_LocalVal() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/localVal.kt"); @@ -108,6 +150,12 @@ public class SlicerTestGenerated extends AbstractSlicerTest { doTest(fileName); } + @TestMetadata("inflow/nonLocalReturn.kt") + public void testInflow_NonLocalReturn() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/nonLocalReturn.kt"); + doTest(fileName); + } + @TestMetadata("inflow/notNullAssertion.kt") public void testInflow_NotNullAssertion() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/notNullAssertion.kt"); @@ -162,6 +210,18 @@ public class SlicerTestGenerated extends AbstractSlicerTest { doTest(fileName); } + @TestMetadata("outflow/anonymousFunBodyExpression.kt") + public void testOutflow_AnonymousFunBodyExpression() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/anonymousFunBodyExpression.kt"); + doTest(fileName); + } + + @TestMetadata("outflow/anonymousFunReturnExpression.kt") + public void testOutflow_AnonymousFunReturnExpression() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/anonymousFunReturnExpression.kt"); + doTest(fileName); + } + @TestMetadata("outflow/callArgument.kt") public void testOutflow_CallArgument() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/callArgument.kt"); @@ -174,6 +234,18 @@ public class SlicerTestGenerated extends AbstractSlicerTest { doTest(fileName); } + @TestMetadata("outflow/doubleLambdaResult.kt") + public void testOutflow_DoubleLambdaResult() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/doubleLambdaResult.kt"); + doTest(fileName); + } + + @TestMetadata("outflow/explicitLambdaReturnExpression.kt") + public void testOutflow_ExplicitLambdaReturnExpression() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/explicitLambdaReturnExpression.kt"); + doTest(fileName); + } + @TestMetadata("outflow/extensionIndexingDereferences.kt") public void testOutflow_ExtensionIndexingDereferences() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/extensionIndexingDereferences.kt"); @@ -222,6 +294,30 @@ public class SlicerTestGenerated extends AbstractSlicerTest { doTest(fileName); } + @TestMetadata("outflow/lambdaResult.kt") + public void testOutflow_LambdaResult() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/lambdaResult.kt"); + doTest(fileName); + } + + @TestMetadata("outflow/lambdaResultWithAssignments.kt") + public void testOutflow_LambdaResultWithAssignments() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/lambdaResultWithAssignments.kt"); + doTest(fileName); + } + + @TestMetadata("outflow/lambdaResultWithDirectCall.kt") + public void testOutflow_LambdaResultWithDirectCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/lambdaResultWithDirectCall.kt"); + doTest(fileName); + } + + @TestMetadata("outflow/lambdaResultWithDirectCallViaAssignment.kt") + public void testOutflow_LambdaResultWithDirectCallViaAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt"); + doTest(fileName); + } + @TestMetadata("outflow/localVariableUsages.kt") public void testOutflow_LocalVariableUsages() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/localVariableUsages.kt"); @@ -234,6 +330,12 @@ public class SlicerTestGenerated extends AbstractSlicerTest { doTest(fileName); } + @TestMetadata("outflow/nonLocalReturn.kt") + public void testOutflow_NonLocalReturn() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/nonLocalReturn.kt"); + doTest(fileName); + } + @TestMetadata("outflow/notNullAssertion.kt") public void testOutflow_NotNullAssertion() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/notNullAssertion.kt");