Analyze Data Flow: Support callable references
#KT-11994 In Progress
This commit is contained in:
@@ -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<KtCallableReferenceExpression> { 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
|
||||
}
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
// FLOW: IN
|
||||
|
||||
fun foo(f: (Int) -> Int): Int {
|
||||
return f(1)
|
||||
}
|
||||
|
||||
fun test() {
|
||||
fun bar(n: Int) = n
|
||||
val <caret>x = foo(::bar)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
9 val <bold>x = foo(::bar)</bold>
|
||||
9 val x = <bold>foo(::bar)</bold>
|
||||
3 fun <bold>foo(f: (Int) -> Int): Int {</bold>
|
||||
4 return <bold>f(1)</bold>
|
||||
4 [LAMBDA] return <bold>f</bold>(1)
|
||||
3 [LAMBDA] fun foo(<bold>f: (Int) -> Int</bold>): Int {
|
||||
9 [LAMBDA] val x = foo(<bold>::bar</bold>)
|
||||
8 fun <bold>bar(n: Int) = n</bold>
|
||||
8 fun bar(n: Int) = <bold>n</bold>
|
||||
8 fun bar(<bold>n: Int</bold>) = n
|
||||
@@ -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 <caret>x = foo(f)
|
||||
}
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
10 val <bold>x = foo(f)</bold>
|
||||
10 val x = <bold>foo(f)</bold>
|
||||
3 fun <bold>foo(f: (Int) -> Int): Int {</bold>
|
||||
4 return <bold>f(1)</bold>
|
||||
4 [LAMBDA] return <bold>f</bold>(1)
|
||||
3 [LAMBDA] fun foo(<bold>f: (Int) -> Int</bold>): Int {
|
||||
10 [LAMBDA] val x = foo(<bold>f</bold>)
|
||||
9 [LAMBDA] val <bold>f = ::bar</bold>
|
||||
9 [LAMBDA] val f = <bold>::bar</bold>
|
||||
8 fun <bold>bar(n: Int) = n</bold>
|
||||
8 fun bar(n: Int) = <bold>n</bold>
|
||||
8 fun bar(<bold>n: Int</bold>) = n
|
||||
@@ -0,0 +1,6 @@
|
||||
// FLOW: IN
|
||||
|
||||
fun test() {
|
||||
fun bar(n: Int) = n
|
||||
val <caret>x = (::bar)(1)
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
5 val <bold>x = (::bar)(1)</bold>
|
||||
5 val x = <bold>(::bar)(1)</bold>
|
||||
5 [LAMBDA] val x = <bold>(::bar)</bold>(1)
|
||||
4 fun <bold>bar(n: Int) = n</bold>
|
||||
4 fun bar(n: Int) = <bold>n</bold>
|
||||
4 fun bar(<bold>n: Int</bold>) = n
|
||||
@@ -0,0 +1,10 @@
|
||||
// FLOW: OUT
|
||||
|
||||
fun foo(f: (Int) -> Int): Int {
|
||||
return f(1)
|
||||
}
|
||||
|
||||
fun test() {
|
||||
fun bar(n: Int) = <caret>n
|
||||
val x = foo(::bar)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
8 fun bar(n: Int) = <bold>n</bold>
|
||||
8 fun <bold>bar(n: Int) = n</bold>
|
||||
9 [LAMBDA] val x = foo(<bold>::bar</bold>)
|
||||
3 [LAMBDA] fun foo(<bold>f: (Int) -> Int</bold>): Int {
|
||||
4 return <bold>f(1)</bold>
|
||||
3 fun <bold>foo(f: (Int) -> Int): Int {</bold>
|
||||
9 val x = <bold>foo(::bar)</bold>
|
||||
9 val <bold>x = foo(::bar)</bold>
|
||||
@@ -0,0 +1,11 @@
|
||||
// FLOW: OUT
|
||||
|
||||
fun foo(f: (Int) -> Int): Int {
|
||||
return f(1)
|
||||
}
|
||||
|
||||
fun test() {
|
||||
fun bar(n: Int) = <caret>n
|
||||
val f = ::bar
|
||||
val x = foo(f)
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
8 fun bar(n: Int) = <bold>n</bold>
|
||||
8 fun <bold>bar(n: Int) = n</bold>
|
||||
9 [LAMBDA] val f = <bold>::bar</bold>
|
||||
9 [LAMBDA] val <bold>f = ::bar</bold>
|
||||
3 [LAMBDA] fun foo(<bold>f: (Int) -> Int</bold>): Int {
|
||||
4 return <bold>f(1)</bold>
|
||||
3 fun <bold>foo(f: (Int) -> Int): Int {</bold>
|
||||
10 val x = <bold>foo(f)</bold>
|
||||
10 val <bold>x = foo(f)</bold>
|
||||
@@ -0,0 +1,6 @@
|
||||
// FLOW: OUT
|
||||
|
||||
fun test() {
|
||||
fun bar(n: Int) = <caret>n
|
||||
val x = (::bar)(1)
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
4 fun bar(n: Int) = <bold>n</bold>
|
||||
4 fun <bold>bar(n: Int) = n</bold>
|
||||
5 [LAMBDA] val x = (<bold>::bar</bold>)(1)
|
||||
5 val x = <bold>(::bar)(1)</bold>
|
||||
5 val <bold>x = (::bar)(1)</bold>
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user