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.TraversalOrder
|
||||||
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse
|
import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
|
||||||
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyze
|
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.KotlinFunctionFindUsagesOptions
|
||||||
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
|
import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
|
||||||
import org.jetbrains.kotlin.idea.findUsages.processAllUsages
|
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.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector
|
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
@@ -89,7 +90,7 @@ private fun KtDeclaration.processVariableAccesses(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun KtParameter.canProcess(): Boolean {
|
private fun KtParameter.canProcess(): Boolean {
|
||||||
return !(isLoopParameter || isVarArg || hasValOrVar())
|
return !(isLoopParameter || isVarArg)
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract class Slicer(
|
abstract class Slicer(
|
||||||
@@ -127,12 +128,7 @@ class InflowSlicer(
|
|||||||
) : Slicer(element, processor, parentUsage) {
|
) : Slicer(element, processor, parentUsage) {
|
||||||
private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true)
|
private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true)
|
||||||
|
|
||||||
private fun KtProperty.processAssignments() {
|
private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) {
|
||||||
val analysisScope = parentUsage.scope.toSearchScope()
|
|
||||||
val accessSearchScope = if (isVar) analysisScope else {
|
|
||||||
val containerScope = getStrictParentOfType<KtDeclaration>()?.let { LocalSearchScope(it) } ?: return
|
|
||||||
analysisScope.intersectWith(containerScope)
|
|
||||||
}
|
|
||||||
processVariableAccesses(accessSearchScope, Access.Write) body@ {
|
processVariableAccesses(accessSearchScope, Access.Write) body@ {
|
||||||
val refExpression = it.element as? KtExpression ?: return@body
|
val refExpression = it.element as? KtExpression ?: return@body
|
||||||
val rhs = KtPsiUtil.safeDeparenthesize(refExpression).getAssignmentByLHS()?.right ?: 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() {
|
private fun KtProperty.processProperty() {
|
||||||
val bindingContext by lazy { analyzeFully() }
|
val bindingContext by lazy { analyzeFully() }
|
||||||
|
|
||||||
@@ -168,7 +173,7 @@ class InflowSlicer(
|
|||||||
val isDefaultSetter = setter?.bodyExpression == null
|
val isDefaultSetter = setter?.bodyExpression == null
|
||||||
if (isDefaultGetter) {
|
if (isDefaultGetter) {
|
||||||
if (isDefaultSetter) {
|
if (isDefaultSetter) {
|
||||||
processAssignments()
|
processPropertyAssignments()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
setter!!.processBackingFieldAssignments()
|
setter!!.processBackingFieldAssignments()
|
||||||
@@ -183,11 +188,11 @@ class InflowSlicer(
|
|||||||
if (function.isOverridable()) return
|
if (function.isOverridable()) return
|
||||||
|
|
||||||
if (function is KtPropertyAccessor && function.isSetter) {
|
if (function is KtPropertyAccessor && function.isSetter) {
|
||||||
function.property.processAssignments()
|
function.property.processPropertyAssignments()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
val parameterDescriptor = resolveToDescriptor() as ValueParameterDescriptor
|
val parameterDescriptor = analyze()[BindingContext.VALUE_PARAMETER, this] ?: return
|
||||||
|
|
||||||
(function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope()) body@ {
|
(function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope()) body@ {
|
||||||
val refExpression = it.element as? KtExpression ?: return@body
|
val refExpression = it.element as? KtExpression ?: return@body
|
||||||
@@ -201,6 +206,10 @@ class InflowSlicer(
|
|||||||
} ?: return@body
|
} ?: return@body
|
||||||
flownExpression.passToProcessorAsValue()
|
flownExpression.passToProcessorAsValue()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (valOrVarKeyword.toValVar() == KotlinValVar.Var) {
|
||||||
|
processAssignments(parentUsage.scope.toSearchScope())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun KtDeclarationWithBody.processFunction() {
|
private fun KtDeclarationWithBody.processFunction() {
|
||||||
@@ -257,7 +266,7 @@ class InflowSlicer(
|
|||||||
if (accessedDescriptor is SyntheticFieldDescriptor) {
|
if (accessedDescriptor is SyntheticFieldDescriptor) {
|
||||||
val property = accessedDeclaration as? KtProperty ?: return
|
val property = accessedDeclaration as? KtProperty ?: return
|
||||||
if (accessedDescriptor.propertyDescriptor.setter?.isDefault ?: true) {
|
if (accessedDescriptor.propertyDescriptor.setter?.isDefault ?: true) {
|
||||||
property.processAssignments()
|
property.processPropertyAssignments()
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
property.setter?.processBackingFieldAssignments()
|
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);
|
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")
|
@TestMetadata("inflow/whenExpression.kt")
|
||||||
public void testInflow_WhenExpression() throws Exception {
|
public void testInflow_WhenExpression() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/whenExpression.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/inflow/whenExpression.kt");
|
||||||
@@ -468,6 +480,18 @@ public class SlicerTestGenerated extends AbstractSlicerTest {
|
|||||||
doTest(fileName);
|
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")
|
@TestMetadata("outflow/whenExpression.kt")
|
||||||
public void testOutflow_WhenExpression() throws Exception {
|
public void testOutflow_WhenExpression() throws Exception {
|
||||||
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/whenExpression.kt");
|
String fileName = KotlinTestUtils.navigationMetadata("idea/testData/slicer/outflow/whenExpression.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user