diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt index 265836d983f..797efa4b33d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -32,6 +32,7 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource import org.jetbrains.kotlin.descriptors.PropertyDescriptor import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor import org.jetbrains.kotlin.idea.caches.resolve.analyze @@ -43,6 +44,7 @@ import org.jetbrains.kotlin.idea.findUsages.processAllUsages import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector import org.jetbrains.kotlin.psi.* import org.jetbrains.kotlin.psi.psiUtil.* +import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.calls.callUtil.getResolvedCall import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument @@ -211,6 +213,12 @@ class InflowSlicer( is MagicInstruction -> when (createdAt.kind) { MagicKind.NOT_NULL_ASSERTION, MagicKind.CAST -> createdAt.passInputsToProcessor() + MagicKind.BOUND_CALLABLE_REFERENCE, MagicKind.UNBOUND_CALLABLE_REFERENCE -> { + val callableRefExpr = expressionValue.element as? KtCallableReferenceExpression ?: return + val referencedDescriptor = analyze()[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return + val referencedDeclaration = (referencedDescriptor as? DeclarationDescriptorWithSource)?.source?.getPsi() ?: return + referencedDeclaration.passToProcessor(parentUsage.lambdaLevel - 1) + } else -> return } @@ -277,10 +285,17 @@ class OutflowSlicer( return null } + private fun PsiElement.getCallableReferenceForExactCallee(): KtCallableReferenceExpression? { + val callableRef = getParentOfTypeAndBranch { callableReference } ?: return null + val callee = KtPsiUtil.safeDeparenthesize(callableRef.callableReference) + return if (callee == this) callableRef else null + } + private fun KtFunction.processFunction() { if (this is KtConstructor<*> || this is KtNamedFunction && name != null) { processCalls(parentUsage.scope.toSearchScope()) { it.element?.getCallElementForExactCallee()?.passToProcessor() + it.element?.getCallableReferenceForExactCallee()?.passToProcessor(parentUsage.lambdaLevel + 1) } return } diff --git a/idea/testData/slicer/inflow/funResultViaCallableRef.kt b/idea/testData/slicer/inflow/funResultViaCallableRef.kt new file mode 100644 index 00000000000..a0837c47674 --- /dev/null +++ b/idea/testData/slicer/inflow/funResultViaCallableRef.kt @@ -0,0 +1,10 @@ +// FLOW: IN + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + fun bar(n: Int) = n + val x = foo(::bar) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/funResultViaCallableRef.results.txt b/idea/testData/slicer/inflow/funResultViaCallableRef.results.txt new file mode 100644 index 00000000000..f85f5507d9c --- /dev/null +++ b/idea/testData/slicer/inflow/funResultViaCallableRef.results.txt @@ -0,0 +1,10 @@ +9 val x = foo(::bar) +9 val x = foo(::bar) +3 fun foo(f: (Int) -> Int): Int { +4 return f(1) +4 [LAMBDA] return f(1) +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +9 [LAMBDA] val x = foo(::bar) +8 fun bar(n: Int) = n +8 fun bar(n: Int) = n +8 fun bar(n: Int) = n diff --git a/idea/testData/slicer/inflow/funResultViaCallableRefWithAssignment.kt b/idea/testData/slicer/inflow/funResultViaCallableRefWithAssignment.kt new file mode 100644 index 00000000000..d4ae77f229d --- /dev/null +++ b/idea/testData/slicer/inflow/funResultViaCallableRefWithAssignment.kt @@ -0,0 +1,11 @@ +// FLOW: IN + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + fun bar(n: Int) = n + val f = ::bar + val x = foo(f) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/funResultViaCallableRefWithAssignment.results.txt b/idea/testData/slicer/inflow/funResultViaCallableRefWithAssignment.results.txt new file mode 100644 index 00000000000..39b1d8c302c --- /dev/null +++ b/idea/testData/slicer/inflow/funResultViaCallableRefWithAssignment.results.txt @@ -0,0 +1,12 @@ +10 val x = foo(f) +10 val x = foo(f) +3 fun foo(f: (Int) -> Int): Int { +4 return f(1) +4 [LAMBDA] return f(1) +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +10 [LAMBDA] val x = foo(f) +9 [LAMBDA] val f = ::bar +9 [LAMBDA] val f = ::bar +8 fun bar(n: Int) = n +8 fun bar(n: Int) = n +8 fun bar(n: Int) = n diff --git a/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.kt b/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.kt new file mode 100644 index 00000000000..86a82e4b0c9 --- /dev/null +++ b/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.kt @@ -0,0 +1,6 @@ +// FLOW: IN + +fun test() { + fun bar(n: Int) = n + val x = (::bar)(1) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.results.txt b/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.results.txt new file mode 100644 index 00000000000..aae08af218e --- /dev/null +++ b/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.results.txt @@ -0,0 +1,6 @@ +5 val x = (::bar)(1) +5 val x = (::bar)(1) +5 [LAMBDA] val x = (::bar)(1) +4 fun bar(n: Int) = n +4 fun bar(n: Int) = n +4 fun bar(n: Int) = n diff --git a/idea/testData/slicer/outflow/funResultViaCallableRef.kt b/idea/testData/slicer/outflow/funResultViaCallableRef.kt new file mode 100644 index 00000000000..fbd84f10ab3 --- /dev/null +++ b/idea/testData/slicer/outflow/funResultViaCallableRef.kt @@ -0,0 +1,10 @@ +// FLOW: OUT + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + fun bar(n: Int) = n + val x = foo(::bar) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/funResultViaCallableRef.results.txt b/idea/testData/slicer/outflow/funResultViaCallableRef.results.txt new file mode 100644 index 00000000000..c41704dc49c --- /dev/null +++ b/idea/testData/slicer/outflow/funResultViaCallableRef.results.txt @@ -0,0 +1,8 @@ +8 fun bar(n: Int) = n +8 fun bar(n: Int) = n +9 [LAMBDA] val x = foo(::bar) +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +4 return f(1) +3 fun foo(f: (Int) -> Int): Int { +9 val x = foo(::bar) +9 val x = foo(::bar) diff --git a/idea/testData/slicer/outflow/funResultViaCallableRefWithAssignment.kt b/idea/testData/slicer/outflow/funResultViaCallableRefWithAssignment.kt new file mode 100644 index 00000000000..47fe8ec7120 --- /dev/null +++ b/idea/testData/slicer/outflow/funResultViaCallableRefWithAssignment.kt @@ -0,0 +1,11 @@ +// FLOW: OUT + +fun foo(f: (Int) -> Int): Int { + return f(1) +} + +fun test() { + fun bar(n: Int) = n + val f = ::bar + val x = foo(f) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/funResultViaCallableRefWithAssignment.results.txt b/idea/testData/slicer/outflow/funResultViaCallableRefWithAssignment.results.txt new file mode 100644 index 00000000000..36ab1c508e6 --- /dev/null +++ b/idea/testData/slicer/outflow/funResultViaCallableRefWithAssignment.results.txt @@ -0,0 +1,9 @@ +8 fun bar(n: Int) = n +8 fun bar(n: Int) = n +9 [LAMBDA] val f = ::bar +9 [LAMBDA] val f = ::bar +3 [LAMBDA] fun foo(f: (Int) -> Int): Int { +4 return f(1) +3 fun foo(f: (Int) -> Int): Int { +10 val x = foo(f) +10 val x = foo(f) diff --git a/idea/testData/slicer/outflow/funResultViaCallableRefWithDirectCall.kt b/idea/testData/slicer/outflow/funResultViaCallableRefWithDirectCall.kt new file mode 100644 index 00000000000..9cd82df6bf8 --- /dev/null +++ b/idea/testData/slicer/outflow/funResultViaCallableRefWithDirectCall.kt @@ -0,0 +1,6 @@ +// FLOW: OUT + +fun test() { + fun bar(n: Int) = n + val x = (::bar)(1) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/funResultViaCallableRefWithDirectCall.results.txt b/idea/testData/slicer/outflow/funResultViaCallableRefWithDirectCall.results.txt new file mode 100644 index 00000000000..630a32f7167 --- /dev/null +++ b/idea/testData/slicer/outflow/funResultViaCallableRefWithDirectCall.results.txt @@ -0,0 +1,5 @@ +4 fun bar(n: Int) = n +4 fun bar(n: Int) = n +5 [LAMBDA] val x = (::bar)(1) +5 val x = (::bar)(1) +5 val x = (::bar)(1) diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java index 7449b537056..52d8be55413 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java @@ -72,6 +72,24 @@ public class SlicerTestGenerated extends AbstractSlicerTest { doTest(fileName); } + @TestMetadata("inflow/funResultViaCallableRef.kt") + public void testInflow_FunResultViaCallableRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funResultViaCallableRef.kt"); + doTest(fileName); + } + + @TestMetadata("inflow/funResultViaCallableRefWithAssignment.kt") + public void testInflow_FunResultViaCallableRefWithAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funResultViaCallableRefWithAssignment.kt"); + doTest(fileName); + } + + @TestMetadata("inflow/funResultViaCallableRefWithDirectCall.kt") + public void testInflow_FunResultViaCallableRefWithDirectCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.kt"); + doTest(fileName); + } + @TestMetadata("inflow/funWithExpressionBody.kt") public void testInflow_FunWithExpressionBody() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/funWithExpressionBody.kt"); @@ -270,6 +288,24 @@ public class SlicerTestGenerated extends AbstractSlicerTest { doTest(fileName); } + @TestMetadata("outflow/funResultViaCallableRef.kt") + public void testOutflow_FunResultViaCallableRef() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/funResultViaCallableRef.kt"); + doTest(fileName); + } + + @TestMetadata("outflow/funResultViaCallableRefWithAssignment.kt") + public void testOutflow_FunResultViaCallableRefWithAssignment() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/funResultViaCallableRefWithAssignment.kt"); + doTest(fileName); + } + + @TestMetadata("outflow/funResultViaCallableRefWithDirectCall.kt") + public void testOutflow_FunResultViaCallableRefWithDirectCall() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/funResultViaCallableRefWithDirectCall.kt"); + doTest(fileName); + } + @TestMetadata("outflow/funReturnExpression.kt") public void testOutflow_FunReturnExpression() throws Exception { String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/funReturnExpression.kt");