Skip to content

Commit

Permalink
Tracking of receiver for incoming lambda's for outflow analysis + bet…
Browse files Browse the repository at this point in the history
…ter handling of invoke and operator calls
  • Loading branch information
valentinkip committed Apr 17, 2020
1 parent 8f01427 commit 04d4f74
Show file tree
Hide file tree
Showing 11 changed files with 262 additions and 95 deletions.
29 changes: 18 additions & 11 deletions idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -140,19 +140,26 @@ class InflowSlicer(
}
val currentBehaviour = mode.currentBehaviour
if (lambda != null) {
if (currentBehaviour is LambdaResultInflowBehaviour) {
lambda.passToProcessor(mode.dropBehaviour())
}
else if (currentBehaviour is LambdaArgumentInflowBehaviour) {
val valueParameters = lambda.valueParameters
if (valueParameters.isEmpty() && lambda is KtFunctionLiteral) {
if (currentBehaviour.argumentIndex == 0) {
lambda.implicitItUsages().forEach {
it.passToProcessor(mode.dropBehaviour())
when (currentBehaviour) {
is LambdaResultInflowBehaviour -> {
lambda.passToProcessor(mode.dropBehaviour())
}

is LambdaArgumentInflowBehaviour -> {
val valueParameters = lambda.valueParameters
if (valueParameters.isEmpty() && lambda is KtFunctionLiteral) {
if (currentBehaviour.argumentIndex == 0) {
lambda.implicitItUsages().forEach {
it.passToProcessor(mode.dropBehaviour())
}
}
} else {
valueParameters.getOrNull(currentBehaviour.argumentIndex)?.passToProcessor(mode.dropBehaviour())
}
} else {
valueParameters.getOrNull(currentBehaviour.argumentIndex)?.passToProcessor(mode.dropBehaviour())
}

is LambdaReceiverInflowBehaviour -> {
processExtensionReceiverUsages(lambda, lambda, mode.dropBehaviour())
}
}
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import com.intellij.slicer.SliceUsage
import org.jetbrains.kotlin.idea.KotlinBundle
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

data class LambdaCallsBehaviour(private val sliceProducer: SliceProducer) : KotlinSliceAnalysisMode.Behaviour {
Expand All @@ -19,7 +18,7 @@ data class LambdaCallsBehaviour(private val sliceProducer: SliceProducer) : Kotl
if (sliceUsage is KotlinSliceUsage && sliceUsage.mode.currentBehaviour === this@LambdaCallsBehaviour) {
val sliceElement = sliceUsage.element ?: return true
val resolvedCall = (sliceElement as? KtElement)?.resolveToCall()
if (resolvedCall?.call?.callType == Call.CallType.INVOKE) {
if (resolvedCall != null && resolvedCall.resultingDescriptor.isImplicitInvokeFunction()) {
val originalMode = sliceUsage.mode.dropBehaviour()
val newSliceUsage = KotlinSliceUsage(resolvedCall.call.callElement, parent, originalMode, true)
return sliceProducer.produceAndProcess(newSliceUsage, originalMode, parent, uniqueProcessor)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* 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 org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
import org.jetbrains.kotlin.psi.KtElement

object LambdaReceiverInflowBehaviour : KotlinSliceAnalysisMode.Behaviour {
override fun processUsages(element: KtElement, parent: KotlinSliceUsage, uniqueProcessor: SliceUsageProcessor) {
InflowSlicer(element, uniqueProcessor, parent).processChildren(parent.forcedExpressionMode)
}

override val slicePresentationPrefix: String
get() = TODO()

override val testPresentationPrefix: String
get() = "[LAMBDA RECEIVER IN] "

override fun equals(other: Any?) = other === this
override fun hashCode() = 0
}
76 changes: 8 additions & 68 deletions idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -10,27 +10,21 @@ import com.intellij.psi.PsiMethod
import com.intellij.usageView.UsageInfo
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction
import org.jetbrains.kotlin.descriptors.CallableDescriptor
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingElement
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector
import org.jetbrains.kotlin.idea.util.actualsForExpected
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
import org.jetbrains.kotlin.psi.*
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
import org.jetbrains.kotlin.resolve.source.getPsi
import org.jetbrains.kotlin.util.OperatorNameConventions

class OutflowSlicer(
element: KtElement,
Expand Down Expand Up @@ -73,13 +67,13 @@ class OutflowSlicer(

when (declaration) {
is KtFunction -> {
processExtensionReceiver(declaration, declaration)
processExtensionReceiverUsages(declaration, declaration.bodyExpression, mode)
}

is KtProperty -> {
//TODO: process only one of them or both depending on the usage type
declaration.getter?.let { processExtensionReceiver(declaration, it) }
declaration.setter?.let { processExtensionReceiver(declaration, it) }
processExtensionReceiverUsages(declaration, declaration.getter?.bodyExpression, mode)
processExtensionReceiverUsages(declaration, declaration.setter?.bodyExpression, mode)
}
}
}
Expand Down Expand Up @@ -154,36 +148,6 @@ class OutflowSlicer(
processCalls(function, includeOverriders = false, CallSliceProducer)
}

private fun processExtensionReceiver(declaration: KtCallableDeclaration, declarationWithBody: KtDeclarationWithBody) {
//TODO: overriders
val resolutionFacade = declaration.getResolutionFacade()
val callableDescriptor = declaration.resolveToDescriptorIfAny(resolutionFacade) as? CallableDescriptor ?: return
val extensionReceiver = callableDescriptor.extensionReceiverParameter ?: return
val body = declarationWithBody.bodyExpression ?: return

body.forEachDescendantOfType<KtThisExpression> { thisExpression ->
val receiverDescriptor = thisExpression.resolveToCall(resolutionFacade)?.resultingDescriptor
if (receiverDescriptor == extensionReceiver) {
thisExpression.passToProcessor()
}
}

// process implicit receiver usages
val pseudocode = pseudocodeCache[body]
if (pseudocode != null) {
for (instruction in pseudocode.instructions) {
if (instruction is MagicInstruction && instruction.kind == MagicKind.IMPLICIT_RECEIVER) {
val receiverPseudoValue = instruction.outputValue
pseudocode.getUsages(receiverPseudoValue).forEach { receiverUseInstruction ->
if (receiverUseInstruction is KtElementInstruction) {
processIfReceiverValue(receiverUseInstruction, receiverPseudoValue)
}
}
}
}
}
}

private fun processExpression(expression: KtExpression) {
val expressionWithValue = when (expression) {
is KtFunctionLiteral -> expression.parent as KtLambdaExpression
Expand All @@ -192,24 +156,24 @@ class OutflowSlicer(
expressionWithValue.processPseudocodeUsages { pseudoValue, instruction ->
when (instruction) {
is WriteValueInstruction -> {
if (!processIfReceiverValue(instruction, pseudoValue)) {
if (!processIfReceiverValue(instruction, pseudoValue, mode)) {
instruction.target.accessedDescriptor?.originalSource?.getPsi()?.passToProcessor()
}
}

is ReadValueInstruction -> {
processIfReceiverValue(instruction, pseudoValue)
processIfReceiverValue(instruction, pseudoValue, mode)
}

is CallInstruction -> {
if (!processIfReceiverValue(instruction, pseudoValue)) {
if (!processIfReceiverValue(instruction, pseudoValue, mode)) {
val parameterDescriptor = instruction.arguments[pseudoValue] ?: return@processPseudocodeUsages
val parameter = parameterDescriptor.originalSource.getPsi()
if (parameter != null) {
parameter.passToProcessorInCallMode(instruction.element)
} else {
val function = parameterDescriptor.containingDeclaration as? FunctionDescriptor
if (function != null && function.isOperator && function.name == OperatorNameConventions.INVOKE) {
val function = parameterDescriptor.containingDeclaration as? FunctionDescriptor ?: return@processPseudocodeUsages
if (function.isImplicitInvokeFunction()) {
val receiverPseudoValue = instruction.receiverValues.entries.singleOrNull()?.key
?: return@processPseudocodeUsages
if (receiverPseudoValue.createdAt != null) {
Expand Down Expand Up @@ -256,30 +220,6 @@ class OutflowSlicer(
}
}

private fun processIfReceiverValue(instruction: KtElementInstruction, pseudoValue: PseudoValue): Boolean {
val receiverValue = (instruction as? InstructionWithReceivers)?.receiverValues?.get(pseudoValue) ?: return false
val resolvedCall = instruction.element.resolveToCall() ?: return true
when (resolvedCall.call.callType) {
Call.CallType.DEFAULT -> {
if (receiverValue == resolvedCall.extensionReceiver) {
val targetDeclaration = resolvedCall.resultingDescriptor.originalSource.getPsi()
(targetDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessorInCallMode(instruction.element)
}
}

Call.CallType.INVOKE -> {
if (receiverValue == resolvedCall.dispatchReceiver && mode.currentBehaviour is LambdaCallsBehaviour) {
instruction.element.passToProcessor()
}
}

else -> {
//TODO
}
}
return true
}

private fun processDereferenceIfNeeded(
expression: KtExpression,
pseudoValue: PseudoValue,
Expand Down
Loading

0 comments on commit 04d4f74

Please sign in to comment.