Data Flow to Here: process data-class copy-method calls
This commit is contained in:
@@ -21,7 +21,9 @@ import org.jetbrains.kotlin.descriptors.*
|
|||||||
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.AnonymousFunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.*
|
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.handlers.SliceUsageProcessor
|
||||||
|
import org.jetbrains.kotlin.idea.findUsages.processAllUsages
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
||||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
|
import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
|
||||||
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
|
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
|
||||||
@@ -128,8 +130,29 @@ class InflowSlicer(
|
|||||||
processCalls(function, includeOverriders, ArgumentSliceProducer(parameterDescriptor))
|
processCalls(function, includeOverriders, ArgumentSliceProducer(parameterDescriptor))
|
||||||
}
|
}
|
||||||
|
|
||||||
if (parameter.valOrVarKeyword.toValVar() == KotlinValVar.Var) {
|
val valVar = parameter.valOrVarKeyword.toValVar()
|
||||||
processAssignments(parameter, analysisScope)
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// FLOW: IN
|
||||||
|
|
||||||
|
data class DataClass(val value1: Int, val value2: Int)
|
||||||
|
|
||||||
|
fun foo(dataClass: DataClass) {
|
||||||
|
val <caret>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))
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
10 val dataClass = DataClass(<bold>1</bold>, 2) (in bar())
|
||||||
|
6 <bold>val v</bold> = dataClass.value1 (in foo(DataClass))
|
||||||
|
6 val v = <bold>dataClass.value1</bold> (in foo(DataClass))
|
||||||
|
3 data class DataClass(<bold>val value1: Int</bold>, val value2: Int) (in DataClass)
|
||||||
|
10 val dataClass = DataClass(<bold>1</bold>, 2) (in bar())
|
||||||
|
|
||||||
|
12 foo(dataClass.copy(value1 = <bold>10</bold>)) (in bar())
|
||||||
|
6 <bold>val v</bold> = dataClass.value1 (in foo(DataClass))
|
||||||
|
6 val v = <bold>dataClass.value1</bold> (in foo(DataClass))
|
||||||
|
3 data class DataClass(<bold>val value1: Int</bold>, val value2: Int) (in DataClass)
|
||||||
|
12 foo(dataClass.copy(value1 = <bold>10</bold>)) (in bar())
|
||||||
|
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
[NotNull Values]
|
||||||
|
6 <bold>val v</bold> = dataClass.value1 (in foo(DataClass))
|
||||||
|
6 <bold>val v</bold> = dataClass.value1 (in foo(DataClass))
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
6 <bold>val v</bold> = dataClass.value1 (in foo(DataClass))
|
||||||
|
6 val v = <bold>dataClass.value1</bold> (in foo(DataClass))
|
||||||
|
3 data class DataClass(<bold>val value1: Int</bold>, val value2: Int) (in DataClass)
|
||||||
|
10 val dataClass = DataClass(<bold>1</bold>, 2) (in bar())
|
||||||
|
12 foo(dataClass.copy(value1 = <bold>10</bold>)) (in bar())
|
||||||
|
14 foo(DataClass(value1 = <bold>1</bold>, value2 = 2)) (in bar())
|
||||||
+11
@@ -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(<caret>value: Int, dataClass: DataClass) {
|
||||||
|
foo(dataClass.copy(value1 = value))
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
9 fun bar(<bold>value: Int</bold>, dataClass: DataClass) { (in bar(Int, DataClass))
|
||||||
|
10 foo(dataClass.copy(value1 = <bold>value</bold>)) (in bar(Int, DataClass))
|
||||||
|
3 data class DataClass(<bold>val value1: Int</bold>, val value2: Int) (in DataClass)
|
||||||
|
6 val v = dataClass.<bold>value1</bold> (in foo(DataClass))
|
||||||
|
6 <bold>val v</bold> = dataClass.value1 (in foo(DataClass))
|
||||||
|
10 foo(dataClass.copy(<bold>value1</bold> = value)) (in bar(Int, DataClass))
|
||||||
+5
@@ -58,6 +58,11 @@ public class SlicerLeafGroupingTestGenerated extends AbstractSlicerLeafGroupingT
|
|||||||
runTest("idea/testData/slicer/inflow/compositeAssignments.kt");
|
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")
|
@TestMetadata("defaultGetterFieldInSetter.kt")
|
||||||
public void testDefaultGetterFieldInSetter() throws Exception {
|
public void testDefaultGetterFieldInSetter() throws Exception {
|
||||||
runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");
|
runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");
|
||||||
|
|||||||
+5
@@ -58,6 +58,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG
|
|||||||
runTest("idea/testData/slicer/inflow/compositeAssignments.kt");
|
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")
|
@TestMetadata("defaultGetterFieldInSetter.kt")
|
||||||
public void testDefaultGetterFieldInSetter() throws Exception {
|
public void testDefaultGetterFieldInSetter() throws Exception {
|
||||||
runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");
|
runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");
|
||||||
|
|||||||
@@ -70,6 +70,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
|
|||||||
runTest("idea/testData/slicer/inflow/compositeAssignments.kt");
|
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")
|
@TestMetadata("defaultGetterFieldInSetter.kt")
|
||||||
public void testDefaultGetterFieldInSetter() throws Exception {
|
public void testDefaultGetterFieldInSetter() throws Exception {
|
||||||
runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");
|
runTest("idea/testData/slicer/inflow/defaultGetterFieldInSetter.kt");
|
||||||
@@ -483,6 +488,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
|
|||||||
runTest("idea/testData/slicer/outflow/cast.kt");
|
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")
|
@TestMetadata("defaultExplicitPrimaryConstructorCalls.kt")
|
||||||
public void testDefaultExplicitPrimaryConstructorCalls() throws Exception {
|
public void testDefaultExplicitPrimaryConstructorCalls() throws Exception {
|
||||||
runTest("idea/testData/slicer/outflow/defaultExplicitPrimaryConstructorCalls.kt");
|
runTest("idea/testData/slicer/outflow/defaultExplicitPrimaryConstructorCalls.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user