Code refactoring + better handling of incoming data flow to java super methods
This commit is contained in:
@@ -7,6 +7,7 @@ package org.jetbrains.kotlin.idea.slicer
|
||||
|
||||
import com.intellij.psi.PsiCall
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
@@ -33,7 +34,6 @@ import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchReques
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
|
||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.js.resolve.diagnostics.findPsi
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||
@@ -129,25 +129,7 @@ class InflowSlicer(
|
||||
val parameterDescriptor = parameter.resolveToParameterDescriptorIfAny(BodyResolveMode.FULL) ?: return
|
||||
|
||||
if (function is KtFunction) {
|
||||
val sliceTransformer = ArgumentExpressionTransformer(parameterDescriptor)
|
||||
|
||||
when {
|
||||
function is KtFunctionLiteral -> {
|
||||
processFunctionLiteralCalls(function, sliceTransformer)
|
||||
}
|
||||
|
||||
function is KtNamedFunction && function.name == null -> {
|
||||
processAnonymousFunctionCalls(function, sliceTransformer)
|
||||
}
|
||||
|
||||
else -> {
|
||||
processCalls(function, analysisScope, includeOverriders) { usageInfo ->
|
||||
usageInfo.element?.let {
|
||||
sliceTransformer.extractArgumentExpression(it)?.passToProcessorAsValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
processCalls(function, includeOverriders, ArgumentSliceProducer(parameterDescriptor))
|
||||
}
|
||||
|
||||
if (parameter.valOrVarKeyword.toValVar() == KotlinValVar.Var) {
|
||||
@@ -156,33 +138,7 @@ class InflowSlicer(
|
||||
}
|
||||
|
||||
private fun processExtensionReceiver(declaration: KtCallableDeclaration, includeOverriders: Boolean) {
|
||||
fun processReceiver(refElement: PsiElement) {
|
||||
when (refElement) {
|
||||
is KtExpression -> {
|
||||
val resolvedCall = refElement.resolveToCall() ?: return
|
||||
when (val receiver = resolvedCall.extensionReceiver) {
|
||||
is ExpressionReceiver -> {
|
||||
receiver.expression.passToProcessorAsValue()
|
||||
}
|
||||
|
||||
is ImplicitReceiver -> {
|
||||
val callableDeclaration = (receiver.declarationDescriptor as? CallableDescriptor)?.originalSource?.getPsi()
|
||||
(callableDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
(refElement.parent as? PsiCall)?.argumentList?.expressions?.getOrNull(0)?.passToProcessorAsValue()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
processCalls(declaration, analysisScope, includeOverriders) { usageInfo ->
|
||||
usageInfo.element?.let {
|
||||
processReceiver(it)
|
||||
}
|
||||
}
|
||||
processCalls(declaration, includeOverriders, ReceiverSliceProducer)
|
||||
}
|
||||
|
||||
private fun processExpression(expression: KtExpression) {
|
||||
@@ -234,8 +190,8 @@ class InflowSlicer(
|
||||
val anonymousFunction = accessedDescriptor.containingDeclaration as? AnonymousFunctionDescriptor
|
||||
if (anonymousFunction != null && accessedDescriptor.name.asString() == "it") {
|
||||
val functionLiteral = anonymousFunction.source.getPsi() as KtFunctionLiteral
|
||||
val sliceTransformer = ArgumentExpressionTransformer(anonymousFunction.valueParameters.first())
|
||||
processFunctionLiteralCalls(functionLiteral, sliceTransformer)
|
||||
val sliceTransformer = ArgumentSliceProducer(anonymousFunction.valueParameters.first())
|
||||
processCalls(functionLiteral, false, sliceTransformer)
|
||||
}
|
||||
} else {
|
||||
accessedDeclaration.passDeclarationToProcessorWithOverriders()
|
||||
@@ -366,23 +322,27 @@ class InflowSlicer(
|
||||
}
|
||||
|
||||
@Suppress("DataClassPrivateConstructor") // we have modifier data to get equals&hashCode only
|
||||
private data class ArgumentExpressionTransformer private constructor(
|
||||
private data class ArgumentSliceProducer private constructor(
|
||||
private val parameterIndex: Int,
|
||||
private val isExtension: Boolean
|
||||
) : KotlinSliceUsageTransformer
|
||||
) : SliceProducer
|
||||
{
|
||||
constructor(parameterDescriptor: ValueParameterDescriptor) : this(
|
||||
parameterDescriptor.index,
|
||||
parameterDescriptor.containingDeclaration.isExtension
|
||||
)
|
||||
|
||||
override fun transform(usage: KotlinSliceUsage): SliceUsage? {
|
||||
val element = usage.element ?: return null
|
||||
val argumentExpression = extractArgumentExpression(element) ?: return null
|
||||
return KotlinSliceUsage(argumentExpression, usage.parent, usage.behaviour?.originalBehaviour, forcedExpressionMode = true)
|
||||
override fun produce(
|
||||
usage: UsageInfo,
|
||||
behaviour: KotlinSliceUsage.SpecialBehaviour?,
|
||||
parent: SliceUsage
|
||||
): Collection<SliceUsage>? {
|
||||
val element = usage.element ?: return emptyList()
|
||||
val argumentExpression = extractArgumentExpression(element) ?: return emptyList()
|
||||
return listOf(KotlinSliceUsage(argumentExpression, parent, behaviour, forcedExpressionMode = true))
|
||||
}
|
||||
|
||||
fun extractArgumentExpression(refElement: PsiElement): PsiElement? {
|
||||
private fun extractArgumentExpression(refElement: PsiElement): PsiElement? {
|
||||
val refParent = refElement.parent
|
||||
return when {
|
||||
refElement is KtExpression -> {
|
||||
@@ -404,8 +364,47 @@ class InflowSlicer(
|
||||
|
||||
refParent is PsiCall -> refParent.argumentList?.expressions?.getOrNull(parameterIndex + (if (isExtension) 1 else 0))
|
||||
|
||||
refElement is PsiMethod -> refElement.parameterList.parameters.getOrNull(parameterIndex + (if (isExtension) 1 else 0))
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object ReceiverSliceProducer : SliceProducer {
|
||||
override fun produce(
|
||||
usage: UsageInfo,
|
||||
behaviour: KotlinSliceUsage.SpecialBehaviour?,
|
||||
parent: SliceUsage
|
||||
): Collection<SliceUsage>? {
|
||||
val refElement = usage.element ?: return emptyList()
|
||||
when (refElement) {
|
||||
is KtExpression -> {
|
||||
val resolvedCall = refElement.resolveToCall() ?: return emptyList()
|
||||
when (val receiver = resolvedCall.extensionReceiver) {
|
||||
is ExpressionReceiver -> {
|
||||
return listOf(KotlinSliceUsage(receiver.expression, parent, behaviour, true))
|
||||
}
|
||||
|
||||
is ImplicitReceiver -> {
|
||||
val callableDeclaration = (receiver.declarationDescriptor as? CallableDescriptor)?.originalSource?.getPsi()
|
||||
val receiverTypeReference = (callableDeclaration as? KtCallableDeclaration)?.receiverTypeReference
|
||||
?: return emptyList()
|
||||
return listOf(KotlinSliceUsage(receiverTypeReference, parent, behaviour, false))
|
||||
}
|
||||
|
||||
else -> return emptyList()
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val argument = (refElement.parent as? PsiCall)?.argumentList?.expressions?.getOrNull(0) ?: return emptyList()
|
||||
return listOf(KotlinSliceUsage(argument, parent, behaviour, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = other === this
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
}
|
||||
@@ -11,17 +11,9 @@ import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
||||
import org.jetbrains.kotlin.psi.Call
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
import org.jetbrains.kotlin.resolve.scopes.receivers.ExpressionReceiver
|
||||
|
||||
interface KotlinSliceUsageTransformer {
|
||||
fun transform(usage: KotlinSliceUsage): SliceUsage?
|
||||
|
||||
override fun equals(other: Any?): Boolean
|
||||
override fun hashCode(): Int
|
||||
}
|
||||
|
||||
data class LambdaCallsBehaviour(
|
||||
val usageTransformer: KotlinSliceUsageTransformer,
|
||||
val sliceProducer: SliceProducer,
|
||||
override val originalBehaviour: KotlinSliceUsage.SpecialBehaviour?
|
||||
) : KotlinSliceUsage.SpecialBehaviour {
|
||||
|
||||
@@ -32,7 +24,7 @@ data class LambdaCallsBehaviour(
|
||||
val sliceElement = sliceUsage.element ?: return true
|
||||
val resolvedCall = (sliceElement as? KtElement)?.resolveToCall()
|
||||
if (resolvedCall?.call?.callType == Call.CallType.INVOKE) {
|
||||
return usageTransformer.transform(sliceUsage)?.let { uniqueProcessor.process(it) } ?: true
|
||||
return sliceProducer.produceAndProcess(sliceUsage, originalBehaviour, parent, uniqueProcessor)
|
||||
}
|
||||
}
|
||||
return uniqueProcessor.process(sliceUsage)
|
||||
|
||||
@@ -9,6 +9,7 @@ import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.psi.impl.light.LightMemberReference
|
||||
import com.intellij.slicer.SliceUsage
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
@@ -150,17 +151,7 @@ class OutflowSlicer(
|
||||
|
||||
private fun processFunction(function: KtFunction) {
|
||||
if (function is KtConstructor<*> || function is KtNamedFunction && function.name != null) {
|
||||
processCalls(function, analysisScope, includeOverriders = false) { usageInfo ->
|
||||
when (val refElement = usageInfo.element) {
|
||||
null -> (usageInfo.reference as? LightMemberReference)?.element?.passToProcessor()
|
||||
is KtExpression -> {
|
||||
refElement.getCallElementForExactCallee()?.passToProcessor()
|
||||
refElement.getCallableReferenceForExactCallee()
|
||||
?.passToProcessor(LambdaResultOutflowBehaviour(behaviour))
|
||||
}
|
||||
else -> refElement.passToProcessor()
|
||||
}
|
||||
}
|
||||
processCalls(function, includeOverriders = false, sliceProducer = CallSliceProducer)
|
||||
return
|
||||
}
|
||||
|
||||
@@ -249,25 +240,6 @@ class OutflowSlicer(
|
||||
return true
|
||||
}
|
||||
|
||||
private fun PsiElement.getCallElementForExactCallee(): PsiElement? {
|
||||
if (this is KtArrayAccessExpression) return this
|
||||
|
||||
val operationRefExpr = getNonStrictParentOfType<KtOperationReferenceExpression>()
|
||||
if (operationRefExpr != null) return operationRefExpr.parent as? KtOperationExpression
|
||||
|
||||
val parentCall = getParentOfTypeAndBranch<KtCallElement> { calleeExpression } ?: return null
|
||||
val callee = parentCall.calleeExpression?.let { KtPsiUtil.safeDeparenthesize(it) }
|
||||
if (callee == this || callee is KtConstructorCalleeExpression && callee.isAncestor(this, strict = true)) return parentCall
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun PsiElement.getCallableReferenceForExactCallee(): KtCallableReferenceExpression? {
|
||||
val callableRef = getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } ?: return null
|
||||
val callee = KtPsiUtil.safeDeparenthesize(callableRef.callableReference)
|
||||
return if (callee == this) callableRef else null
|
||||
}
|
||||
|
||||
private fun processDereferenceIfNeeded(
|
||||
expression: KtExpression,
|
||||
pseudoValue: PseudoValue,
|
||||
@@ -301,4 +273,54 @@ class OutflowSlicer(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private object CallSliceProducer : SliceProducer {
|
||||
override fun produce(
|
||||
usage: UsageInfo,
|
||||
behaviour: KotlinSliceUsage.SpecialBehaviour?,
|
||||
parent: SliceUsage
|
||||
): Collection<SliceUsage>? {
|
||||
when (val refElement = usage.element) {
|
||||
null -> {
|
||||
val element = (usage.reference as? LightMemberReference)?.element ?: return emptyList()
|
||||
return listOf(KotlinSliceUsage(element, parent, behaviour, false))
|
||||
}
|
||||
|
||||
is KtExpression -> {
|
||||
return mutableListOf<SliceUsage>().apply {
|
||||
refElement.getCallElementForExactCallee()
|
||||
?.let { this += KotlinSliceUsage(it, parent, behaviour, false) }
|
||||
refElement.getCallableReferenceForExactCallee()
|
||||
?.let { this += KotlinSliceUsage(it, parent, LambdaResultOutflowBehaviour(behaviour), false) }
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
return null // unknown type of usage - return null to process it "as is"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun equals(other: Any?) = other === this
|
||||
override fun hashCode() = 0
|
||||
|
||||
private fun PsiElement.getCallElementForExactCallee(): PsiElement? {
|
||||
if (this is KtArrayAccessExpression) return this
|
||||
|
||||
val operationRefExpr = getNonStrictParentOfType<KtOperationReferenceExpression>()
|
||||
if (operationRefExpr != null) return operationRefExpr.parent as? KtOperationExpression
|
||||
|
||||
val parentCall = getParentOfTypeAndBranch<KtCallElement> { calleeExpression } ?: return null
|
||||
val callee = parentCall.calleeExpression?.let { KtPsiUtil.safeDeparenthesize(it) }
|
||||
if (callee == this || callee is KtConstructorCalleeExpression && callee.isAncestor(this, strict = true)) return parentCall
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
private fun PsiElement.getCallableReferenceForExactCallee(): KtCallableReferenceExpression? {
|
||||
val callableRef = getParentOfTypeAndBranch<KtCallableReferenceExpression> { callableReference } ?: return null
|
||||
val callee = KtPsiUtil.safeDeparenthesize(callableRef.callableReference)
|
||||
return if (callee == this) callableRef else null
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2010-2020 JetBrains s.r.o. and Kotlin Programming Language contributors.
|
||||
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
|
||||
*/
|
||||
|
||||
package org.jetbrains.kotlin.idea.slicer
|
||||
|
||||
import com.intellij.slicer.SliceUsage
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
||||
|
||||
interface SliceProducer {
|
||||
fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection<SliceUsage>?
|
||||
|
||||
override fun equals(other: Any?): Boolean
|
||||
override fun hashCode(): Int
|
||||
}
|
||||
|
||||
fun SliceProducer.produceAndProcess(
|
||||
sliceUsage: SliceUsage,
|
||||
behaviour: KotlinSliceUsage.SpecialBehaviour?,
|
||||
parentUsage: SliceUsage,
|
||||
processor: SliceUsageProcessor
|
||||
): Boolean {
|
||||
val result = produce(sliceUsage.usageInfo, behaviour, parentUsage) ?: listOf(sliceUsage)
|
||||
for (usage in result) {
|
||||
if (!processor.process(usage)) return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
@@ -68,29 +68,18 @@ abstract class Slicer(
|
||||
passToProcessor(behaviour, forcedExpressionMode = true)
|
||||
}
|
||||
|
||||
protected fun processFunctionLiteralCalls(
|
||||
functionLiteral: KtFunctionLiteral,
|
||||
sliceTransformer: KotlinSliceUsageTransformer,
|
||||
) {
|
||||
(functionLiteral.parent as KtLambdaExpression).passToProcessorAsValue(LambdaCallsBehaviour(sliceTransformer, behaviour))
|
||||
}
|
||||
|
||||
protected fun processAnonymousFunctionCalls(
|
||||
function: KtNamedFunction,
|
||||
sliceTransformer: KotlinSliceUsageTransformer,
|
||||
) {
|
||||
require(function.name == null)
|
||||
function.passToProcessorAsValue(LambdaCallsBehaviour(sliceTransformer, behaviour))
|
||||
}
|
||||
|
||||
protected fun processCalls(
|
||||
callable: KtCallableDeclaration,
|
||||
scope: SearchScope,
|
||||
includeOverriders: Boolean,
|
||||
usageProcessor: (UsageInfo) -> Unit
|
||||
sliceProducer: SliceProducer
|
||||
) {
|
||||
if (callable is KtFunctionLiteral) {
|
||||
//TODO
|
||||
(callable.parent as KtLambdaExpression).passToProcessorAsValue(LambdaCallsBehaviour(sliceProducer, behaviour))
|
||||
return
|
||||
}
|
||||
|
||||
if (callable is KtFunction && callable.name == null) {
|
||||
callable.passToProcessorAsValue(LambdaCallsBehaviour(sliceProducer, behaviour))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -99,7 +88,7 @@ abstract class Slicer(
|
||||
KotlinFunctionFindUsagesOptions(project).apply {
|
||||
isSearchForTextOccurrences = false
|
||||
isSkipImportStatements = true
|
||||
searchScope = scope
|
||||
searchScope = analysisScope
|
||||
}
|
||||
}
|
||||
|
||||
@@ -107,7 +96,7 @@ abstract class Slicer(
|
||||
KotlinPropertyFindUsagesOptions(project).apply {
|
||||
isSearchForTextOccurrences = false
|
||||
isSkipImportStatements = true
|
||||
searchScope = scope
|
||||
searchScope = analysisScope
|
||||
}
|
||||
}
|
||||
|
||||
@@ -131,6 +120,11 @@ abstract class Slicer(
|
||||
val declaration = superDescriptor.originalSource.getPsi() ?: continue
|
||||
when (declaration) {
|
||||
is KtDeclaration -> {
|
||||
val usageProcessor: (UsageInfo) -> Unit = processor@ { usageInfo ->
|
||||
val element = usageInfo.element ?: return@processor
|
||||
val sliceUsage = KotlinSliceUsage(element, parentUsage, behaviour, false)
|
||||
sliceProducer.produceAndProcess(sliceUsage, behaviour, parentUsage, processor)
|
||||
}
|
||||
if (includeOverriders) {
|
||||
declaration.processAllUsages(options, usageProcessor)
|
||||
} else {
|
||||
@@ -139,12 +133,13 @@ abstract class Slicer(
|
||||
}
|
||||
|
||||
is PsiMethod -> {
|
||||
// todo: work around the bug in JavaSliceProvider.transform()
|
||||
processor.process(JavaSliceUsage.createRootUsage(declaration, parentUsage.params))
|
||||
val sliceUsage = JavaSliceUsage.createRootUsage(declaration, parentUsage.params)
|
||||
sliceProducer.produceAndProcess(sliceUsage, behaviour, parentUsage, processor)
|
||||
}
|
||||
|
||||
else -> {
|
||||
declaration.passToProcessor()
|
||||
val sliceUsage = KotlinSliceUsage(declaration, parentUsage, behaviour, false)
|
||||
sliceProducer.produceAndProcess(sliceUsage, behaviour, parentUsage, processor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -189,12 +184,14 @@ abstract class Slicer(
|
||||
|
||||
protected fun canProcessParameter(parameter: KtParameter) = !parameter.isVarArg
|
||||
|
||||
protected val DeclarationDescriptorWithSource.originalSource: SourceElement
|
||||
get() {
|
||||
var descriptor = this
|
||||
while (descriptor.original != descriptor) {
|
||||
descriptor = descriptor.original
|
||||
protected companion object {
|
||||
val DeclarationDescriptorWithSource.originalSource: SourceElement
|
||||
get() {
|
||||
var descriptor = this
|
||||
while (descriptor.original != descriptor) {
|
||||
descriptor = descriptor.original
|
||||
}
|
||||
return descriptor.source
|
||||
}
|
||||
return descriptor.source
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+3
-2
@@ -1,7 +1,8 @@
|
||||
2 void <bold>foo(int p);</bold>
|
||||
22 i.<bold>foo</bold>(3)
|
||||
9 val v = <bold>p</bold>
|
||||
8 override fun foo(<bold>p: Int</bold>) {
|
||||
2 void <bold>foo(int p);</bold>
|
||||
2 void foo(int <bold>p</bold>);
|
||||
22 i.<bold>foo</bold>(3)
|
||||
|
||||
13 foo(<bold>1</bold>)
|
||||
9 val v = <bold>p</bold>
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
9 val v = <bold>p</bold>
|
||||
8 override fun foo(<bold>p: Int</bold>) {
|
||||
2 void <bold>foo(int p);</bold>
|
||||
2 void foo(int <bold>p</bold>);
|
||||
13 <bold>foo</bold>(1)
|
||||
22 i.<bold>foo</bold>(3)
|
||||
13 foo(<bold>1</bold>)
|
||||
18 i.foo(<bold>2</bold>)
|
||||
|
||||
Reference in New Issue
Block a user