We must check super property usages also when analyzing inflow
This commit is contained in:
@@ -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
|
||||||
@@ -107,18 +106,6 @@ abstract class Slicer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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
@@ -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>
|
||||||
@@ -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>
|
||||||
+10
@@ -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");
|
||||||
|
|||||||
+10
@@ -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");
|
||||||
|
|||||||
Reference in New Issue
Block a user