Tracking of receiver for incoming lambda's for outflow analysis + better handling of invoke and operator calls
This commit is contained in:
@@ -140,19 +140,26 @@ class InflowSlicer(
|
|||||||
}
|
}
|
||||||
val currentBehaviour = mode.currentBehaviour
|
val currentBehaviour = mode.currentBehaviour
|
||||||
if (lambda != null) {
|
if (lambda != null) {
|
||||||
if (currentBehaviour is LambdaResultInflowBehaviour) {
|
when (currentBehaviour) {
|
||||||
lambda.passToProcessor(mode.dropBehaviour())
|
is LambdaResultInflowBehaviour -> {
|
||||||
}
|
lambda.passToProcessor(mode.dropBehaviour())
|
||||||
else if (currentBehaviour is LambdaArgumentInflowBehaviour) {
|
}
|
||||||
val valueParameters = lambda.valueParameters
|
|
||||||
if (valueParameters.isEmpty() && lambda is KtFunctionLiteral) {
|
is LambdaArgumentInflowBehaviour -> {
|
||||||
if (currentBehaviour.argumentIndex == 0) {
|
val valueParameters = lambda.valueParameters
|
||||||
lambda.implicitItUsages().forEach {
|
if (valueParameters.isEmpty() && lambda is KtFunctionLiteral) {
|
||||||
it.passToProcessor(mode.dropBehaviour())
|
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
|
return
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import com.intellij.slicer.SliceUsage
|
|||||||
import org.jetbrains.kotlin.idea.KotlinBundle
|
import org.jetbrains.kotlin.idea.KotlinBundle
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
||||||
import org.jetbrains.kotlin.psi.Call
|
|
||||||
import org.jetbrains.kotlin.psi.KtElement
|
import org.jetbrains.kotlin.psi.KtElement
|
||||||
|
|
||||||
data class LambdaCallsBehaviour(private val sliceProducer: SliceProducer) : KotlinSliceAnalysisMode.Behaviour {
|
data class LambdaCallsBehaviour(private val sliceProducer: SliceProducer) : KotlinSliceAnalysisMode.Behaviour {
|
||||||
@@ -19,7 +18,7 @@ data class LambdaCallsBehaviour(private val sliceProducer: SliceProducer) : Kotl
|
|||||||
if (sliceUsage is KotlinSliceUsage && sliceUsage.mode.currentBehaviour === this@LambdaCallsBehaviour) {
|
if (sliceUsage is KotlinSliceUsage && sliceUsage.mode.currentBehaviour === this@LambdaCallsBehaviour) {
|
||||||
val sliceElement = sliceUsage.element ?: return true
|
val sliceElement = sliceUsage.element ?: return true
|
||||||
val resolvedCall = (sliceElement as? KtElement)?.resolveToCall()
|
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 originalMode = sliceUsage.mode.dropBehaviour()
|
||||||
val newSliceUsage = KotlinSliceUsage(resolvedCall.call.callElement, parent, originalMode, true)
|
val newSliceUsage = KotlinSliceUsage(resolvedCall.call.callElement, parent, originalMode, true)
|
||||||
return sliceProducer.produceAndProcess(newSliceUsage, originalMode, parent, uniqueProcessor)
|
return sliceProducer.produceAndProcess(newSliceUsage, originalMode, parent, uniqueProcessor)
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -10,15 +10,11 @@ import com.intellij.psi.PsiMethod
|
|||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
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.eval.*
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.jumps.ReturnValueInstruction
|
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.DeclarationDescriptorWithSource
|
||||||
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
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.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
||||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingElement
|
import org.jetbrains.kotlin.idea.search.declarationsSearch.forEachOverridingElement
|
||||||
@@ -26,11 +22,9 @@ import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDete
|
|||||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
||||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
|
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
import org.jetbrains.kotlin.util.OperatorNameConventions
|
|
||||||
|
|
||||||
class OutflowSlicer(
|
class OutflowSlicer(
|
||||||
element: KtElement,
|
element: KtElement,
|
||||||
@@ -73,13 +67,13 @@ class OutflowSlicer(
|
|||||||
|
|
||||||
when (declaration) {
|
when (declaration) {
|
||||||
is KtFunction -> {
|
is KtFunction -> {
|
||||||
processExtensionReceiver(declaration, declaration)
|
processExtensionReceiverUsages(declaration, declaration.bodyExpression, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
is KtProperty -> {
|
is KtProperty -> {
|
||||||
//TODO: process only one of them or both depending on the usage type
|
//TODO: process only one of them or both depending on the usage type
|
||||||
declaration.getter?.let { processExtensionReceiver(declaration, it) }
|
processExtensionReceiverUsages(declaration, declaration.getter?.bodyExpression, mode)
|
||||||
declaration.setter?.let { processExtensionReceiver(declaration, it) }
|
processExtensionReceiverUsages(declaration, declaration.setter?.bodyExpression, mode)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -154,36 +148,6 @@ class OutflowSlicer(
|
|||||||
processCalls(function, includeOverriders = false, CallSliceProducer)
|
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) {
|
private fun processExpression(expression: KtExpression) {
|
||||||
val expressionWithValue = when (expression) {
|
val expressionWithValue = when (expression) {
|
||||||
is KtFunctionLiteral -> expression.parent as KtLambdaExpression
|
is KtFunctionLiteral -> expression.parent as KtLambdaExpression
|
||||||
@@ -192,24 +156,24 @@ class OutflowSlicer(
|
|||||||
expressionWithValue.processPseudocodeUsages { pseudoValue, instruction ->
|
expressionWithValue.processPseudocodeUsages { pseudoValue, instruction ->
|
||||||
when (instruction) {
|
when (instruction) {
|
||||||
is WriteValueInstruction -> {
|
is WriteValueInstruction -> {
|
||||||
if (!processIfReceiverValue(instruction, pseudoValue)) {
|
if (!processIfReceiverValue(instruction, pseudoValue, mode)) {
|
||||||
instruction.target.accessedDescriptor?.originalSource?.getPsi()?.passToProcessor()
|
instruction.target.accessedDescriptor?.originalSource?.getPsi()?.passToProcessor()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
is ReadValueInstruction -> {
|
is ReadValueInstruction -> {
|
||||||
processIfReceiverValue(instruction, pseudoValue)
|
processIfReceiverValue(instruction, pseudoValue, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
is CallInstruction -> {
|
is CallInstruction -> {
|
||||||
if (!processIfReceiverValue(instruction, pseudoValue)) {
|
if (!processIfReceiverValue(instruction, pseudoValue, mode)) {
|
||||||
val parameterDescriptor = instruction.arguments[pseudoValue] ?: return@processPseudocodeUsages
|
val parameterDescriptor = instruction.arguments[pseudoValue] ?: return@processPseudocodeUsages
|
||||||
val parameter = parameterDescriptor.originalSource.getPsi()
|
val parameter = parameterDescriptor.originalSource.getPsi()
|
||||||
if (parameter != null) {
|
if (parameter != null) {
|
||||||
parameter.passToProcessorInCallMode(instruction.element)
|
parameter.passToProcessorInCallMode(instruction.element)
|
||||||
} else {
|
} else {
|
||||||
val function = parameterDescriptor.containingDeclaration as? FunctionDescriptor
|
val function = parameterDescriptor.containingDeclaration as? FunctionDescriptor ?: return@processPseudocodeUsages
|
||||||
if (function != null && function.isOperator && function.name == OperatorNameConventions.INVOKE) {
|
if (function.isImplicitInvokeFunction()) {
|
||||||
val receiverPseudoValue = instruction.receiverValues.entries.singleOrNull()?.key
|
val receiverPseudoValue = instruction.receiverValues.entries.singleOrNull()?.key
|
||||||
?: return@processPseudocodeUsages
|
?: return@processPseudocodeUsages
|
||||||
if (receiverPseudoValue.createdAt != null) {
|
if (receiverPseudoValue.createdAt != null) {
|
||||||
@@ -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(
|
private fun processDereferenceIfNeeded(
|
||||||
expression: KtExpression,
|
expression: KtExpression,
|
||||||
pseudoValue: PseudoValue,
|
pseudoValue: PseudoValue,
|
||||||
|
|||||||
@@ -12,13 +12,16 @@ import com.intellij.slicer.JavaSliceUsage
|
|||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
import com.intellij.util.containers.addIfNotNull
|
import com.intellij.util.containers.addIfNotNull
|
||||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||||
|
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
|
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
|
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
|
||||||
import org.jetbrains.kotlin.descriptors.CallableMemberDescriptor
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.KtElementInstruction
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||||
import org.jetbrains.kotlin.descriptors.SourceElement
|
import org.jetbrains.kotlin.descriptors.*
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
import org.jetbrains.kotlin.idea.caches.resolve.analyzeWithContent
|
||||||
|
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.caches.resolve.resolveToDescriptorIfAny
|
||||||
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
import org.jetbrains.kotlin.idea.core.getDeepestSuperDeclarations
|
||||||
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
|
import org.jetbrains.kotlin.idea.findUsages.KotlinFunctionFindUsagesOptions
|
||||||
@@ -33,9 +36,11 @@ import org.jetbrains.kotlin.idea.util.expectedDescriptor
|
|||||||
import org.jetbrains.kotlin.idea.util.hasInlineModifier
|
import org.jetbrains.kotlin.idea.util.hasInlineModifier
|
||||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
abstract class Slicer(
|
abstract class Slicer(
|
||||||
@@ -71,17 +76,21 @@ abstract class Slicer(
|
|||||||
processor.process(KotlinSliceUsage(this, parentUsage, mode, forcedExpressionMode = true))
|
processor.process(KotlinSliceUsage(this, parentUsage, mode, forcedExpressionMode = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun PsiElement.passToProcessorInCallMode(callElement: KtElement, withOverriders: Boolean = false) {
|
protected fun PsiElement.passToProcessorInCallMode(
|
||||||
|
callElement: KtElement,
|
||||||
|
mode: KotlinSliceAnalysisMode = this@Slicer.mode,
|
||||||
|
withOverriders: Boolean = false
|
||||||
|
) {
|
||||||
val newMode = when (this) {
|
val newMode = when (this) {
|
||||||
is KtNamedFunction -> this.callMode(callElement)
|
is KtNamedFunction -> this.callMode(callElement, mode)
|
||||||
|
|
||||||
is KtParameter -> ownerFunction.callMode(callElement)
|
is KtParameter -> ownerFunction.callMode(callElement, mode)
|
||||||
|
|
||||||
is KtTypeReference -> {
|
is KtTypeReference -> {
|
||||||
val declaration = parent
|
val declaration = parent
|
||||||
require(declaration is KtCallableDeclaration)
|
require(declaration is KtCallableDeclaration)
|
||||||
require(this == declaration.receiverTypeReference)
|
require(this == declaration.receiverTypeReference)
|
||||||
declaration.callMode(callElement)
|
declaration.callMode(callElement, mode)
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> mode
|
else -> mode
|
||||||
@@ -110,13 +119,6 @@ abstract class Slicer(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected fun KtDeclaration?.callMode(callElement: KtElement): KotlinSliceAnalysisMode {
|
|
||||||
return if (this is KtNamedFunction && hasInlineModifier())
|
|
||||||
mode.withInlineFunctionCall(callElement, this)
|
|
||||||
else
|
|
||||||
mode
|
|
||||||
}
|
|
||||||
|
|
||||||
protected open fun processCalls(
|
protected open fun processCalls(
|
||||||
callable: KtCallableDeclaration,
|
callable: KtCallableDeclaration,
|
||||||
includeOverriders: Boolean,
|
includeOverriders: Boolean,
|
||||||
@@ -227,6 +229,98 @@ abstract class Slicer(
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected fun canProcessParameter(parameter: KtParameter) = !parameter.isVarArg
|
protected fun canProcessParameter(parameter: KtParameter) = !parameter.isVarArg
|
||||||
|
|
||||||
|
protected fun processExtensionReceiverUsages(
|
||||||
|
declaration: KtCallableDeclaration,
|
||||||
|
body: KtExpression?,
|
||||||
|
mode: KotlinSliceAnalysisMode,
|
||||||
|
) {
|
||||||
|
if (body == null) return
|
||||||
|
//TODO: overriders
|
||||||
|
val resolutionFacade = declaration.getResolutionFacade()
|
||||||
|
val callableDescriptor = declaration.resolveToDescriptorIfAny(resolutionFacade) as? CallableDescriptor ?: return
|
||||||
|
val extensionReceiver = callableDescriptor.extensionReceiverParameter ?: return
|
||||||
|
|
||||||
|
body.forEachDescendantOfType<KtThisExpression> { thisExpression ->
|
||||||
|
val receiverDescriptor = thisExpression.resolveToCall(resolutionFacade)?.resultingDescriptor
|
||||||
|
if (receiverDescriptor == extensionReceiver) {
|
||||||
|
thisExpression.passToProcessor(mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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) {
|
||||||
|
// TODO: make sure it processes correct receiver!!
|
||||||
|
processIfReceiverValue(receiverUseInstruction, receiverPseudoValue, mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected fun processIfReceiverValue(
|
||||||
|
instruction: KtElementInstruction,
|
||||||
|
receiverPseudoValue: PseudoValue,
|
||||||
|
mode: KotlinSliceAnalysisMode,
|
||||||
|
): Boolean {
|
||||||
|
val receiverValue = (instruction as? InstructionWithReceivers)?.receiverValues?.get(receiverPseudoValue) ?: return false
|
||||||
|
val resolvedCall = instruction.element.resolveToCall() ?: return true
|
||||||
|
val descriptor = resolvedCall.resultingDescriptor
|
||||||
|
|
||||||
|
if (descriptor.isImplicitInvokeFunction()) {
|
||||||
|
when (receiverValue) {
|
||||||
|
resolvedCall.dispatchReceiver -> {
|
||||||
|
if (mode.currentBehaviour is LambdaCallsBehaviour) {
|
||||||
|
instruction.element.passToProcessor(mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
resolvedCall.extensionReceiver -> {
|
||||||
|
val dispatchReceiver = resolvedCall.dispatchReceiver ?: return true
|
||||||
|
val dispatchReceiverPseudoValue = instruction.receiverValues.entries
|
||||||
|
.singleOrNull { it.value == dispatchReceiver }?.key
|
||||||
|
?: return true
|
||||||
|
if (dispatchReceiverPseudoValue.createdAt != null) {
|
||||||
|
when (val createdAt = dispatchReceiverPseudoValue.createdAt) {
|
||||||
|
is ReadValueInstruction -> {
|
||||||
|
val accessedDescriptor = createdAt.target.accessedDescriptor ?: return true
|
||||||
|
val accessedDeclaration = accessedDescriptor.originalSource.getPsi() ?: return true
|
||||||
|
when (accessedDescriptor) {
|
||||||
|
is ValueParameterDescriptor -> {
|
||||||
|
accessedDeclaration.passToProcessor(mode.withBehaviour(LambdaReceiverInflowBehaviour))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (receiverValue == resolvedCall.extensionReceiver) {
|
||||||
|
val declaration = descriptor.originalSource.getPsi()
|
||||||
|
(declaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessorInCallMode(instruction.element, mode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
protected fun KtDeclaration?.callMode(callElement: KtElement, defaultMode: KotlinSliceAnalysisMode): KotlinSliceAnalysisMode {
|
||||||
|
return if (this is KtNamedFunction && hasInlineModifier())
|
||||||
|
defaultMode.withInlineFunctionCall(callElement, this)
|
||||||
|
else
|
||||||
|
defaultMode
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val DeclarationDescriptorWithSource.originalSource: SourceElement
|
val DeclarationDescriptorWithSource.originalSource: SourceElement
|
||||||
@@ -238,3 +332,9 @@ val DeclarationDescriptorWithSource.originalSource: SourceElement
|
|||||||
return descriptor.source
|
return descriptor.source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun CallableDescriptor.isImplicitInvokeFunction(): Boolean {
|
||||||
|
if (this !is FunctionDescriptor) return false
|
||||||
|
if (!isOperator) return false
|
||||||
|
if (name != OperatorNameConventions.INVOKE) return false
|
||||||
|
return originalSource.getPsi() == null
|
||||||
|
}
|
||||||
@@ -1,6 +1,26 @@
|
|||||||
15 val <bold>x = A()</bold>
|
15 val <bold>x = A()</bold>
|
||||||
18 <bold>x</bold>[1]
|
18 <bold>x</bold>[1]
|
||||||
|
6 operator fun <bold>A</bold>.get(i: Int) = this
|
||||||
|
6 operator fun A.get(i: Int) = <bold>this</bold>
|
||||||
|
6 operator fun A.<bold>get(i: Int) = this</bold>
|
||||||
|
18 <bold>x[1]</bold>
|
||||||
|
20 <bold>x[1]</bold> += y
|
||||||
|
8 operator fun <bold>A</bold>.plusAssign(a: A) {
|
||||||
|
9 val v = <bold>this</bold>
|
||||||
|
9 val <bold>v = this</bold>
|
||||||
|
21 <bold>x[1]</bold> *= y
|
||||||
|
11 operator fun <bold>A</bold>.times(a: A) = this
|
||||||
|
11 operator fun A.times(a: A) = <bold>this</bold>
|
||||||
|
11 operator fun A.<bold>times(a: A) = this</bold>
|
||||||
|
21 <bold>x[1] *= y</bold>
|
||||||
|
22 <bold>x[1]</bold>++
|
||||||
|
12 operator fun <bold>A</bold>.inc() = this
|
||||||
|
12 operator fun A.inc() = <bold>this</bold>
|
||||||
|
12 operator fun A.<bold>inc() = this</bold>
|
||||||
|
22 <bold>x[1]++</bold>
|
||||||
|
12 DUPLICATE: operator fun <bold>A</bold>.inc() = this
|
||||||
19 <bold>x</bold>[1] = y
|
19 <bold>x</bold>[1] = y
|
||||||
20 <bold>x</bold>[1] += y
|
20 <bold>x</bold>[1] += y
|
||||||
|
6 DUPLICATE: operator fun <bold>A</bold>.get(i: Int) = this
|
||||||
21 <bold>x</bold>[1] *= y
|
21 <bold>x</bold>[1] *= y
|
||||||
22 <bold>x</bold>[1]++
|
22 <bold>x</bold>[1]++
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun test() {
|
||||||
|
val x = foo { <caret>1 }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun foo(callback: () -> Int): Int {
|
||||||
|
return callback.invoke()
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
4 val x = foo { <bold>1</bold> }
|
||||||
|
4 val x = foo <bold>{ 1 }</bold>
|
||||||
|
4 [LAMBDA CALLS] val x = foo <bold>{ 1 }</bold>
|
||||||
|
7 [LAMBDA CALLS] fun foo(<bold>callback: () -> Int</bold>): Int {
|
||||||
|
8 [LAMBDA CALLS] return <bold>callback</bold>.invoke()
|
||||||
|
8 return callback.<bold>invoke()</bold>
|
||||||
|
7 fun <bold>foo(callback: () -> Int): Int {</bold>
|
||||||
|
4 val x = <bold>foo { 1 }</bold>
|
||||||
|
4 val <bold>x = foo { 1 }</bold>
|
||||||
+15
@@ -0,0 +1,15 @@
|
|||||||
|
// FLOW: OUT
|
||||||
|
|
||||||
|
fun String.foo(<caret>p: String) {
|
||||||
|
val v1 = with(p) { this }
|
||||||
|
|
||||||
|
val v2 = with(p) { bar(this) }
|
||||||
|
|
||||||
|
val v3 = with(p) { this@foo }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bar(s: String) = s
|
||||||
|
|
||||||
|
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
|
||||||
|
return receiver.block()
|
||||||
|
}
|
||||||
@@ -0,0 +1,34 @@
|
|||||||
|
3 fun String.foo(<bold>p: String</bold>) {
|
||||||
|
4 val v1 = with(<bold>p</bold>) { this }
|
||||||
|
13 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||||
|
14 (INLINE CALL with) return <bold>receiver</bold>.block()
|
||||||
|
13 (INLINE CALL with) [LAMBDA RECEIVER IN] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||||
|
4 [LAMBDA RECEIVER IN] val v1 = with(p) <bold>{ this }</bold>
|
||||||
|
4 val v1 = with(p) { <bold>this</bold> }
|
||||||
|
4 val v1 = with(p) <bold>{ this }</bold>
|
||||||
|
4 [LAMBDA CALLS] val v1 = with(p) <bold>{ this }</bold>
|
||||||
|
13 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||||
|
14 (INLINE CALL with) return receiver.<bold>block()</bold>
|
||||||
|
4 val v1 = <bold>with(p) { this }</bold>
|
||||||
|
4 val <bold>v1 = with(p) { this }</bold>
|
||||||
|
6 val v2 = with(<bold>p</bold>) { bar(this) }
|
||||||
|
13 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||||
|
14 (INLINE CALL with) return <bold>receiver</bold>.block()
|
||||||
|
13 (INLINE CALL with) [LAMBDA RECEIVER IN] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||||
|
6 [LAMBDA RECEIVER IN] val v2 = with(p) <bold>{ bar(this) }</bold>
|
||||||
|
6 val v2 = with(p) { bar(<bold>this</bold>) }
|
||||||
|
11 fun bar(<bold>s: String</bold>) = s
|
||||||
|
11 fun bar(s: String) = <bold>s</bold>
|
||||||
|
11 fun <bold>bar(s: String) = s</bold>
|
||||||
|
6 val v2 = with(p) { <bold>bar(this)</bold> }
|
||||||
|
6 val v2 = with(p) <bold>{ bar(this) }</bold>
|
||||||
|
6 [LAMBDA CALLS] val v2 = with(p) <bold>{ bar(this) }</bold>
|
||||||
|
13 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||||
|
14 (INLINE CALL with) return receiver.<bold>block()</bold>
|
||||||
|
6 val v2 = <bold>with(p) { bar(this) }</bold>
|
||||||
|
6 val <bold>v2 = with(p) { bar(this) }</bold>
|
||||||
|
8 val v3 = with(<bold>p</bold>) { this@foo }
|
||||||
|
13 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||||
|
14 (INLINE CALL with) return <bold>receiver</bold>.block()
|
||||||
|
13 (INLINE CALL with) [LAMBDA RECEIVER IN] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||||
|
8 [LAMBDA RECEIVER IN] val v3 = with(p) <bold>{ this@foo }</bold>
|
||||||
@@ -623,6 +623,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
|
|||||||
runTest("idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt");
|
runTest("idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("lambdaResultWithInvokeCall.kt")
|
||||||
|
public void testLambdaResultWithInvokeCall() throws Exception {
|
||||||
|
runTest("idea/testData/slicer/outflow/lambdaResultWithInvokeCall.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("letResult.kt")
|
@TestMetadata("letResult.kt")
|
||||||
public void testLetResult() throws Exception {
|
public void testLetResult() throws Exception {
|
||||||
runTest("idea/testData/slicer/outflow/letResult.kt");
|
runTest("idea/testData/slicer/outflow/letResult.kt");
|
||||||
@@ -753,6 +758,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
|
|||||||
runTest("idea/testData/slicer/outflow/whenExpression.kt");
|
runTest("idea/testData/slicer/outflow/whenExpression.kt");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@TestMetadata("withResult.kt")
|
||||||
|
public void testWithResult() throws Exception {
|
||||||
|
runTest("idea/testData/slicer/outflow/withResult.kt");
|
||||||
|
}
|
||||||
|
|
||||||
@TestMetadata("wrongThis.kt")
|
@TestMetadata("wrongThis.kt")
|
||||||
public void testWrongThis() throws Exception {
|
public void testWrongThis() throws Exception {
|
||||||
runTest("idea/testData/slicer/outflow/wrongThis.kt");
|
runTest("idea/testData/slicer/outflow/wrongThis.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user