diff --git a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt index b5ab9478711..a567241bc7a 100644 --- a/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt +++ b/idea/src/org/jetbrains/kotlin/idea/findUsages/findUsageUtils.kt @@ -36,7 +36,7 @@ import org.jetbrains.kotlin.psi.KtPsiUtil import org.jetbrains.kotlin.utils.SmartList fun PsiElement.processAllExactUsages( - options: () -> FindUsagesOptions, + options: FindUsagesOptions, processor: (UsageInfo) -> Unit ) { fun elementsToCheckReferenceAgainst(reference: PsiReference): List { @@ -62,7 +62,7 @@ fun PsiElement.processAllExactUsages( } true }, - options() + options ) } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt index 47be93f379b..38a4ddc8161 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -28,14 +28,18 @@ import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.* import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction import org.jetbrains.kotlin.cfg.pseudocodeTraverser.TraversalOrder import org.jetbrains.kotlin.cfg.pseudocodeTraverser.traverse -import org.jetbrains.kotlin.descriptors.* +import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor +import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource +import org.jetbrains.kotlin.descriptors.SourceElement +import org.jetbrains.kotlin.descriptors.VariableDescriptorWithAccessors import org.jetbrains.kotlin.descriptors.impl.SyntheticFieldDescriptor import org.jetbrains.kotlin.idea.caches.resolve.* -import org.jetbrains.kotlin.idea.core.isOverridable +import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages +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 @@ -51,9 +55,6 @@ import org.jetbrains.kotlin.resolve.BindingContext import org.jetbrains.kotlin.resolve.DescriptorUtils import org.jetbrains.kotlin.resolve.calls.model.DefaultValueArgument import org.jetbrains.kotlin.resolve.calls.model.ExpressionValueArgument -import org.jetbrains.kotlin.resolve.calls.model.ResolvedCall -import org.jetbrains.kotlin.resolve.calls.model.ResolvedValueArgument -import org.jetbrains.kotlin.resolve.calls.tower.NewResolvedCallImpl import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver import org.jetbrains.kotlin.resolve.source.getPsi @@ -79,17 +80,23 @@ private fun KtDeclaration.processHierarchyUpward(scope: AnalysisScope, processor .forEach(processor) } -private fun KtFunction.processCalls(scope: SearchScope, processor: (UsageInfo) -> Unit) { - processAllExactUsages( - { - KotlinFunctionFindUsagesOptions(project).apply { - isSearchForTextOccurrences = false - isSkipImportStatements = true - searchScope = scope.intersectWith(useScope) +private fun KtFunction.processCalls(scope: SearchScope, exactFunctionOnly: Boolean, processor: (UsageInfo) -> Unit) { + val options = KotlinFunctionFindUsagesOptions(project).apply { + isSearchForTextOccurrences = false + isSkipImportStatements = true + searchScope = scope.intersectWith(useScope) + } + if (exactFunctionOnly) { + processAllExactUsages(options, processor) + } else { + val descriptor = unsafeResolveToDescriptor() as? CallableMemberDescriptor ?: return + for (superDescriptor in descriptor.getDeepestSuperDeclarations()) { + when (val declaration = superDescriptor.originalSource.getPsi()) { + is KtDeclaration -> declaration.processAllUsages(options, processor) + else -> {} //TODO } - }, - processor - ) + } + } } private enum class AccessKind { @@ -102,16 +109,13 @@ private fun KtDeclaration.processVariableAccesses( processor: (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) - } + 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 ) @@ -231,7 +235,6 @@ class InflowSlicer( if (!canProcess()) return val function = ownerFunction ?: return - if (function.isOverridable()) return if (function is KtPropertyAccessor && function.isSetter) { function.property.processPropertyAssignments() @@ -251,7 +254,7 @@ class InflowSlicer( val parameterDescriptor = resolveToParameterDescriptorIfAny(BodyResolveMode.FULL) ?: return - (function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope()) body@{ + (function as? KtFunction)?.processCalls(parentUsage.scope.toSearchScope(), exactFunctionOnly = false) body@{ val refElement = it.element ?: return@body val refParent = refElement.parent @@ -259,8 +262,9 @@ class InflowSlicer( refElement is KtExpression -> { val callElement = refElement.getParentOfTypeAndBranch { calleeExpression } ?: return@body val resolvedCall = callElement.resolveToCall() ?: return@body - val valueArguments = resolvedCall.originalValueArguments - when (val resolvedArgument = valueArguments[parameterDescriptor] ?: return@body) { + val callParameterDescriptor = resolvedCall.resultingDescriptor.valueParameters[parameterDescriptor.index] + val resolvedArgument = resolvedCall.valueArguments[callParameterDescriptor] ?: return@body + when (resolvedArgument) { is DefaultValueArgument -> defaultValue is ExpressionValueArgument -> resolvedArgument.valueArgument?.getArgumentExpression() else -> null @@ -431,7 +435,7 @@ class OutflowSlicer( if (this is KtConstructor<*> || this is KtNamedFunction && name != null) { processHierarchyUpward(parentUsage.scope) { if (this is KtFunction) { - processCalls(parentUsage.scope.toSearchScope()) { + processCalls(parentUsage.scope.toSearchScope(), exactFunctionOnly = true) { when (val refElement = it.element) { null -> (it.reference as? LightMemberReference)?.element?.passToProcessor() is KtExpression -> { @@ -536,17 +540,3 @@ private val DeclarationDescriptorWithSource.originalSource: SourceElement } return descriptor.source } - -private val ResolvedCall<*>.originalValueArguments: Map - get() = when (this) { - is NewResolvedCallImpl<*> -> LinkedHashMap().also { - for ((valueParameter, argument) in valueArguments) { - var parameter = valueParameter - while (parameter != parameter.original) { - parameter = parameter.original - } - it[parameter] = argument - } - } - else -> valueArguments - } \ No newline at end of file diff --git a/idea/testData/slicer/inflow/abstractFun.kt b/idea/testData/slicer/inflow/abstractFun.kt new file mode 100644 index 00000000000..9238be97f74 --- /dev/null +++ b/idea/testData/slicer/inflow/abstractFun.kt @@ -0,0 +1,18 @@ +// FLOW: IN + +interface I { + fun foo(p: Int) +} + +class C : I { + override fun foo(p: Int) { + } + + fun f() { + foo(1) + } +} + +fun f(i: I) { + i.foo(2) +} diff --git a/idea/testData/slicer/inflow/abstractFun.leafGroups.txt b/idea/testData/slicer/inflow/abstractFun.leafGroups.txt new file mode 100644 index 00000000000..c27d13bd50f --- /dev/null +++ b/idea/testData/slicer/inflow/abstractFun.leafGroups.txt @@ -0,0 +1,8 @@ +12 foo(1) +4 fun foo(p: Int) +12 foo(1) + +17 i.foo(2) +4 fun foo(p: Int) +17 i.foo(2) + diff --git a/idea/testData/slicer/inflow/abstractFun.nullnessGroups.txt b/idea/testData/slicer/inflow/abstractFun.nullnessGroups.txt new file mode 100644 index 00000000000..8f0046bd5c6 --- /dev/null +++ b/idea/testData/slicer/inflow/abstractFun.nullnessGroups.txt @@ -0,0 +1,3 @@ +[NotNull Values] +4 fun foo(p: Int) +4 fun foo(p: Int) diff --git a/idea/testData/slicer/inflow/abstractFun.results.txt b/idea/testData/slicer/inflow/abstractFun.results.txt new file mode 100644 index 00000000000..d2d4c3e8ad6 --- /dev/null +++ b/idea/testData/slicer/inflow/abstractFun.results.txt @@ -0,0 +1,3 @@ +4 fun foo(p: Int) +12 foo(1) +17 i.foo(2) diff --git a/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.leafGroups.txt b/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.leafGroups.txt index 438da5a8c89..f5afba2db22 100644 --- a/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.leafGroups.txt +++ b/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.leafGroups.txt @@ -1,8 +1,9 @@ -4 fun bar(n: Int) = n +5 val x = (::bar)(1) 5 val x = (::bar)(1) 5 val x = (::bar)(1) 5 [LAMBDA] val x = (::bar)(1) 4 fun bar(n: Int) = n 4 fun bar(n: Int) = n 4 fun bar(n: Int) = n +5 val x = (::bar)(1) diff --git a/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.results.txt b/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.results.txt index aae08af218e..2de187b773f 100644 --- a/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.results.txt +++ b/idea/testData/slicer/inflow/funResultViaCallableRefWithDirectCall.results.txt @@ -4,3 +4,4 @@ 4 fun bar(n: Int) = n 4 fun bar(n: Int) = n 4 fun bar(n: Int) = n +5 val x = (::bar)(1) diff --git a/idea/testData/slicer/inflow/overrideFun.kt b/idea/testData/slicer/inflow/overrideFun.kt new file mode 100644 index 00000000000..69091246e22 --- /dev/null +++ b/idea/testData/slicer/inflow/overrideFun.kt @@ -0,0 +1,19 @@ +// FLOW: IN + +interface I { + fun foo(p: Int) +} + +class C : I { + override fun foo(p: Int) { + println(p) + } + + fun f() { + foo(1) + } +} + +fun f(i: I) { + i.foo(2) +} diff --git a/idea/testData/slicer/inflow/overrideFun.leafGroups.txt b/idea/testData/slicer/inflow/overrideFun.leafGroups.txt new file mode 100644 index 00000000000..84ceab6cb70 --- /dev/null +++ b/idea/testData/slicer/inflow/overrideFun.leafGroups.txt @@ -0,0 +1,10 @@ +13 foo(1) +9 println(p) +8 override fun foo(p: Int) { +13 foo(1) + +18 i.foo(2) +9 println(p) +8 override fun foo(p: Int) { +18 i.foo(2) + diff --git a/idea/testData/slicer/inflow/overrideFun.nullnessGroups.txt b/idea/testData/slicer/inflow/overrideFun.nullnessGroups.txt new file mode 100644 index 00000000000..3756dff8a11 --- /dev/null +++ b/idea/testData/slicer/inflow/overrideFun.nullnessGroups.txt @@ -0,0 +1,3 @@ +[NotNull Values] +9 println(p) +9 println(p) diff --git a/idea/testData/slicer/inflow/overrideFun.results.txt b/idea/testData/slicer/inflow/overrideFun.results.txt new file mode 100644 index 00000000000..11a785941c4 --- /dev/null +++ b/idea/testData/slicer/inflow/overrideFun.results.txt @@ -0,0 +1,4 @@ +9 println(p) +8 override fun foo(p: Int) { +13 foo(1) +18 i.foo(2) diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java index 19c38fea6de..84cae3e84a8 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java @@ -24,6 +24,11 @@ public class SlicerLeafGroupingTestGenerated extends AbstractSlicerLeafGroupingT KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("abstractFun.kt") + public void testAbstractFun() throws Exception { + runTest("idea/testData/slicer/inflow/abstractFun.kt"); + } + public void testAllFilesPresentInInflow() throws Exception { KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClassWithExcluded(this.getClass(), new File("idea/testData/slicer/inflow"), Pattern.compile("^(.+)\\.kt$"), null); } @@ -223,6 +228,11 @@ public class SlicerLeafGroupingTestGenerated extends AbstractSlicerLeafGroupingT runTest("idea/testData/slicer/inflow/nullsAndNotNulls.kt"); } + @TestMetadata("overrideFun.kt") + public void testOverrideFun() throws Exception { + runTest("idea/testData/slicer/inflow/overrideFun.kt"); + } + @TestMetadata("overridingFunctionResult.kt") public void testOverridingFunctionResult() throws Exception { runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java index 7f135c968f8..e8d8d17ec09 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java @@ -24,6 +24,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG KotlinTestUtils.runTest(this::doTest, this, testDataFilePath); } + @TestMetadata("abstractFun.kt") + public void testAbstractFun() throws Exception { + runTest("idea/testData/slicer/inflow/abstractFun.kt"); + } + public void testAllFilesPresentInInflow() throws Exception { KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClassWithExcluded(this.getClass(), new File("idea/testData/slicer/inflow"), Pattern.compile("^(.+)\\.kt$"), null); } @@ -223,6 +228,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG runTest("idea/testData/slicer/inflow/nullsAndNotNulls.kt"); } + @TestMetadata("overrideFun.kt") + public void testOverrideFun() throws Exception { + runTest("idea/testData/slicer/inflow/overrideFun.kt"); + } + @TestMetadata("overridingFunctionResult.kt") public void testOverridingFunctionResult() throws Exception { runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java index fe7368b308f..54b3022322b 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java @@ -28,6 +28,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest { KotlinTestUtils.assertAllTestsPresentInSingleGeneratedClassWithExcluded(this.getClass(), new File("idea/testData/slicer"), Pattern.compile("^(.+)\\.kt$"), null); } + @TestMetadata("inflow/abstractFun.kt") + public void testInflow_AbstractFun() throws Exception { + runTest("idea/testData/slicer/inflow/abstractFun.kt"); + } + @TestMetadata("inflow/anonymousFunBodyExpression.kt") public void testInflow_AnonymousFunBodyExpression() throws Exception { runTest("idea/testData/slicer/inflow/anonymousFunBodyExpression.kt"); @@ -223,6 +228,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest { runTest("idea/testData/slicer/inflow/nullsAndNotNulls.kt"); } + @TestMetadata("inflow/overrideFun.kt") + public void testInflow_OverrideFun() throws Exception { + runTest("idea/testData/slicer/inflow/overrideFun.kt"); + } + @TestMetadata("inflow/overridingFunctionResult.kt") public void testInflow_OverridingFunctionResult() throws Exception { runTest("idea/testData/slicer/inflow/overridingFunctionResult.kt");