From 7c81d93db0102d78d0048dcfb1b97126542537e7 Mon Sep 17 00:00:00 2001 From: Valentin Kipyatkov Date: Tue, 21 Apr 2020 11:18:32 +0300 Subject: [PATCH] Data Flow to Here: process data-class copy-method calls --- .../kotlin/idea/slicer/InflowSlicer.kt | 27 +++++++++++++++++-- idea/testData/slicer/inflow/dataClassCopy.kt | 15 +++++++++++ .../inflow/dataClassCopy.leafGroups.txt | 12 +++++++++ .../inflow/dataClassCopy.nullnessGroups.txt | 3 +++ .../slicer/inflow/dataClassCopy.results.txt | 6 +++++ idea/testData/slicer/outflow/dataClassCopy.kt | 11 ++++++++ .../slicer/outflow/dataClassCopy.results.txt | 6 +++++ .../SlicerLeafGroupingTestGenerated.java | 5 ++++ .../SlicerNullnessGroupingTestGenerated.java | 5 ++++ .../idea/slicer/SlicerTreeTestGenerated.java | 10 +++++++ 10 files changed, 98 insertions(+), 2 deletions(-) create mode 100644 idea/testData/slicer/inflow/dataClassCopy.kt create mode 100644 idea/testData/slicer/inflow/dataClassCopy.leafGroups.txt create mode 100644 idea/testData/slicer/inflow/dataClassCopy.nullnessGroups.txt create mode 100644 idea/testData/slicer/inflow/dataClassCopy.results.txt create mode 100644 idea/testData/slicer/outflow/dataClassCopy.kt create mode 100644 idea/testData/slicer/outflow/dataClassCopy.results.txt diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt index b6a6bcc30cb..b95ac7f00f8 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt @@ -21,7 +21,9 @@ import org.jetbrains.kotlin.descriptors.* import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.idea.caches.resolve.* +import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor +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.idea.references.KtPropertyDelegationMethodsReference @@ -128,8 +130,29 @@ class InflowSlicer( processCalls(function, includeOverriders, ArgumentSliceProducer(parameterDescriptor)) } - if (parameter.valOrVarKeyword.toValVar() == KotlinValVar.Var) { - processAssignments(parameter, analysisScope) + val valVar = parameter.valOrVarKeyword.toValVar() + if (valVar != KotlinValVar.None) { + val classOrObject = (parameter.ownerFunction as? KtPrimaryConstructor)?.getContainingClassOrObject() + if (classOrObject != null && classOrObject.hasModifier(KtTokens.DATA_KEYWORD)) { + // Search usages of constructor parameter in form of named argument of call to "copy" function. + // We will miss calls of "copy" with positional parameters but it's unlikely someone write such code. + // Also, we will find named arguments of constructor calls but we need them anyway (already found above). + val options = KotlinPropertyFindUsagesOptions(project).apply { + searchScope = analysisScope + } + //TODO: optimizations to search only in files where "copy" word is present and also not resolve anything except named arguments + parameter.processAllUsages(options) { usageInfo -> + (((usageInfo.element as? KtNameReferenceExpression) + ?.parent as? KtValueArgumentName) + ?.parent as? KtValueArgument) + ?.getArgumentExpression() + ?.passToProcessorAsValue() + } + } + + if (valVar == KotlinValVar.Var) { + processAssignments(parameter, analysisScope) + } } } diff --git a/idea/testData/slicer/inflow/dataClassCopy.kt b/idea/testData/slicer/inflow/dataClassCopy.kt new file mode 100644 index 00000000000..d2d2554c1d8 --- /dev/null +++ b/idea/testData/slicer/inflow/dataClassCopy.kt @@ -0,0 +1,15 @@ +// FLOW: IN + +data class DataClass(val value1: Int, val value2: Int) + +fun foo(dataClass: DataClass) { + val v = dataClass.value1 +} + +fun bar() { + val dataClass = DataClass(1, 2) + foo(dataClass) + foo(dataClass.copy(value1 = 10)) + foo(dataClass.copy(value2 = 11)) + foo(DataClass(value1 = 1, value2 = 2)) +} \ No newline at end of file diff --git a/idea/testData/slicer/inflow/dataClassCopy.leafGroups.txt b/idea/testData/slicer/inflow/dataClassCopy.leafGroups.txt new file mode 100644 index 00000000000..d08eebfadd4 --- /dev/null +++ b/idea/testData/slicer/inflow/dataClassCopy.leafGroups.txt @@ -0,0 +1,12 @@ +10 val dataClass = DataClass(1, 2) (in bar()) +6 val v = dataClass.value1 (in foo(DataClass)) +6 val v = dataClass.value1 (in foo(DataClass)) +3 data class DataClass(val value1: Int, val value2: Int) (in DataClass) +10 val dataClass = DataClass(1, 2) (in bar()) + +12 foo(dataClass.copy(value1 = 10)) (in bar()) +6 val v = dataClass.value1 (in foo(DataClass)) +6 val v = dataClass.value1 (in foo(DataClass)) +3 data class DataClass(val value1: Int, val value2: Int) (in DataClass) +12 foo(dataClass.copy(value1 = 10)) (in bar()) + diff --git a/idea/testData/slicer/inflow/dataClassCopy.nullnessGroups.txt b/idea/testData/slicer/inflow/dataClassCopy.nullnessGroups.txt new file mode 100644 index 00000000000..086fdaca641 --- /dev/null +++ b/idea/testData/slicer/inflow/dataClassCopy.nullnessGroups.txt @@ -0,0 +1,3 @@ +[NotNull Values] +6 val v = dataClass.value1 (in foo(DataClass)) +6 val v = dataClass.value1 (in foo(DataClass)) diff --git a/idea/testData/slicer/inflow/dataClassCopy.results.txt b/idea/testData/slicer/inflow/dataClassCopy.results.txt new file mode 100644 index 00000000000..a0eb613afe4 --- /dev/null +++ b/idea/testData/slicer/inflow/dataClassCopy.results.txt @@ -0,0 +1,6 @@ +6 val v = dataClass.value1 (in foo(DataClass)) +6 val v = dataClass.value1 (in foo(DataClass)) +3 data class DataClass(val value1: Int, val value2: Int) (in DataClass) +10 val dataClass = DataClass(1, 2) (in bar()) +12 foo(dataClass.copy(value1 = 10)) (in bar()) +14 foo(DataClass(value1 = 1, value2 = 2)) (in bar()) diff --git a/idea/testData/slicer/outflow/dataClassCopy.kt b/idea/testData/slicer/outflow/dataClassCopy.kt new file mode 100644 index 00000000000..ecbea426231 --- /dev/null +++ b/idea/testData/slicer/outflow/dataClassCopy.kt @@ -0,0 +1,11 @@ +// FLOW: OUT + +data class DataClass(val value1: Int, val value2: Int) + +fun foo(dataClass: DataClass) { + val v = dataClass.value1 +} + +fun bar(value: Int, dataClass: DataClass) { + foo(dataClass.copy(value1 = value)) +} \ No newline at end of file diff --git a/idea/testData/slicer/outflow/dataClassCopy.results.txt b/idea/testData/slicer/outflow/dataClassCopy.results.txt new file mode 100644 index 00000000000..f07e939bef6 --- /dev/null +++ b/idea/testData/slicer/outflow/dataClassCopy.results.txt @@ -0,0 +1,6 @@ +9 fun bar(value: Int, dataClass: DataClass) { (in bar(Int, DataClass)) +10 foo(dataClass.copy(value1 = value)) (in bar(Int, DataClass)) +3 data class DataClass(val value1: Int, val value2: Int) (in DataClass) +6 val v = dataClass.value1 (in foo(DataClass)) +6 val v = dataClass.value1 (in foo(DataClass)) +10 foo(dataClass.copy(value1 = value)) (in bar(Int, DataClass)) diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java index 2242947e309..4bcad099ef8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java @@ -58,6 +58,11 @@ public class SlicerLeafGroupingTestGenerated extends AbstractSlicerLeafGroupingT runTest("idea/testData/slicer/inflow/compositeAssignments.kt"); } + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("idea/testData/slicer/inflow/dataClassCopy.kt"); + } + @TestMetadata("defaultGetterFieldInSetter.kt") public void testDefaultGetterFieldInSetter() throws Exception { runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java index 7473b924b7d..eac7d0405a5 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java @@ -58,6 +58,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG runTest("idea/testData/slicer/inflow/compositeAssignments.kt"); } + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("idea/testData/slicer/inflow/dataClassCopy.kt"); + } + @TestMetadata("defaultGetterFieldInSetter.kt") public void testDefaultGetterFieldInSetter() throws Exception { runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java index 4d8e587fec1..038d52a31d0 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java @@ -70,6 +70,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest { runTest("idea/testData/slicer/inflow/compositeAssignments.kt"); } + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("idea/testData/slicer/inflow/dataClassCopy.kt"); + } + @TestMetadata("defaultGetterFieldInSetter.kt") public void testDefaultGetterFieldInSetter() throws Exception { runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt"); @@ -483,6 +488,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest { runTest("idea/testData/slicer/outflow/cast.kt"); } + @TestMetadata("dataClassCopy.kt") + public void testDataClassCopy() throws Exception { + runTest("idea/testData/slicer/outflow/dataClassCopy.kt"); + } + @TestMetadata("defaultExplicitPrimaryConstructorCalls.kt") public void testDefaultExplicitPrimaryConstructorCalls() throws Exception { runTest("idea/testData/slicer/outflow/defaultExplicitPrimaryConstructorCalls.kt");