We must check super property usages also when analyzing inflow

This commit is contained in:
Valentin Kipyatkov
2020-02-10 09:32:20 +02:00
parent 612fd6d1bf
commit b1cd56ba91
14 changed files with 138 additions and 48 deletions
@@ -48,9 +48,7 @@ class InflowSlicer(
private fun PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true)
private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) {
processVariableAccesses(accessSearchScope,
AccessKind.WRITE_WITH_OPTIONAL_READ
) body@{
processVariableAccesses(accessSearchScope, AccessKind.WRITE_WITH_OPTIONAL_READ) body@{
val refElement = it.element ?: return@body
val refParent = refElement.parent
@@ -21,26 +21,24 @@ class OutflowSlicer(
processor: Processor<SliceUsage>,
parentUsage: KotlinSliceUsage
) : Slicer(element, processor, parentUsage) {
private fun KtDeclaration.processVariable() {
processHierarchyUpward(parentUsage.scope) {
if (this is KtParameter && !canProcess()) return@processHierarchyUpward
private fun KtCallableDeclaration.processVariable() {
if (this is KtParameter && !canProcess()) return
val withDereferences = parentUsage.params.showInstanceDereferences
val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY
(this as? KtDeclaration)?.processVariableAccesses(parentUsage.scope.toSearchScope(), accessKind) body@{
val refElement = it.element
if (refElement !is KtExpression) {
refElement?.passToProcessor()
return@body
}
val withDereferences = parentUsage.params.showInstanceDereferences
val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY
processVariableAccesses(parentUsage.scope.toSearchScope(), accessKind) body@{
val refElement = it.element
if (refElement !is KtExpression) {
refElement?.passToProcessor()
return@body
}
val refExpression = KtPsiUtil.safeDeparenthesize(refElement)
if (withDereferences) {
refExpression.processDereferences()
}
if (!withDereferences || KotlinReadWriteAccessDetector.INSTANCE.getExpressionAccess(refExpression) == ReadWriteAccessDetector.Access.Read) {
refExpression.passToProcessor()
}
val refExpression = KtPsiUtil.safeDeparenthesize(refElement)
if (withDereferences) {
refExpression.processDereferences()
}
if (!withDereferences || KotlinReadWriteAccessDetector.INSTANCE.getExpressionAccess(refExpression) == ReadWriteAccessDetector.Access.Read) {
refExpression.passToProcessor()
}
}
}
@@ -152,7 +150,8 @@ class OutflowSlicer(
if (parentUsage.forcedExpressionMode) return element.processExpression()
when (element) {
is KtProperty, is KtParameter -> (element as KtDeclaration).processVariable()
is KtProperty -> element.processVariable()
is KtParameter -> element.processVariable()
is KtFunction -> element.processFunction()
is KtPropertyAccessor -> if (element.isGetter) {
element.property.processVariable()
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.idea.slicer
import com.intellij.analysis.AnalysisScope
import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod
import com.intellij.psi.search.SearchScope
@@ -106,19 +105,7 @@ abstract class Slicer(
it.namedUnwrappedElement?.processor()
}
}
protected fun KtDeclaration.processHierarchyUpward(scope: AnalysisScope, processor: PsiElement.() -> Unit) {
processor()
val descriptor = unsafeResolveToDescriptor() as? CallableMemberDescriptor ?: return
DescriptorUtils
.getAllOverriddenDescriptors(descriptor)
.asSequence()
.mapNotNull { it.originalSource.getPsi() }
.filter { scope.contains(it) }
.toList()
.forEach(processor)
}
protected enum class AccessKind {
READ_ONLY, WRITE_ONLY, WRITE_WITH_OPTIONAL_READ, READ_OR_WRITE
}
@@ -126,19 +113,29 @@ abstract class Slicer(
protected fun KtDeclaration.processVariableAccesses(
scope: SearchScope,
kind: AccessKind,
processor: (UsageInfo) -> Unit
usageProcessor: (UsageInfo) -> Unit
) {
processAllExactUsages(
KotlinPropertyFindUsagesOptions(project).apply {
isReadAccess = kind == AccessKind.READ_ONLY || kind == AccessKind.READ_OR_WRITE
isWriteAccess = kind == AccessKind.WRITE_ONLY || kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE
isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE
isSearchForTextOccurrences = false
isSkipImportStatements = true
searchScope = scope.intersectWith(useScope)
},
processor
)
val allDeclarations = mutableListOf(this)
val descriptor = unsafeResolveToDescriptor()
if (descriptor is CallableMemberDescriptor) {
DescriptorUtils.getAllOverriddenDeclarations(descriptor).mapNotNullTo(allDeclarations) {
it.originalSource.getPsi() as? KtDeclaration
}
}
for (declaration in allDeclarations) {
declaration.processAllExactUsages(
KotlinPropertyFindUsagesOptions(project).apply {
isReadAccess = kind == AccessKind.READ_ONLY || kind == AccessKind.READ_OR_WRITE
isWriteAccess = kind == AccessKind.WRITE_ONLY || kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE
isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE
isSearchForTextOccurrences = false
isSkipImportStatements = true
searchScope = scope.intersectWith(declaration.useScope)
},
usageProcessor
)
}
}
protected fun KtParameter.canProcess() = !isVarArg
+14
View File
@@ -0,0 +1,14 @@
// FLOW: IN
open class Base {
open var prop: Int = 0
}
class Derived : Base() {
override var prop: Int = 1
}
fun foo(b: Base, d: Derived) {
b.prop = 10
val <caret>v = d.prop
}
@@ -0,0 +1,11 @@
8 override var prop: Int = <bold>1</bold>
13 val <bold>v = d.prop</bold>
13 val v = <bold>d.prop</bold>
8 override var <bold>prop: Int = 1</bold>
8 override var prop: Int = <bold>1</bold>
12 b.prop = <bold>10</bold>
13 val <bold>v = d.prop</bold>
13 val v = <bold>d.prop</bold>
8 override var <bold>prop: Int = 1</bold>
12 b.prop = <bold>10</bold>
@@ -0,0 +1,3 @@
[NotNull Values]
13 val <bold>v = d.prop</bold>
13 val <bold>v = d.prop</bold>
@@ -0,0 +1,5 @@
13 val <bold>v = d.prop</bold>
13 val v = <bold>d.prop</bold>
8 override var <bold>prop: Int = 1</bold>
8 override var prop: Int = <bold>1</bold>
12 b.prop = <bold>10</bold>
+14
View File
@@ -0,0 +1,14 @@
// FLOW: IN
interface I {
var prop: Int
}
class C : I {
override var prop: Int = 0
}
fun foo(i: I, c: C) {
i.prop = 10
val <caret>v = c.prop
}
@@ -0,0 +1,11 @@
8 override var prop: Int = <bold>0</bold>
13 val <bold>v = c.prop</bold>
13 val v = <bold>c.prop</bold>
8 override var <bold>prop: Int = 0</bold>
8 override var prop: Int = <bold>0</bold>
12 i.prop = <bold>10</bold>
13 val <bold>v = c.prop</bold>
13 val v = <bold>c.prop</bold>
8 override var <bold>prop: Int = 0</bold>
12 i.prop = <bold>10</bold>
@@ -0,0 +1,3 @@
[NotNull Values]
13 val <bold>v = c.prop</bold>
13 val <bold>v = c.prop</bold>
@@ -0,0 +1,5 @@
13 val <bold>v = c.prop</bold>
13 val v = <bold>c.prop</bold>
8 override var <bold>prop: Int = 0</bold>
8 override var prop: Int = <bold>0</bold>
12 i.prop = <bold>10</bold>
@@ -243,6 +243,11 @@ public class SlicerLeafGroupingTestGenerated extends AbstractSlicerLeafGroupingT
runTest("idea/testData/slicer/inflow/overrideFun.kt");
}
@TestMetadata("overrideProperty.kt")
public void testOverrideProperty() throws Exception {
runTest("idea/testData/slicer/inflow/overrideProperty.kt");
}
@TestMetadata("overridingFunctionResult.kt")
public void testOverridingFunctionResult() throws Exception {
runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt");
@@ -273,6 +278,11 @@ public class SlicerLeafGroupingTestGenerated extends AbstractSlicerLeafGroupingT
runTest("idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt");
}
@TestMetadata("propertyInInterface.kt")
public void testPropertyInInterface() throws Exception {
runTest("idea/testData/slicer/inflow/propertyInInterface.kt");
}
@TestMetadata("qualifiedAssignmentsForQualifiedRef.kt")
public void testQualifiedAssignmentsForQualifiedRef() throws Exception {
runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt");
@@ -243,6 +243,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG
runTest("idea/testData/slicer/inflow/overrideFun.kt");
}
@TestMetadata("overrideProperty.kt")
public void testOverrideProperty() throws Exception {
runTest("idea/testData/slicer/inflow/overrideProperty.kt");
}
@TestMetadata("overridingFunctionResult.kt")
public void testOverridingFunctionResult() throws Exception {
runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt");
@@ -273,6 +278,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG
runTest("idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt");
}
@TestMetadata("propertyInInterface.kt")
public void testPropertyInInterface() throws Exception {
runTest("idea/testData/slicer/inflow/propertyInInterface.kt");
}
@TestMetadata("qualifiedAssignmentsForQualifiedRef.kt")
public void testQualifiedAssignmentsForQualifiedRef() throws Exception {
runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt");
@@ -243,6 +243,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
runTest("idea/testData/slicer/inflow/overrideFun.kt");
}
@TestMetadata("inflow/overrideProperty.kt")
public void testInflow_OverrideProperty() throws Exception {
runTest("idea/testData/slicer/inflow/overrideProperty.kt");
}
@TestMetadata("inflow/overridingFunctionResult.kt")
public void testInflow_OverridingFunctionResult() throws Exception {
runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt");
@@ -273,6 +278,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
runTest("idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt");
}
@TestMetadata("inflow/propertyInInterface.kt")
public void testInflow_PropertyInInterface() throws Exception {
runTest("idea/testData/slicer/inflow/propertyInInterface.kt");
}
@TestMetadata("inflow/qualifiedAssignmentsForQualifiedRef.kt")
public void testInflow_QualifiedAssignmentsForQualifiedRef() throws Exception {
runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt");