Analyze Data Flow: Support val/var parameters
#KT-11994 In Progress
This commit is contained in:
@@ -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
@@ -0,0 +1,9 @@
|
||||
// FLOW: IN
|
||||
|
||||
open class A(val <caret>n: Int)
|
||||
|
||||
class B : A(1)
|
||||
|
||||
fun test() {
|
||||
val z = A(2).n
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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
@@ -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
|
||||
}
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user