Skip to content

Commit

Permalink
Code refactoring + better handling of incoming data flow to java supe…
Browse files Browse the repository at this point in the history
…r methods
  • Loading branch information
valentinkip committed Apr 17, 2020
1 parent b5b0fd6 commit 1e39d4a
Show file tree
Hide file tree
Showing 7 changed files with 172 additions and 129 deletions.
111 changes: 55 additions & 56 deletions idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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) {
Expand All @@ -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) {
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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 -> {
Expand All @@ -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
}
}
12 changes: 2 additions & 10 deletions idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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 {

Expand All @@ -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)
Expand Down
82 changes: 52 additions & 30 deletions idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
}

Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
}
}
}
30 changes: 30 additions & 0 deletions idea/src/org/jetbrains/kotlin/idea/slicer/SliceProducer.kt
Original file line number Diff line number Diff line change
@@ -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
}
Loading

0 comments on commit 1e39d4a

Please sign in to comment.