Analyze Data Flow: Support callable references

#KT-11994 In Progress
This commit is contained in:
Alexey Sedunov
2017-06-05 21:31:58 +03:00
parent 3f411fc93b
commit d61ddaccb6
14 changed files with 155 additions and 0 deletions
@@ -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
}
+10
View File
@@ -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)
}
@@ -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
+10
View File
@@ -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)
}
@@ -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)
}
@@ -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");