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 PsiElement.passToProcessorAsValue(lambdaLevel: Int = parentUsage.lambdaLevel) = passToProcessor(lambdaLevel, true)
private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) { private fun KtDeclaration.processAssignments(accessSearchScope: SearchScope) {
processVariableAccesses(accessSearchScope, processVariableAccesses(accessSearchScope, AccessKind.WRITE_WITH_OPTIONAL_READ) body@{
AccessKind.WRITE_WITH_OPTIONAL_READ
) body@{
val refElement = it.element ?: return@body val refElement = it.element ?: return@body
val refParent = refElement.parent val refParent = refElement.parent
@@ -21,26 +21,24 @@ class OutflowSlicer(
processor: Processor<SliceUsage>, processor: Processor<SliceUsage>,
parentUsage: KotlinSliceUsage parentUsage: KotlinSliceUsage
) : Slicer(element, processor, parentUsage) { ) : Slicer(element, processor, parentUsage) {
private fun KtDeclaration.processVariable() { private fun KtCallableDeclaration.processVariable() {
processHierarchyUpward(parentUsage.scope) { if (this is KtParameter && !canProcess()) return
if (this is KtParameter && !canProcess()) return@processHierarchyUpward
val withDereferences = parentUsage.params.showInstanceDereferences val withDereferences = parentUsage.params.showInstanceDereferences
val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY val accessKind = if (withDereferences) AccessKind.READ_OR_WRITE else AccessKind.READ_ONLY
(this as? KtDeclaration)?.processVariableAccesses(parentUsage.scope.toSearchScope(), accessKind) body@{ processVariableAccesses(parentUsage.scope.toSearchScope(), accessKind) body@{
val refElement = it.element val refElement = it.element
if (refElement !is KtExpression) { if (refElement !is KtExpression) {
refElement?.passToProcessor() refElement?.passToProcessor()
return@body return@body
} }
val refExpression = KtPsiUtil.safeDeparenthesize(refElement) val refExpression = KtPsiUtil.safeDeparenthesize(refElement)
if (withDereferences) { if (withDereferences) {
refExpression.processDereferences() refExpression.processDereferences()
} }
if (!withDereferences || KotlinReadWriteAccessDetector.INSTANCE.getExpressionAccess(refExpression) == ReadWriteAccessDetector.Access.Read) { if (!withDereferences || KotlinReadWriteAccessDetector.INSTANCE.getExpressionAccess(refExpression) == ReadWriteAccessDetector.Access.Read) {
refExpression.passToProcessor() refExpression.passToProcessor()
}
} }
} }
} }
@@ -152,7 +150,8 @@ class OutflowSlicer(
if (parentUsage.forcedExpressionMode) return element.processExpression() if (parentUsage.forcedExpressionMode) return element.processExpression()
when (element) { when (element) {
is KtProperty, is KtParameter -> (element as KtDeclaration).processVariable() is KtProperty -> element.processVariable()
is KtParameter -> element.processVariable()
is KtFunction -> element.processFunction() is KtFunction -> element.processFunction()
is KtPropertyAccessor -> if (element.isGetter) { is KtPropertyAccessor -> if (element.isGetter) {
element.property.processVariable() element.property.processVariable()
@@ -5,7 +5,6 @@
package org.jetbrains.kotlin.idea.slicer package org.jetbrains.kotlin.idea.slicer
import com.intellij.analysis.AnalysisScope
import com.intellij.psi.PsiElement import com.intellij.psi.PsiElement
import com.intellij.psi.PsiMethod import com.intellij.psi.PsiMethod
import com.intellij.psi.search.SearchScope import com.intellij.psi.search.SearchScope
@@ -106,19 +105,7 @@ abstract class Slicer(
it.namedUnwrappedElement?.processor() 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 { protected enum class AccessKind {
READ_ONLY, WRITE_ONLY, WRITE_WITH_OPTIONAL_READ, READ_OR_WRITE READ_ONLY, WRITE_ONLY, WRITE_WITH_OPTIONAL_READ, READ_OR_WRITE
} }
@@ -126,19 +113,29 @@ abstract class Slicer(
protected fun KtDeclaration.processVariableAccesses( protected fun KtDeclaration.processVariableAccesses(
scope: SearchScope, scope: SearchScope,
kind: AccessKind, kind: AccessKind,
processor: (UsageInfo) -> Unit usageProcessor: (UsageInfo) -> Unit
) { ) {
processAllExactUsages( val allDeclarations = mutableListOf(this)
KotlinPropertyFindUsagesOptions(project).apply { val descriptor = unsafeResolveToDescriptor()
isReadAccess = kind == AccessKind.READ_ONLY || kind == AccessKind.READ_OR_WRITE if (descriptor is CallableMemberDescriptor) {
isWriteAccess = kind == AccessKind.WRITE_ONLY || kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE DescriptorUtils.getAllOverriddenDeclarations(descriptor).mapNotNullTo(allDeclarations) {
isReadWriteAccess = kind == AccessKind.WRITE_WITH_OPTIONAL_READ || kind == AccessKind.READ_OR_WRITE it.originalSource.getPsi() as? KtDeclaration
isSearchForTextOccurrences = false }
isSkipImportStatements = true }
searchScope = scope.intersectWith(useScope)
}, for (declaration in allDeclarations) {
processor 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 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"); 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") @TestMetadata("overridingFunctionResult.kt")
public void testOverridingFunctionResult() throws Exception { public void testOverridingFunctionResult() throws Exception {
runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt"); runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt");
@@ -273,6 +278,11 @@ public class SlicerLeafGroupingTestGenerated extends AbstractSlicerLeafGroupingT
runTest("idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt"); 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") @TestMetadata("qualifiedAssignmentsForQualifiedRef.kt")
public void testQualifiedAssignmentsForQualifiedRef() throws Exception { public void testQualifiedAssignmentsForQualifiedRef() throws Exception {
runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt"); runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt");
@@ -243,6 +243,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG
runTest("idea/testData/slicer/inflow/overrideFun.kt"); 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") @TestMetadata("overridingFunctionResult.kt")
public void testOverridingFunctionResult() throws Exception { public void testOverridingFunctionResult() throws Exception {
runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt"); runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt");
@@ -273,6 +278,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG
runTest("idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt"); 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") @TestMetadata("qualifiedAssignmentsForQualifiedRef.kt")
public void testQualifiedAssignmentsForQualifiedRef() throws Exception { public void testQualifiedAssignmentsForQualifiedRef() throws Exception {
runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt"); runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt");
@@ -243,6 +243,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
runTest("idea/testData/slicer/inflow/overrideFun.kt"); 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") @TestMetadata("inflow/overridingFunctionResult.kt")
public void testInflow_OverridingFunctionResult() throws Exception { public void testInflow_OverridingFunctionResult() throws Exception {
runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt"); runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt");
@@ -273,6 +278,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
runTest("idea/testData/slicer/inflow/primaryConstructorParameterWithDefault.kt"); 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") @TestMetadata("inflow/qualifiedAssignmentsForQualifiedRef.kt")
public void testInflow_QualifiedAssignmentsForQualifiedRef() throws Exception { public void testInflow_QualifiedAssignmentsForQualifiedRef() throws Exception {
runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt"); runTest("idea/testData/slicer/inflow/qualifiedAssignmentsForQualifiedRef.kt");