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.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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+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");
|
||||
}
|
||||
|
||||
@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");
|
||||
|
||||
+5
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user