Analyze Data Flow: Support val/var parameters

#KT-11994 In Progress
This commit is contained in:
Alexey Sedunov
2017-05-31 19:10:32 +03:00
parent ea7d535ae7
commit 49da81f681
10 changed files with 143 additions and 12 deletions
@@ -34,7 +34,6 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruc
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.ValueParameterDescriptor
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.analyze
@@ -43,6 +42,8 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptor
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
import org.jetbrains.kotlin.idea.findUsages.processAllUsages
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
import org.jetbrains.kotlin.lexer.KtTokens
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector
import org.jetbrains.kotlin.psi.*
@@ -89,7 +90,7 @@ private fun KtDeclaration.processVariableAccesses(
}
private fun KtParameter.canProcess(): Boolean {
return !(isLoopParameter || isVarArg || hasValOrVar())
return !(isLoopParameter || isVarArg)
}
abstract class Slicer(
@@ -127,12 +128,7 @@ class InflowSlicer(
) : Slicer(element, processor, parentUsage) {
private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true)
private fun KtProperty.processAssignments() {
val analysisScope = parentUsage.scope.toSearchScope()
val accessSearchScope = if (isVar) analysisScope else {
val containerScope = getStrictParentOfType<KtDeclaration>()?.let { LocalSearchScope(it) } ?: return
analysisScope.intersectWith(containerScope)
}
private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) {
processVariableAccesses(accessSearchScope, Access.Write) body@ {
val refExpression = it.element as? KtExpression ?: return@body
val rhs = KtPsiUtil.safeDeparenthesize(refExpression).getAssignmentByLHS()?.right ?: return@body
@@ -150,6 +146,15 @@ class InflowSlicer(
}
}
private fun KtProperty.processPropertyAssignments() {
val analysisScope = parentUsage.scope.toSearchScope()
val accessSearchScope = if (isVar) analysisScope else {
val containerScope = getStrictParentOfType<KtDeclaration>()?.let { LocalSearchScope(it) } ?: return
analysisScope.intersectWith(containerScope)
}
processAssignments(accessSearchScope)
}
private fun KtProperty.processProperty() {
val bindingContext by lazy { analyzeFully() }
@@ -168,7 +173,7 @@ class InflowSlicer(
val isDefaultSetter = setter?.bodyExpression == null
if (isDefaultGetter) {
if (isDefaultSetter) {
processAssignments()
processPropertyAssignments()
}
else {
setter!!.processBackingFieldAssignments()
@@ -183,11 +188,11 @@ class InflowSlicer(
if (function.isOverridable()) return
if (function is KtPropertyAccessor && function.isSetter) {
function.property.processAssignments()
function.property.processPropertyAssignments()
return
}
val parameterDescriptor = resolveToDescriptor() as ValueParameterDescriptor
val parameterDescriptor = analyze()[BindingContext.VALUE_PARAMETER, this] ?: return
(function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope()) body@ {
val refExpression = it.element as? KtExpression ?: return@body
@@ -201,6 +206,10 @@ class InflowSlicer(
} ?: return@body
flownExpression.passToProcessorAsValue()
}
if (valOrVarKeyword.toValVar() == KotlinValVar.Var) {
processAssignments(parentUsage.scope.toSearchScope())
}
}
private fun KtDeclarationWithBody.processFunction() {
@@ -257,7 +266,7 @@ class InflowSlicer(
if (accessedDescriptor is SyntheticFieldDescriptor) {
val property = accessedDeclaration as? KtProperty ?: return
if (accessedDescriptor.propertyDescriptor.setter?.isDefault ?: true) {
property.processAssignments()
property.processPropertyAssignments()
}
else {
property.setter?.processBackingFieldAssignments()
+9
View File
@@ -0,0 +1,9 @@
// FLOW: IN
open class A(val <caret>n: Int)
class B : A(1)
fun test() {
val z = A(2).n
}
+3
View File
@@ -0,0 +1,3 @@
3 open class A(val <bold>n: Int</bold>)
5 class B : A(<bold>1</bold>)
8 val z = A(<bold>2</bold>).n
+25
View File
@@ -0,0 +1,25 @@
// FLOW: IN
open class A(var <caret>n: Int) {
val x = n
val y: Int
init {
y = n
bar(n)
}
fun bar(m: Int) {
val z = n
n = 1
}
}
class B : A(1)
fun test() {
val z = A(2).n
A(3).n = 2
}
+5
View File
@@ -0,0 +1,5 @@
3 open class A(var <bold>n: Int</bold>) {
20 class B : A(<bold>1</bold>)
23 val z = A(<bold>2</bold>).n
24 A(<bold>3</bold>).n = 2
16 n = <bold>1</bold>
+21
View File
@@ -0,0 +1,21 @@
// FLOW: OUT
class A(val <caret>n: Int) {
val x = n
val y: Int
init {
y = n
bar(n)
}
fun bar(m: Int) {
val z = n
}
}
fun test() {
val z = A(1).n
}
+6
View File
@@ -0,0 +1,6 @@
3 class A(val <bold>n: Int</bold>) {
4 val <bold>x = n</bold>
6 val <bold>y: Int</bold>
14 fun bar(<bold>m: Int</bold>) {
15 val <bold>z = n</bold>
20 val <bold>z = A(1).n</bold>
+23
View File
@@ -0,0 +1,23 @@
// FLOW: OUT
class A(var <caret>n: Int) {
val x = n
val y: Int
init {
y = n
bar(n)
}
fun bar(m: Int) {
val z = n
n = 1
}
}
fun test() {
val z = A(1).n
A(1).n = 2
}
+6
View File
@@ -0,0 +1,6 @@
3 class A(var <bold>n: Int</bold>) {
4 val <bold>x = n</bold>
6 val <bold>y: Int</bold>
14 fun bar(<bold>m: Int</bold>) {
15 val <bold>z = n</bold>
21 val <bold>z = A(1).n</bold>
@@ -258,6 +258,18 @@ public class SlicerTestGenerated extends AbstractSlicerTest {
doTest(fileName);
}
@TestMetadata("inflow/valParameter.kt")
public void testInflow_ValParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/valParameter.kt");
doTest(fileName);
}
@TestMetadata("inflow/varParameter.kt")
public void testInflow_VarParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/varParameter.kt");
doTest(fileName);
}
@TestMetadata("inflow/whenExpression.kt")
public void testInflow_WhenExpression() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/whenExpression.kt");
@@ -468,6 +480,18 @@ public class SlicerTestGenerated extends AbstractSlicerTest {
doTest(fileName);
}
@TestMetadata("outflow/valParameter.kt")
public void testOutflow_ValParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/valParameter.kt");
doTest(fileName);
}
@TestMetadata("outflow/varParameter.kt")
public void testOutflow_VarParameter() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/varParameter.kt");
doTest(fileName);
}
@TestMetadata("outflow/whenExpression.kt")
public void testOutflow_WhenExpression() throws Exception {
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/whenExpression.kt");