diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt index fc8d9073c57..a27a236dea7 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt @@ -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 diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt index c4ec4884b1e..44f16015713 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt @@ -21,26 +21,24 @@ class OutflowSlicer( processor: Processor, 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() diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt index c40c91262b7..1759346aa06 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -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 diff --git a/idea/testData/slicer/inflow/overrideProperty.kt b/idea/testData/slicer/inflow/overrideProperty.kt new file mode 100644 index 00000000000..6d56b3f85cb --- /dev/null +++ b/idea/testData/slicer/inflow/overrideProperty.kt @@ -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 v = d.prop +} diff --git a/idea/testData/slicer/inflow/overrideProperty.leafGroups.txt b/idea/testData/slicer/inflow/overrideProperty.leafGroups.txt new file mode 100644 index 00000000000..71ad7fd0000 --- /dev/null +++ b/idea/testData/slicer/inflow/overrideProperty.leafGroups.txt @@ -0,0 +1,11 @@ +8 override var prop: Int = 1 +13 val v = d.prop +13 val v = d.prop +8 override var prop: Int = 1 +8 override var prop: Int = 1 + +12 b.prop = 10 +13 val v = d.prop +13 val v = d.prop +8 override var prop: Int = 1 +12 b.prop = 10 diff --git a/idea/testData/slicer/inflow/overrideProperty.nullnessGroups.txt b/idea/testData/slicer/inflow/overrideProperty.nullnessGroups.txt new file mode 100644 index 00000000000..9fb76243a49 --- /dev/null +++ b/idea/testData/slicer/inflow/overrideProperty.nullnessGroups.txt @@ -0,0 +1,3 @@ +[NotNull Values] +13 val v = d.prop +13 val v = d.prop diff --git a/idea/testData/slicer/inflow/overrideProperty.results.txt b/idea/testData/slicer/inflow/overrideProperty.results.txt new file mode 100644 index 00000000000..3c1e88d32e7 --- /dev/null +++ b/idea/testData/slicer/inflow/overrideProperty.results.txt @@ -0,0 +1,5 @@ +13 val v = d.prop +13 val v = d.prop +8 override var prop: Int = 1 +8 override var prop: Int = 1 +12 b.prop = 10 diff --git a/idea/testData/slicer/inflow/propertyInInterface.kt b/idea/testData/slicer/inflow/propertyInInterface.kt new file mode 100644 index 00000000000..42c4dbf230b --- /dev/null +++ b/idea/testData/slicer/inflow/propertyInInterface.kt @@ -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 v = c.prop +} diff --git a/idea/testData/slicer/inflow/propertyInInterface.leafGroups.txt b/idea/testData/slicer/inflow/propertyInInterface.leafGroups.txt new file mode 100644 index 00000000000..c4a451fa46a --- /dev/null +++ b/idea/testData/slicer/inflow/propertyInInterface.leafGroups.txt @@ -0,0 +1,11 @@ +8 override var prop: Int = 0 +13 val v = c.prop +13 val v = c.prop +8 override var prop: Int = 0 +8 override var prop: Int = 0 + +12 i.prop = 10 +13 val v = c.prop +13 val v = c.prop +8 override var prop: Int = 0 +12 i.prop = 10 diff --git a/idea/testData/slicer/inflow/propertyInInterface.nullnessGroups.txt b/idea/testData/slicer/inflow/propertyInInterface.nullnessGroups.txt new file mode 100644 index 00000000000..6d866c04281 --- /dev/null +++ b/idea/testData/slicer/inflow/propertyInInterface.nullnessGroups.txt @@ -0,0 +1,3 @@ +[NotNull Values] +13 val v = c.prop +13 val v = c.prop diff --git a/idea/testData/slicer/inflow/propertyInInterface.results.txt b/idea/testData/slicer/inflow/propertyInInterface.results.txt new file mode 100644 index 00000000000..1f1df3a99f6 --- /dev/null +++ b/idea/testData/slicer/inflow/propertyInInterface.results.txt @@ -0,0 +1,5 @@ +13 val v = c.prop +13 val v = c.prop +8 override var prop: Int = 0 +8 override var prop: Int = 0 +12 i.prop = 10 diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java index 981188e13b1..4b062509243 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java @@ -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"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java index e7a54679fe4..7375bbe6dd2 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java @@ -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"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java index 9f296edb804..f7d95ea677a 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java @@ -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");