Analyze Data Flow to Here to work with virtual methods
This commit is contained in:
@@ -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<PsiElement> {
|
||||
@@ -62,7 +62,7 @@ fun PsiElement.processAllExactUsages(
|
||||
}
|
||||
true
|
||||
},
|
||||
options()
|
||||
options
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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<KtCallElement> { 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<ValueParameterDescriptor, ResolvedValueArgument>
|
||||
get() = when (this) {
|
||||
is NewResolvedCallImpl<*> -> LinkedHashMap<ValueParameterDescriptor, ResolvedValueArgument>().also {
|
||||
for ((valueParameter, argument) in valueArguments) {
|
||||
var parameter = valueParameter
|
||||
while (parameter != parameter.original) {
|
||||
parameter = parameter.original
|
||||
}
|
||||
it[parameter] = argument
|
||||
}
|
||||
}
|
||||
else -> valueArguments
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// FLOW: IN
|
||||
|
||||
interface I {
|
||||
fun foo(<caret>p: Int)
|
||||
}
|
||||
|
||||
class C : I {
|
||||
override fun foo(p: Int) {
|
||||
}
|
||||
|
||||
fun f() {
|
||||
foo(1)
|
||||
}
|
||||
}
|
||||
|
||||
fun f(i: I) {
|
||||
i.foo(2)
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
12 foo(<bold>1</bold>)
|
||||
4 fun foo(<bold>p: Int</bold>)
|
||||
12 foo(<bold>1</bold>)
|
||||
|
||||
17 i.foo(<bold>2</bold>)
|
||||
4 fun foo(<bold>p: Int</bold>)
|
||||
17 i.foo(<bold>2</bold>)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[NotNull Values]
|
||||
4 fun foo(<bold>p: Int</bold>)
|
||||
4 fun foo(<bold>p: Int</bold>)
|
||||
@@ -0,0 +1,3 @@
|
||||
4 fun foo(<bold>p: Int</bold>)
|
||||
12 foo(<bold>1</bold>)
|
||||
17 i.foo(<bold>2</bold>)
|
||||
+2
-1
@@ -1,8 +1,9 @@
|
||||
4 fun bar(<bold>n: Int</bold>) = n
|
||||
5 val x = (::bar)(<bold>1</bold>)
|
||||
5 val <bold>x = (::bar)(1)</bold>
|
||||
5 val x = <bold>(::bar)(1)</bold>
|
||||
5 [LAMBDA] val x = <bold>(::bar)</bold>(1)
|
||||
4 fun <bold>bar(n: Int) = n</bold>
|
||||
4 fun bar(n: Int) = <bold>n</bold>
|
||||
4 fun bar(<bold>n: Int</bold>) = n
|
||||
5 val x = (::bar)(<bold>1</bold>)
|
||||
|
||||
|
||||
@@ -4,3 +4,4 @@
|
||||
4 fun <bold>bar(n: Int) = n</bold>
|
||||
4 fun bar(n: Int) = <bold>n</bold>
|
||||
4 fun bar(<bold>n: Int</bold>) = n
|
||||
5 val x = (::bar)(<bold>1</bold>)
|
||||
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// FLOW: IN
|
||||
|
||||
interface I {
|
||||
fun foo(p: Int)
|
||||
}
|
||||
|
||||
class C : I {
|
||||
override fun foo(p: Int) {
|
||||
println(<caret>p)
|
||||
}
|
||||
|
||||
fun f() {
|
||||
foo(1)
|
||||
}
|
||||
}
|
||||
|
||||
fun f(i: I) {
|
||||
i.foo(2)
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
13 foo(<bold>1</bold>)
|
||||
9 println(<bold>p</bold>)
|
||||
8 override fun foo(<bold>p: Int</bold>) {
|
||||
13 foo(<bold>1</bold>)
|
||||
|
||||
18 i.foo(<bold>2</bold>)
|
||||
9 println(<bold>p</bold>)
|
||||
8 override fun foo(<bold>p: Int</bold>) {
|
||||
18 i.foo(<bold>2</bold>)
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
[NotNull Values]
|
||||
9 println(<bold>p</bold>)
|
||||
9 println(<bold>p</bold>)
|
||||
@@ -0,0 +1,4 @@
|
||||
9 println(<bold>p</bold>)
|
||||
8 override fun foo(<bold>p: Int</bold>) {
|
||||
13 foo(<bold>1</bold>)
|
||||
18 i.foo(<bold>2</bold>)
|
||||
+10
@@ -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");
|
||||
|
||||
+10
@@ -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");
|
||||
|
||||
@@ -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");
|
||||
|
||||
Reference in New Issue
Block a user