From 49da81f681e7ea5433f06da098312ebfbd0ceb0e Mon Sep 17 00:00:00 2001 From: Alexey Sedunov Date: Wed, 31 May 2017 19:10:32 +0300 Subject: [PATCH] Analyze Data Flow: Support val/var parameters #KT-11994 In Progress --- .../jetbrains/kotlin/idea/slicer/Slicer.kt | 33 ++++++++++++------- idea/testData/slicer/inflow/valParameter.kt | 9 +++++ .../slicer/inflow/valParameter.results.txt | 3 ++ idea/testData/slicer/inflow/varParameter.kt | 25 ++++++++++++++ .../slicer/inflow/varParameter.results.txt | 5 +++ idea/testData/slicer/outflow/valParameter.kt | 21 ++++++++++++ .../slicer/outflow/valParameter.results.txt | 6 ++++ idea/testData/slicer/outflow/varParameter.kt | 23 +++++++++++++ .../slicer/outflow/varParameter.results.txt | 6 ++++ .../idea/slicer/SlicerTestGenerated.java | 24 ++++++++++++++ 10 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 idea/testData/slicer/inflow/valParameter.kt create mode 100644 idea/testData/slicer/inflow/valParameter.results.txt create mode 100644 idea/testData/slicer/inflow/varParameter.kt create mode 100644 idea/testData/slicer/inflow/varParameter.results.txt create mode 100644 idea/testData/slicer/outflow/valParameter.kt create mode 100644 idea/testData/slicer/outflow/valParameter.results.txt create mode 100644 idea/testData/slicer/outflow/varParameter.kt create mode 100644 idea/testData/slicer/outflow/varParameter.results.txt diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt index 0ed82919f2c..c887955cc74 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -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()?.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()?.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() diff --git a/idea/testData/slicer/inflow/valParameter.kt b/idea/testData/slicer/inflow/valParameter.kt new file mode 100644 index 00000000000..d026d813369 --- /dev/null +++ b/idea/testData/slicer/inflow/valParameter.kt @@ -0,0 +1,9 @@ +// FLOW: IN + +open class A(val n: Int) + +class B : A(1) + +fun test() { + val z = A(2).n +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/valParameter.results.txt b/idea/testData/slicer/inflow/valParameter.results.txt new file mode 100644 index 00000000000..4d949b68e97 --- /dev/null +++ b/idea/testData/slicer/inflow/valParameter.results.txt @@ -0,0 +1,3 @@ +3 open class A(val n: Int) +5 class B : A(1) +8 val z = A(2).n diff --git a/idea/testData/slicer/inflow/varParameter.kt b/idea/testData/slicer/inflow/varParameter.kt new file mode 100644 index 00000000000..8c100003cb1 --- /dev/null +++ b/idea/testData/slicer/inflow/varParameter.kt @@ -0,0 +1,25 @@ +// FLOW: IN + +open class A(var 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 +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/varParameter.results.txt b/idea/testData/slicer/inflow/varParameter.results.txt new file mode 100644 index 00000000000..d139d9280cb --- /dev/null +++ b/idea/testData/slicer/inflow/varParameter.results.txt @@ -0,0 +1,5 @@ +3 open class A(var n: Int) { +20 class B : A(1) +23 val z = A(2).n +24 A(3).n = 2 +16 n = 1 diff --git a/idea/testData/slicer/outflow/valParameter.kt b/idea/testData/slicer/outflow/valParameter.kt new file mode 100644 index 00000000000..9a21c3897bf --- /dev/null +++ b/idea/testData/slicer/outflow/valParameter.kt @@ -0,0 +1,21 @@ +// FLOW: OUT + +class A(val 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 +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/valParameter.results.txt b/idea/testData/slicer/outflow/valParameter.results.txt new file mode 100644 index 00000000000..5cd4ee882f3 --- /dev/null +++ b/idea/testData/slicer/outflow/valParameter.results.txt @@ -0,0 +1,6 @@ +3 class A(val n: Int) { +4 val x = n +6 val y: Int +14 fun bar(m: Int) { +15 val z = n +20 val z = A(1).n diff --git a/idea/testData/slicer/outflow/varParameter.kt b/idea/testData/slicer/outflow/varParameter.kt new file mode 100644 index 00000000000..4b285951e0a --- /dev/null +++ b/idea/testData/slicer/outflow/varParameter.kt @@ -0,0 +1,23 @@ +// FLOW: OUT + +class A(var 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 +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/varParameter.results.txt b/idea/testData/slicer/outflow/varParameter.results.txt new file mode 100644 index 00000000000..2ab1b3cecc4 --- /dev/null +++ b/idea/testData/slicer/outflow/varParameter.results.txt @@ -0,0 +1,6 @@ +3 class A(var n: Int) { +4 val x = n +6 val y: Int +14 fun bar(m: Int) { +15 val z = n +21 val z = A(1).n diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java index 596d8697686..2207ad83829 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestGenerated.java @@ -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");