Special handling of inline function to track values from individual calls
This commit is contained in:
@@ -32,10 +32,10 @@ data class ArgumentSliceProducer private constructor(
|
||||
parameterDescriptor.containingDeclaration.isExtension
|
||||
)
|
||||
|
||||
override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection<SliceUsage>? {
|
||||
override fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection<SliceUsage>? {
|
||||
val element = usage.element ?: return emptyList()
|
||||
val argumentExpression = extractArgumentExpression(element) ?: return emptyList()
|
||||
return listOf(KotlinSliceUsage(argumentExpression, parent, behaviour, forcedExpressionMode = true))
|
||||
return listOf(KotlinSliceUsage(argumentExpression, parent, mode, forcedExpressionMode = true))
|
||||
}
|
||||
|
||||
private fun extractArgumentExpression(refElement: PsiElement): PsiElement? {
|
||||
|
||||
@@ -15,8 +15,8 @@ import org.jetbrains.kotlin.psi.psiUtil.getParentOfTypeAndBranch
|
||||
import org.jetbrains.kotlin.psi.psiUtil.isAncestor
|
||||
|
||||
object CallSliceProducer : SliceProducer {
|
||||
override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection<SliceUsage>? {
|
||||
if ((parent as? KotlinSliceUsage)?.behaviour is LambdaCallsBehaviour) {
|
||||
override fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection<SliceUsage>? {
|
||||
if ((parent as? KotlinSliceUsage)?.mode?.currentBehaviour is LambdaCallsBehaviour) {
|
||||
// UsageInfo produced by LambdaCallsBehaviour has full call-element and does not require any processing
|
||||
return null
|
||||
}
|
||||
@@ -24,15 +24,22 @@ object CallSliceProducer : SliceProducer {
|
||||
when (val refElement = usage.element) {
|
||||
null -> {
|
||||
val element = (usage.reference as? LightMemberReference)?.element ?: return emptyList()
|
||||
return listOf(KotlinSliceUsage(element, parent, behaviour, false))
|
||||
return listOf(KotlinSliceUsage(element, parent, mode, false))
|
||||
}
|
||||
|
||||
is KtExpression -> {
|
||||
return mutableListOf<SliceUsage>().apply {
|
||||
refElement.getCallElementForExactCallee()
|
||||
?.let { this += KotlinSliceUsage(it, parent, behaviour, false) }
|
||||
?.let { this += KotlinSliceUsage(it, parent, mode, false) }
|
||||
refElement.getCallableReferenceForExactCallee()
|
||||
?.let { this += KotlinSliceUsage(it, parent, LambdaCallsBehaviour(SliceProducer.Trivial, behaviour), false) }
|
||||
?.let {
|
||||
this += KotlinSliceUsage(
|
||||
it,
|
||||
parent,
|
||||
mode.withBehaviour(LambdaCallsBehaviour(SliceProducer.Trivial)),
|
||||
forcedExpressionMode = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ import org.jetbrains.kotlin.idea.references.readWriteAccessWithFullExpression
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.searchOverriders
|
||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
||||
import org.jetbrains.kotlin.idea.util.hasInlineModifier
|
||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
@@ -141,8 +142,8 @@ class InflowSlicer(
|
||||
else -> null
|
||||
}
|
||||
if (lambda != null) {
|
||||
if (behaviour is LambdaResultInflowBehaviour) {
|
||||
lambda.passToProcessor(behaviour.originalBehaviour)
|
||||
if (mode.currentBehaviour is LambdaResultInflowBehaviour) {
|
||||
lambda.passToProcessor(mode.dropBehaviour())
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -176,7 +177,7 @@ class InflowSlicer(
|
||||
val callable = accessedDescriptor.containingDeclaration as? CallableDescriptor ?: return
|
||||
when (val declaration = callable.originalSource.getPsi()) {
|
||||
is KtFunctionLiteral -> {
|
||||
declaration.passToProcessorAsValue(LambdaCallsBehaviour(ReceiverSliceProducer, behaviour))
|
||||
declaration.passToProcessorAsValue(mode.withBehaviour(LambdaCallsBehaviour(ReceiverSliceProducer)))
|
||||
}
|
||||
|
||||
is KtCallableDeclaration -> {
|
||||
@@ -194,12 +195,12 @@ class InflowSlicer(
|
||||
processCalls(functionLiteral, false, ArgumentSliceProducer(parameterDescriptor))
|
||||
}
|
||||
} else {
|
||||
accessedDeclaration.passDeclarationToProcessorWithOverriders()
|
||||
accessedDeclaration.passDeclarationToProcessorWithOverriders(createdAt.element)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
accessedDeclaration?.passDeclarationToProcessorWithOverriders()
|
||||
accessedDeclaration?.passDeclarationToProcessorWithOverriders(createdAt.element)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -215,8 +216,8 @@ class InflowSlicer(
|
||||
val referencedDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return
|
||||
val referencedDeclaration = (referencedDescriptor as? DeclarationDescriptorWithSource)?.originalSource?.getPsi()
|
||||
?: return
|
||||
if (behaviour is LambdaResultInflowBehaviour) {
|
||||
referencedDeclaration.passToProcessor(behaviour.originalBehaviour)
|
||||
if (mode.currentBehaviour is LambdaResultInflowBehaviour) {
|
||||
referencedDeclaration.passToProcessor(mode.dropBehaviour())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -228,9 +229,9 @@ class InflowSlicer(
|
||||
val resultingDescriptor = resolvedCall.resultingDescriptor
|
||||
if (resultingDescriptor is FunctionInvokeDescriptor) {
|
||||
(resolvedCall.dispatchReceiver as? ExpressionReceiver)?.expression
|
||||
?.passToProcessorAsValue(LambdaResultInflowBehaviour(behaviour))
|
||||
?.passToProcessorAsValue(mode.withBehaviour(LambdaResultInflowBehaviour))
|
||||
} else {
|
||||
resultingDescriptor.originalSource.getPsi()?.passDeclarationToProcessorWithOverriders()
|
||||
resultingDescriptor.originalSource.getPsi()?.passDeclarationToProcessorWithOverriders(createdAt.element)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -305,19 +306,37 @@ class InflowSlicer(
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.passDeclarationToProcessorWithOverriders() {
|
||||
passToProcessor()
|
||||
private fun PsiElement.passDeclarationToProcessorWithOverriders(callElement: KtElement) {
|
||||
val newMode = if (this is KtNamedFunction && hasInlineModifier())
|
||||
mode.withInlineFunctionCall(callElement, this)
|
||||
else
|
||||
mode
|
||||
|
||||
passToProcessor(newMode)
|
||||
|
||||
HierarchySearchRequest(this, analysisScope)
|
||||
.searchOverriders()
|
||||
.forEach { it.namedUnwrappedElement?.passToProcessor() }
|
||||
.forEach { it.namedUnwrappedElement?.passToProcessor(newMode) }
|
||||
|
||||
if (this is KtCallableDeclaration && isExpectDeclaration()) {
|
||||
resolveToDescriptorIfAny(BodyResolveMode.FULL)
|
||||
?.actualsForExpected()
|
||||
?.forEach {
|
||||
(it as? DeclarationDescriptorWithSource)?.originalSource?.getPsi()?.passToProcessor()
|
||||
(it as? DeclarationDescriptorWithSource)?.originalSource?.getPsi()?.passToProcessor(newMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun processCalls(callable: KtCallableDeclaration, includeOverriders: Boolean, sliceProducer: SliceProducer) {
|
||||
if (callable is KtNamedFunction) {
|
||||
val (newMode, callElement) = mode.popInlineFunctionCall(callable)
|
||||
if (newMode != null && callElement != null) {
|
||||
val sliceUsage = KotlinSliceUsage(callElement, parentUsage, newMode, false)
|
||||
sliceProducer.produceAndProcess(sliceUsage, newMode, parentUsage, processor)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
super.processCalls(callable, includeOverriders, sliceProducer)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,67 @@
|
||||
/*
|
||||
* 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
|
||||
import org.jetbrains.kotlin.psi.KtNamedFunction
|
||||
import org.jetbrains.kotlin.psi.psiUtil.createSmartPointer
|
||||
|
||||
data class KotlinSliceAnalysisMode(val behaviourStack: List<Behaviour>, val inlineCallStack: List<InlineFunctionCall>) {
|
||||
fun withBehaviour(behaviour: Behaviour) = copy(behaviourStack = behaviourStack + behaviour)
|
||||
|
||||
fun withInlineFunctionCall(
|
||||
callElement: KtElement,
|
||||
function: KtNamedFunction
|
||||
) = copy(inlineCallStack = inlineCallStack + InlineFunctionCall(callElement, function))
|
||||
|
||||
fun dropBehaviour(): KotlinSliceAnalysisMode {
|
||||
check(behaviourStack.isNotEmpty())
|
||||
return copy(behaviourStack = behaviourStack.dropLast(1))
|
||||
}
|
||||
|
||||
fun popInlineFunctionCall(function: KtNamedFunction): Pair<KotlinSliceAnalysisMode?, KtElement?> {
|
||||
val last = inlineCallStack.lastOrNull()
|
||||
if (last?.function != function) return null to null
|
||||
val newMode = copy(inlineCallStack = inlineCallStack.dropLast(1))
|
||||
return newMode to last.callElement
|
||||
}
|
||||
|
||||
val currentBehaviour: Behaviour?
|
||||
get() = behaviourStack.lastOrNull()
|
||||
|
||||
interface Behaviour {
|
||||
fun processUsages(element: KtElement, parent: KotlinSliceUsage, uniqueProcessor: SliceUsageProcessor)
|
||||
|
||||
val slicePresentationPrefix: String
|
||||
val testPresentationPrefix: String
|
||||
|
||||
override fun equals(other: Any?): Boolean
|
||||
override fun hashCode(): Int
|
||||
}
|
||||
|
||||
class InlineFunctionCall(callElement: KtElement, function: KtNamedFunction) {
|
||||
private val callElementPointer = callElement.createSmartPointer()
|
||||
private val functionPointer = function.createSmartPointer()
|
||||
|
||||
val callElement: KtElement?
|
||||
get() = callElementPointer.element
|
||||
|
||||
val function: KtNamedFunction?
|
||||
get() = functionPointer.element
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (this === other) return true
|
||||
return other is InlineFunctionCall && other.callElement == callElement && other.function == function
|
||||
}
|
||||
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
|
||||
companion object {
|
||||
val Default = KotlinSliceAnalysisMode(emptyList(), emptyList())
|
||||
}
|
||||
}
|
||||
@@ -14,8 +14,8 @@ import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
class KotlinSliceDereferenceUsage(
|
||||
element: PsiElement,
|
||||
parent: KotlinSliceUsage,
|
||||
behaviour: SpecialBehaviour?
|
||||
) : KotlinSliceUsage(element, parent, behaviour, false) {
|
||||
mode: KotlinSliceAnalysisMode
|
||||
) : KotlinSliceUsage(element, parent, mode, false) {
|
||||
override fun processChildren(processor: Processor<in SliceUsage>) {
|
||||
// no children
|
||||
}
|
||||
|
||||
@@ -25,8 +25,8 @@ import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
class KotlinSliceDereferenceUsage(
|
||||
element: PsiElement,
|
||||
parent: KotlinSliceUsage,
|
||||
behaviour: SpecialBehaviour?
|
||||
) : KotlinSliceUsage(element, parent, behaviour, false) {
|
||||
mode: KotlinSliceAnalysisMode
|
||||
) : KotlinSliceUsage(element, parent, mode, false) {
|
||||
override fun processChildren(processor: Processor<SliceUsage>) {
|
||||
// no children
|
||||
}
|
||||
|
||||
@@ -49,6 +49,7 @@ class KotlinSliceProvider : SliceLanguageSupportProvider, SliceUsageTransformer
|
||||
}
|
||||
|
||||
val leafAnalyzer by lazy { SliceLeafAnalyzer(LEAF_ELEMENT_EQUALITY, this) }
|
||||
|
||||
val nullnessAnalyzer: SliceNullnessAnalyzerBase by lazy {
|
||||
object : SliceNullnessAnalyzerBase(LEAF_ELEMENT_EQUALITY, this) {
|
||||
override fun checkNullability(element: PsiElement?): Nullability {
|
||||
@@ -72,7 +73,7 @@ class KotlinSliceProvider : SliceLanguageSupportProvider, SliceUsageTransformer
|
||||
|
||||
override fun transform(usage: SliceUsage): Collection<SliceUsage>? {
|
||||
if (usage is KotlinSliceUsage) return null
|
||||
return listOf(KotlinSliceUsage(usage.element, usage.parent, null, false))
|
||||
return listOf(KotlinSliceUsage(usage.element, usage.parent, KotlinSliceAnalysisMode.Default, false))
|
||||
}
|
||||
|
||||
override fun getExpressionAtCaret(atCaret: PsiElement, dataFlowToThis: Boolean): KtElement? {
|
||||
|
||||
@@ -24,57 +24,61 @@ import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
open class KotlinSliceUsage : SliceUsage {
|
||||
interface SpecialBehaviour {
|
||||
val originalBehaviour: SpecialBehaviour?
|
||||
fun processUsages(element: KtElement, parent: KotlinSliceUsage, uniqueProcessor: SliceUsageProcessor)
|
||||
|
||||
val slicePresentationPrefix: String
|
||||
val testPresentationPrefix: String
|
||||
|
||||
override fun equals(other: Any?): Boolean
|
||||
override fun hashCode(): Int
|
||||
}
|
||||
|
||||
val behaviour: SpecialBehaviour?
|
||||
val mode: KotlinSliceAnalysisMode
|
||||
val forcedExpressionMode: Boolean
|
||||
|
||||
private var usageInfo: UsageInfo? = null
|
||||
|
||||
constructor(
|
||||
element: PsiElement,
|
||||
parent: SliceUsage,
|
||||
behaviour: SpecialBehaviour?,
|
||||
mode: KotlinSliceAnalysisMode,
|
||||
forcedExpressionMode: Boolean,
|
||||
) : super(element, parent) {
|
||||
this.behaviour = behaviour
|
||||
this.mode = mode
|
||||
this.forcedExpressionMode = forcedExpressionMode
|
||||
initializeUsageInfo()
|
||||
}
|
||||
|
||||
constructor(element: PsiElement, params: SliceAnalysisParams) : super(element, params) {
|
||||
this.behaviour = null
|
||||
this.mode = KotlinSliceAnalysisMode.Default
|
||||
this.forcedExpressionMode = false
|
||||
initializeUsageInfo()
|
||||
}
|
||||
|
||||
private fun initializeUsageInfo() {
|
||||
val originalInfo = getUsageInfo()
|
||||
if (mode != KotlinSliceAnalysisMode.Default) {
|
||||
val element = originalInfo.element
|
||||
if (element != null) {
|
||||
usageInfo = UsageInfoWrapper(element, mode)
|
||||
} else {
|
||||
usageInfo = null
|
||||
}
|
||||
} else {
|
||||
usageInfo = originalInfo
|
||||
}
|
||||
}
|
||||
|
||||
// we have to replace UsageInfo with another one whose equality takes into account mode
|
||||
override fun getUsageInfo(): UsageInfo {
|
||||
return usageInfo ?: super.getUsageInfo()
|
||||
}
|
||||
|
||||
override fun copy(): KotlinSliceUsage {
|
||||
val element = usageInfo.element!!
|
||||
val element = getUsageInfo().element!!
|
||||
return if (parent == null)
|
||||
KotlinSliceUsage(element, params)
|
||||
else
|
||||
KotlinSliceUsage(element, parent, behaviour, forcedExpressionMode)
|
||||
KotlinSliceUsage(element, parent, mode, forcedExpressionMode)
|
||||
}
|
||||
|
||||
override fun getUsageInfo(): UsageInfo {
|
||||
val originalInfo = super.getUsageInfo()
|
||||
if (behaviour != null) {
|
||||
val element = originalInfo.element ?: return originalInfo
|
||||
// Do not let IDEA consider usages of the same anonymous function as duplicates when their levels differ
|
||||
return UsageInfoWrapper(element, behaviour)
|
||||
}
|
||||
return originalInfo
|
||||
}
|
||||
|
||||
override fun canBeLeaf() = element != null && behaviour == null
|
||||
override fun canBeLeaf() = element != null && mode == KotlinSliceAnalysisMode.Default
|
||||
|
||||
public override fun processUsagesFlownDownTo(element: PsiElement, uniqueProcessor: SliceUsageProcessor) {
|
||||
val ktElement = element as? KtElement ?: return
|
||||
val behaviour = mode.currentBehaviour
|
||||
if (behaviour != null) {
|
||||
behaviour.processUsages(ktElement, this, uniqueProcessor)
|
||||
} else {
|
||||
@@ -84,6 +88,7 @@ open class KotlinSliceUsage : SliceUsage {
|
||||
|
||||
public override fun processUsagesFlownFromThe(element: PsiElement, uniqueProcessor: SliceUsageProcessor) {
|
||||
val ktElement = element as? KtElement ?: return
|
||||
val behaviour = mode.currentBehaviour
|
||||
if (behaviour != null) {
|
||||
behaviour.processUsages(ktElement, this, uniqueProcessor)
|
||||
} else {
|
||||
@@ -92,9 +97,9 @@ open class KotlinSliceUsage : SliceUsage {
|
||||
}
|
||||
|
||||
@Suppress("EqualsOrHashCode")
|
||||
private class UsageInfoWrapper(element: PsiElement, private val behaviour: SpecialBehaviour) : UsageInfo(element) {
|
||||
private class UsageInfoWrapper(element: PsiElement, private val mode: KotlinSliceAnalysisMode) : UsageInfo(element) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return other is UsageInfoWrapper && super.equals(other) && behaviour == other.behaviour
|
||||
return other is UsageInfoWrapper && super.equals(other) && mode == other.mode
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,10 +43,8 @@ object KotlinSliceUsageCellRenderer : SliceUsageCellRendererBase() {
|
||||
}
|
||||
}
|
||||
|
||||
var behaviour = sliceUsage.behaviour
|
||||
while (behaviour != null) {
|
||||
for (behaviour in sliceUsage.mode.behaviourStack.reversed()) {
|
||||
append(behaviour.slicePresentationPrefix, SimpleTextAttributes.REGULAR_BOLD_ATTRIBUTES)
|
||||
behaviour = behaviour.originalBehaviour
|
||||
}
|
||||
|
||||
val declaration = sliceUsage.element?.parents?.firstOrNull {
|
||||
|
||||
@@ -12,20 +12,17 @@ import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
||||
import org.jetbrains.kotlin.psi.Call
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
data class LambdaCallsBehaviour(
|
||||
val sliceProducer: SliceProducer,
|
||||
override val originalBehaviour: KotlinSliceUsage.SpecialBehaviour?
|
||||
) : KotlinSliceUsage.SpecialBehaviour {
|
||||
|
||||
data class LambdaCallsBehaviour(private val sliceProducer: SliceProducer) : KotlinSliceAnalysisMode.Behaviour {
|
||||
override fun processUsages(element: KtElement, parent: KotlinSliceUsage, uniqueProcessor: SliceUsageProcessor) {
|
||||
val processor = object : SliceUsageProcessor {
|
||||
override fun process(sliceUsage: SliceUsage): Boolean {
|
||||
if (sliceUsage is KotlinSliceUsage && sliceUsage.behaviour === this@LambdaCallsBehaviour) {
|
||||
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) {
|
||||
val newSliceUsage = KotlinSliceUsage(resolvedCall.call.callElement, parent, originalBehaviour, true)
|
||||
return sliceProducer.produceAndProcess(newSliceUsage, originalBehaviour, parent, uniqueProcessor)
|
||||
val originalMode = sliceUsage.mode.dropBehaviour()
|
||||
val newSliceUsage = KotlinSliceUsage(resolvedCall.call.callElement, parent, originalMode, true)
|
||||
return sliceProducer.produceAndProcess(newSliceUsage, originalMode, parent, uniqueProcessor)
|
||||
}
|
||||
}
|
||||
return uniqueProcessor.process(sliceUsage)
|
||||
|
||||
@@ -9,10 +9,7 @@ import org.jetbrains.kotlin.idea.KotlinBundle
|
||||
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
||||
import org.jetbrains.kotlin.psi.KtElement
|
||||
|
||||
data class LambdaResultInflowBehaviour(
|
||||
override val originalBehaviour: KotlinSliceUsage.SpecialBehaviour?
|
||||
) : KotlinSliceUsage.SpecialBehaviour {
|
||||
|
||||
object LambdaResultInflowBehaviour : KotlinSliceAnalysisMode.Behaviour {
|
||||
override fun processUsages(element: KtElement, parent: KotlinSliceUsage, uniqueProcessor: SliceUsageProcessor) {
|
||||
InflowSlicer(element, uniqueProcessor, parent).processChildren(parent.forcedExpressionMode)
|
||||
}
|
||||
@@ -22,4 +19,7 @@ data class LambdaResultInflowBehaviour(
|
||||
|
||||
override val testPresentationPrefix: String
|
||||
get() = "[LAMBDA IN] "
|
||||
|
||||
override fun equals(other: Any?) = other === this
|
||||
override fun hashCode() = 0
|
||||
}
|
||||
@@ -6,6 +6,7 @@
|
||||
package org.jetbrains.kotlin.idea.slicer
|
||||
|
||||
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.PsiMethod
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||
@@ -22,6 +23,7 @@ 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.hasInlineModifier
|
||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||
@@ -200,7 +202,7 @@ class OutflowSlicer(
|
||||
|
||||
is CallInstruction -> {
|
||||
if (!processIfReceiverValue(instruction, pseudoValue)) {
|
||||
instruction.arguments[pseudoValue]?.originalSource?.getPsi()?.passToProcessor()
|
||||
instruction.arguments[pseudoValue]?.originalSource?.getPsi()?.passCalledDeclarationToProcessor(instruction.element)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -226,13 +228,13 @@ class OutflowSlicer(
|
||||
Call.CallType.DEFAULT -> {
|
||||
if (receiverValue == resolvedCall.extensionReceiver) {
|
||||
val targetDeclaration = resolvedCall.resultingDescriptor.originalSource.getPsi()
|
||||
(targetDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessor()
|
||||
(targetDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passCalledDeclarationToProcessor(instruction.element)
|
||||
}
|
||||
}
|
||||
|
||||
Call.CallType.INVOKE -> {
|
||||
if (receiverValue == resolvedCall.dispatchReceiver && behaviour is LambdaCallsBehaviour) {
|
||||
instruction.element.passToProcessor(behaviour)
|
||||
if (receiverValue == resolvedCall.dispatchReceiver && mode.currentBehaviour is LambdaCallsBehaviour) {
|
||||
instruction.element.passCalledDeclarationToProcessor(instruction.element, mode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -258,7 +260,7 @@ class OutflowSlicer(
|
||||
} ?: return
|
||||
|
||||
if (receiver != null && resolvedCall.dispatchReceiver == receiver) {
|
||||
processor.process(KotlinSliceDereferenceUsage(expression, parentUsage, behaviour))
|
||||
processor.process(KotlinSliceDereferenceUsage(expression, parentUsage, mode))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,4 +278,20 @@ class OutflowSlicer(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun PsiElement.passCalledDeclarationToProcessor(
|
||||
callElement: KtElement,
|
||||
mode: KotlinSliceAnalysisMode = this@OutflowSlicer.mode
|
||||
) {
|
||||
var newMode = mode
|
||||
when (this) {
|
||||
is KtParameter -> {
|
||||
val ownerFunction = ownerFunction as? KtNamedFunction
|
||||
if (ownerFunction != null && ownerFunction.hasInlineModifier()) {
|
||||
newMode = mode.withInlineFunctionCall(callElement, ownerFunction)
|
||||
}
|
||||
}
|
||||
}
|
||||
passToProcessor(newMode)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,14 +18,14 @@ import org.jetbrains.kotlin.resolve.scopes.receivers.ImplicitReceiver
|
||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||
|
||||
object ReceiverSliceProducer : SliceProducer {
|
||||
override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection<SliceUsage>? {
|
||||
override fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, 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, forcedExpressionMode = true))
|
||||
return listOf(KotlinSliceUsage(receiver.expression, parent, mode, forcedExpressionMode = true))
|
||||
}
|
||||
|
||||
is ImplicitReceiver -> {
|
||||
@@ -33,13 +33,13 @@ object ReceiverSliceProducer : SliceProducer {
|
||||
?: return emptyList()
|
||||
when (val callableDeclaration = callableDescriptor.originalSource.getPsi()) {
|
||||
is KtFunctionLiteral -> {
|
||||
val newBehaviour = LambdaCallsBehaviour(ReceiverSliceProducer, behaviour)
|
||||
return listOf(KotlinSliceUsage(callableDeclaration, parent, newBehaviour, forcedExpressionMode = true))
|
||||
val newMode = mode.withBehaviour(LambdaCallsBehaviour(ReceiverSliceProducer))
|
||||
return listOf(KotlinSliceUsage(callableDeclaration, parent, newMode, forcedExpressionMode = true))
|
||||
}
|
||||
|
||||
is KtCallableDeclaration -> {
|
||||
val receiverTypeReference = callableDeclaration.receiverTypeReference ?: return emptyList()
|
||||
return listOf(KotlinSliceUsage(receiverTypeReference, parent, behaviour, false))
|
||||
return listOf(KotlinSliceUsage(receiverTypeReference, parent, mode, false))
|
||||
}
|
||||
|
||||
else -> return emptyList()
|
||||
@@ -52,7 +52,7 @@ object ReceiverSliceProducer : SliceProducer {
|
||||
|
||||
else -> {
|
||||
val argument = (refElement.parent as? PsiCall)?.argumentList?.expressions?.getOrNull(0) ?: return emptyList()
|
||||
return listOf(KotlinSliceUsage(argument, parent, behaviour, true))
|
||||
return listOf(KotlinSliceUsage(argument, parent, mode, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,13 +10,13 @@ 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>?
|
||||
fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection<SliceUsage>?
|
||||
|
||||
override fun equals(other: Any?): Boolean
|
||||
override fun hashCode(): Int
|
||||
|
||||
object Trivial : SliceProducer {
|
||||
override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection<SliceUsage>? {
|
||||
override fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection<SliceUsage>? {
|
||||
return null
|
||||
}
|
||||
|
||||
@@ -27,11 +27,11 @@ interface SliceProducer {
|
||||
|
||||
fun SliceProducer.produceAndProcess(
|
||||
sliceUsage: SliceUsage,
|
||||
behaviour: KotlinSliceUsage.SpecialBehaviour?,
|
||||
mode: KotlinSliceAnalysisMode,
|
||||
parentUsage: SliceUsage,
|
||||
processor: SliceUsageProcessor
|
||||
): Boolean {
|
||||
val result = produce(sliceUsage.usageInfo, behaviour, parentUsage) ?: listOf(sliceUsage)
|
||||
val result = produce(sliceUsage.usageInfo, mode, parentUsage) ?: listOf(sliceUsage)
|
||||
for (usage in result) {
|
||||
if (!processor.process(usage)) return false
|
||||
}
|
||||
|
||||
@@ -40,7 +40,7 @@ abstract class Slicer(
|
||||
abstract fun processChildren(forcedExpressionMode: Boolean)
|
||||
|
||||
protected val analysisScope: SearchScope = parentUsage.scope.toSearchScope()
|
||||
protected val behaviour: KotlinSliceUsage.SpecialBehaviour? = parentUsage.behaviour
|
||||
protected val mode: KotlinSliceAnalysisMode = parentUsage.mode
|
||||
protected val project = element.project
|
||||
|
||||
protected class PseudocodeCache {
|
||||
@@ -57,24 +57,21 @@ abstract class Slicer(
|
||||
|
||||
protected val pseudocodeCache = PseudocodeCache()
|
||||
|
||||
protected fun PsiElement.passToProcessor(
|
||||
behaviour: KotlinSliceUsage.SpecialBehaviour? = this@Slicer.behaviour,
|
||||
forcedExpressionMode: Boolean = false,
|
||||
) {
|
||||
processor.process(KotlinSliceUsage(this, parentUsage, behaviour, forcedExpressionMode))
|
||||
protected fun PsiElement.passToProcessor(mode: KotlinSliceAnalysisMode = this@Slicer.mode) {
|
||||
processor.process(KotlinSliceUsage(this, parentUsage, mode, false))
|
||||
}
|
||||
|
||||
protected fun PsiElement.passToProcessorAsValue(behaviour: KotlinSliceUsage.SpecialBehaviour? = this@Slicer.behaviour) {
|
||||
passToProcessor(behaviour, forcedExpressionMode = true)
|
||||
protected fun PsiElement.passToProcessorAsValue(mode: KotlinSliceAnalysisMode = this@Slicer.mode) {
|
||||
processor.process(KotlinSliceUsage(this, parentUsage, mode, forcedExpressionMode = true))
|
||||
}
|
||||
|
||||
protected fun processCalls(
|
||||
protected open fun processCalls(
|
||||
callable: KtCallableDeclaration,
|
||||
includeOverriders: Boolean,
|
||||
sliceProducer: SliceProducer
|
||||
sliceProducer: SliceProducer,
|
||||
) {
|
||||
if (callable is KtFunctionLiteral || callable is KtFunction && callable.name == null) {
|
||||
callable.passToProcessorAsValue(LambdaCallsBehaviour(sliceProducer, behaviour))
|
||||
callable.passToProcessorAsValue(mode.withBehaviour(LambdaCallsBehaviour(sliceProducer)))
|
||||
return
|
||||
}
|
||||
|
||||
@@ -117,8 +114,8 @@ abstract class Slicer(
|
||||
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)
|
||||
val sliceUsage = KotlinSliceUsage(element, parentUsage, mode, false)
|
||||
sliceProducer.produceAndProcess(sliceUsage, mode, parentUsage, processor)
|
||||
}
|
||||
if (includeOverriders) {
|
||||
declaration.processAllUsages(options, usageProcessor)
|
||||
@@ -129,12 +126,12 @@ abstract class Slicer(
|
||||
|
||||
is PsiMethod -> {
|
||||
val sliceUsage = JavaSliceUsage.createRootUsage(declaration, parentUsage.params)
|
||||
sliceProducer.produceAndProcess(sliceUsage, behaviour, parentUsage, processor)
|
||||
sliceProducer.produceAndProcess(sliceUsage, mode, parentUsage, processor)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val sliceUsage = KotlinSliceUsage(declaration, parentUsage, behaviour, false)
|
||||
sliceProducer.produceAndProcess(sliceUsage, behaviour, parentUsage, processor)
|
||||
val sliceUsage = KotlinSliceUsage(declaration, parentUsage, mode, false)
|
||||
sliceProducer.produceAndProcess(sliceUsage, mode, parentUsage, processor)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,8 @@
|
||||
5 val <bold>v = this</bold>
|
||||
5 val v = <bold>this</bold>
|
||||
4 [LAMBDA CALLS] with("A") <bold>{</bold>
|
||||
9 [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
10 return <bold>receiver</bold>.block()
|
||||
9 inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
9 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
10 (INLINE CALL with) return <bold>receiver</bold>.block()
|
||||
9 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
4 with(<bold>"A"</bold>) {
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
5 val <bold>v = this</bold>
|
||||
5 val v = <bold>this</bold>
|
||||
4 [LAMBDA CALLS] with("A") <bold>{</bold>
|
||||
9 [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
10 return <bold>receiver</bold>.block()
|
||||
9 inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
9 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
10 (INLINE CALL with) return <bold>receiver</bold>.block()
|
||||
9 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
4 with(<bold>"A"</bold>) {
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
// FLOW: IN
|
||||
|
||||
fun <caret>Any.extensionFun() {
|
||||
}
|
||||
|
||||
fun String.foo() {
|
||||
with(123) {
|
||||
extensionFun()
|
||||
}
|
||||
|
||||
with(456) {
|
||||
this.extensionFun()
|
||||
}
|
||||
|
||||
with(789) {
|
||||
// no calls here
|
||||
}
|
||||
|
||||
withNoInline(1) {
|
||||
extensionFun()
|
||||
}
|
||||
|
||||
withNoInline(2) {
|
||||
// no calls here
|
||||
}
|
||||
|
||||
"A".let {
|
||||
it.extensionFun()
|
||||
}
|
||||
|
||||
"B".let {
|
||||
// no calls here
|
||||
}
|
||||
|
||||
"D".letNoInline {
|
||||
it.extensionFun()
|
||||
}
|
||||
|
||||
"C".letNoInline {
|
||||
// no calls here
|
||||
}
|
||||
}
|
||||
|
||||
inline fun <T, R> with(receiver: T, block: T.() -> R): R {
|
||||
val result = receiver.block()
|
||||
return result
|
||||
}
|
||||
|
||||
inline fun <T, R> T.let(block: (T) -> R): R {
|
||||
return block(this)
|
||||
}
|
||||
|
||||
fun <T, R> withNoInline(receiver: T, block: T.() -> R): R {
|
||||
val result = receiver.block()
|
||||
return result
|
||||
}
|
||||
|
||||
fun <T, R> T.letNoInline(block: (T) -> R): R {
|
||||
return block(this)
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
35 <bold>"D"</bold>.letNoInline {
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
36 <bold>it</bold>.extensionFun()
|
||||
35 [LAMBDA CALLS] "D".letNoInline <bold>{</bold>
|
||||
58 [LAMBDA CALLS] fun <T, R> T.letNoInline(<bold>block: (T) -> R</bold>): R {
|
||||
59 return block(<bold>this</bold>)
|
||||
58 fun <T, R> <bold>T</bold>.letNoInline(block: (T) -> R): R {
|
||||
35 <bold>"D"</bold>.letNoInline {
|
||||
|
||||
39 <bold>"C"</bold>.letNoInline {
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
36 <bold>it</bold>.extensionFun()
|
||||
35 [LAMBDA CALLS] "D".letNoInline <bold>{</bold>
|
||||
58 [LAMBDA CALLS] fun <T, R> T.letNoInline(<bold>block: (T) -> R</bold>): R {
|
||||
59 return block(<bold>this</bold>)
|
||||
58 fun <T, R> <bold>T</bold>.letNoInline(block: (T) -> R): R {
|
||||
39 <bold>"C"</bold>.letNoInline {
|
||||
|
||||
27 <bold>"A"</bold>.let {
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
28 <bold>it</bold>.extensionFun()
|
||||
27 [LAMBDA CALLS] "A".let <bold>{</bold>
|
||||
49 (INLINE CALL let) [LAMBDA CALLS] inline fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
50 (INLINE CALL let) return block(<bold>this</bold>)
|
||||
49 (INLINE CALL let) inline fun <T, R> <bold>T</bold>.let(block: (T) -> R): R {
|
||||
27 <bold>"A"</bold>.let {
|
||||
|
||||
19 withNoInline(<bold>1</bold>) {
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
19 [LAMBDA CALLS] withNoInline(1) <bold>{</bold>
|
||||
53 [LAMBDA CALLS] fun <T, R> withNoInline(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
54 val result = <bold>receiver</bold>.block()
|
||||
53 fun <T, R> withNoInline(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
19 withNoInline(<bold>1</bold>) {
|
||||
|
||||
7 with(<bold>123</bold>) {
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
7 [LAMBDA CALLS] with(123) <bold>{</bold>
|
||||
44 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
45 (INLINE CALL with) val result = <bold>receiver</bold>.block()
|
||||
44 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
7 with(<bold>123</bold>) {
|
||||
|
||||
23 withNoInline(<bold>2</bold>) {
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
19 [LAMBDA CALLS] withNoInline(1) <bold>{</bold>
|
||||
53 [LAMBDA CALLS] fun <T, R> withNoInline(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
54 val result = <bold>receiver</bold>.block()
|
||||
53 fun <T, R> withNoInline(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
23 withNoInline(<bold>2</bold>) {
|
||||
|
||||
11 with(<bold>456</bold>) {
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
12 <bold>this</bold>.extensionFun()
|
||||
11 [LAMBDA CALLS] with(456) <bold>{</bold>
|
||||
44 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
45 (INLINE CALL with) val result = <bold>receiver</bold>.block()
|
||||
44 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
11 with(<bold>456</bold>) {
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
[NotNull Values]
|
||||
7 with(123) <bold>{</bold>
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
7 [LAMBDA CALLS] with(123) <bold>{</bold>
|
||||
19 [LAMBDA CALLS] withNoInline(1) <bold>{</bold>
|
||||
12 <bold>this</bold>.extensionFun()
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
12 <bold>this</bold>.extensionFun()
|
||||
36 <bold>it</bold>.extensionFun()
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
28 <bold>it</bold>.extensionFun()
|
||||
36 <bold>it</bold>.extensionFun()
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
3 fun <bold>Any</bold>.extensionFun() {
|
||||
7 [LAMBDA CALLS] with(123) <bold>{</bold>
|
||||
44 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
45 (INLINE CALL with) val result = <bold>receiver</bold>.block()
|
||||
44 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
7 with(<bold>123</bold>) {
|
||||
12 <bold>this</bold>.extensionFun()
|
||||
11 [LAMBDA CALLS] with(456) <bold>{</bold>
|
||||
44 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
45 (INLINE CALL with) val result = <bold>receiver</bold>.block()
|
||||
44 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
11 with(<bold>456</bold>) {
|
||||
19 [LAMBDA CALLS] withNoInline(1) <bold>{</bold>
|
||||
53 [LAMBDA CALLS] fun <T, R> withNoInline(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
54 val result = <bold>receiver</bold>.block()
|
||||
53 fun <T, R> withNoInline(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
19 withNoInline(<bold>1</bold>) {
|
||||
23 withNoInline(<bold>2</bold>) {
|
||||
28 <bold>it</bold>.extensionFun()
|
||||
27 [LAMBDA CALLS] "A".let <bold>{</bold>
|
||||
49 (INLINE CALL let) [LAMBDA CALLS] inline fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
50 (INLINE CALL let) return block(<bold>this</bold>)
|
||||
49 (INLINE CALL let) inline fun <T, R> <bold>T</bold>.let(block: (T) -> R): R {
|
||||
27 <bold>"A"</bold>.let {
|
||||
36 <bold>it</bold>.extensionFun()
|
||||
35 [LAMBDA CALLS] "D".letNoInline <bold>{</bold>
|
||||
58 [LAMBDA CALLS] fun <T, R> T.letNoInline(<bold>block: (T) -> R</bold>): R {
|
||||
59 return block(<bold>this</bold>)
|
||||
58 fun <T, R> <bold>T</bold>.letNoInline(block: (T) -> R): R {
|
||||
35 <bold>"D"</bold>.letNoInline {
|
||||
39 <bold>"C"</bold>.letNoInline {
|
||||
@@ -1,18 +1,18 @@
|
||||
5 fun bar(<bold>a: Int</bold>): Int = foo(a) { if (it > 0) it else return 0 }
|
||||
5 fun <bold>bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 }</bold>
|
||||
5 fun bar(a: Int): Int = <bold>foo(a) { if (it > 0) it else return 0 }</bold>
|
||||
3 inline fun <bold>foo(a: Int, f: (Int) -> Int) = f(a)</bold>
|
||||
3 inline fun foo(a: Int, f: (Int) -> Int) = <bold>f(a)</bold>
|
||||
3 [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = <bold>f</bold>(a)
|
||||
3 [LAMBDA IN] inline fun foo(a: Int, <bold>f: (Int) -> Int</bold>) = f(a)
|
||||
3 (INLINE CALL foo) inline fun <bold>foo(a: Int, f: (Int) -> Int) = f(a)</bold>
|
||||
3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = <bold>f(a)</bold>
|
||||
3 (INLINE CALL foo) [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = <bold>f</bold>(a)
|
||||
3 (INLINE CALL foo) [LAMBDA IN] inline fun foo(a: Int, <bold>f: (Int) -> Int</bold>) = f(a)
|
||||
5 [LAMBDA IN] fun bar(a: Int): Int = foo(a) <bold>{ if (it > 0) it else return 0 }</bold>
|
||||
5 fun bar(a: Int): Int = foo(a) <bold>{ if (it > 0) it else return 0 }</bold>
|
||||
5 fun bar(a: Int): Int = foo(a) { <bold>if (it > 0) it else return 0</bold> }
|
||||
5 fun bar(a: Int): Int = foo(a) { if (it > 0) <bold>it</bold> else return 0 }
|
||||
5 [LAMBDA CALLS] fun bar(a: Int): Int = foo(a) <bold>{ if (it > 0) it else return 0 }</bold>
|
||||
3 [LAMBDA CALLS] inline fun foo(a: Int, <bold>f: (Int) -> Int</bold>) = f(a)
|
||||
3 inline fun foo(a: Int, f: (Int) -> Int) = f(<bold>a</bold>)
|
||||
3 inline fun foo(<bold>a: Int</bold>, f: (Int) -> Int) = f(a)
|
||||
3 (INLINE CALL foo) [LAMBDA CALLS] inline fun foo(a: Int, <bold>f: (Int) -> Int</bold>) = f(a)
|
||||
3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(<bold>a</bold>)
|
||||
3 (INLINE CALL foo) inline fun foo(<bold>a: Int</bold>, f: (Int) -> Int) = f(a)
|
||||
5 fun bar(a: Int): Int = foo(<bold>a</bold>) { if (it > 0) it else return 0 }
|
||||
5 fun bar(<bold>a: Int</bold>): Int = foo(a) { if (it > 0) it else return 0 }
|
||||
|
||||
|
||||
+7
-7
@@ -1,17 +1,17 @@
|
||||
5 fun <bold>bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 }</bold>
|
||||
5 fun bar(a: Int): Int = <bold>foo(a) { if (it > 0) it else return 0 }</bold>
|
||||
3 inline fun <bold>foo(a: Int, f: (Int) -> Int) = f(a)</bold>
|
||||
3 inline fun foo(a: Int, f: (Int) -> Int) = <bold>f(a)</bold>
|
||||
3 [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = <bold>f</bold>(a)
|
||||
3 [LAMBDA IN] inline fun foo(a: Int, <bold>f: (Int) -> Int</bold>) = f(a)
|
||||
3 (INLINE CALL foo) inline fun <bold>foo(a: Int, f: (Int) -> Int) = f(a)</bold>
|
||||
3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = <bold>f(a)</bold>
|
||||
3 (INLINE CALL foo) [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = <bold>f</bold>(a)
|
||||
3 (INLINE CALL foo) [LAMBDA IN] inline fun foo(a: Int, <bold>f: (Int) -> Int</bold>) = f(a)
|
||||
5 [LAMBDA IN] fun bar(a: Int): Int = foo(a) <bold>{ if (it > 0) it else return 0 }</bold>
|
||||
5 fun bar(a: Int): Int = foo(a) <bold>{ if (it > 0) it else return 0 }</bold>
|
||||
5 fun bar(a: Int): Int = foo(a) { <bold>if (it > 0) it else return 0</bold> }
|
||||
5 fun bar(a: Int): Int = foo(a) { if (it > 0) <bold>it</bold> else return 0 }
|
||||
5 [LAMBDA CALLS] fun bar(a: Int): Int = foo(a) <bold>{ if (it > 0) it else return 0 }</bold>
|
||||
3 [LAMBDA CALLS] inline fun foo(a: Int, <bold>f: (Int) -> Int</bold>) = f(a)
|
||||
3 inline fun foo(a: Int, f: (Int) -> Int) = f(<bold>a</bold>)
|
||||
3 inline fun foo(<bold>a: Int</bold>, f: (Int) -> Int) = f(a)
|
||||
3 (INLINE CALL foo) [LAMBDA CALLS] inline fun foo(a: Int, <bold>f: (Int) -> Int</bold>) = f(a)
|
||||
3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(<bold>a</bold>)
|
||||
3 (INLINE CALL foo) inline fun foo(<bold>a: Int</bold>, f: (Int) -> Int) = f(a)
|
||||
5 fun bar(a: Int): Int = foo(<bold>a</bold>) { if (it > 0) it else return 0 }
|
||||
5 fun bar(<bold>a: Int</bold>): Int = foo(a) { if (it > 0) it else return 0 }
|
||||
5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return <bold>0</bold> }
|
||||
|
||||
@@ -18,8 +18,8 @@
|
||||
18 with(<bold>123</bold>) {
|
||||
8 fun <bold>Any</bold>.extensionFun() {
|
||||
18 [LAMBDA CALLS] with(123) <bold>{</bold>
|
||||
27 [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
28 return <bold>receiver</bold>.block()
|
||||
27 inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
27 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
28 (INLINE CALL with) return <bold>receiver</bold>.block()
|
||||
27 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
18 with(<bold>123</bold>) {
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
12 <bold>""</bold>.extensionFun()
|
||||
14 <bold>1</bold>.extensionFun()
|
||||
18 [LAMBDA CALLS] with(123) <bold>{</bold>
|
||||
27 [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
28 return <bold>receiver</bold>.block()
|
||||
27 inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
27 (INLINE CALL with) [LAMBDA CALLS] inline fun <T, R> with(receiver: T, <bold>block: T.() -> R</bold>): R {
|
||||
28 (INLINE CALL with) return <bold>receiver</bold>.block()
|
||||
27 (INLINE CALL with) inline fun <T, R> with(<bold>receiver: T</bold>, block: T.() -> R): R {
|
||||
18 with(<bold>123</bold>) {
|
||||
|
||||
+5
@@ -178,6 +178,11 @@ public class SlicerLeafGroupingTestGenerated extends AbstractSlicerLeafGroupingT
|
||||
runTest("idea/testData/slicer/inflow/ifExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineFunctionManyCalls.kt")
|
||||
public void testInlineFunctionManyCalls() throws Exception {
|
||||
runTest("idea/testData/slicer/inflow/inlineFunctionManyCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaImplicitParameter.kt")
|
||||
public void testLambdaImplicitParameter() throws Exception {
|
||||
runTest("idea/testData/slicer/inflow/lambdaImplicitParameter.kt");
|
||||
|
||||
+5
@@ -178,6 +178,11 @@ public class SlicerNullnessGroupingTestGenerated extends AbstractSlicerNullnessG
|
||||
runTest("idea/testData/slicer/inflow/ifExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineFunctionManyCalls.kt")
|
||||
public void testInlineFunctionManyCalls() throws Exception {
|
||||
runTest("idea/testData/slicer/inflow/inlineFunctionManyCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaImplicitParameter.kt")
|
||||
public void testLambdaImplicitParameter() throws Exception {
|
||||
runTest("idea/testData/slicer/inflow/lambdaImplicitParameter.kt");
|
||||
|
||||
@@ -52,8 +52,10 @@ internal fun buildTreeRepresentation(rootNode: SliceNode): String {
|
||||
node.sortedChildren.forEach { append(process(it, indent)) }
|
||||
return@buildString
|
||||
}
|
||||
|
||||
// SliceLeafValueClassNode is package-private
|
||||
node.isSliceLeafValueClassNode() -> append("[${node.nodeText}]\n")
|
||||
|
||||
else -> {
|
||||
val chunks = usage.text
|
||||
append(chunks.first().render() + " ")
|
||||
@@ -62,11 +64,10 @@ internal fun buildTreeRepresentation(rootNode: SliceNode): String {
|
||||
append("DEREFERENCE: ")
|
||||
}
|
||||
if (usage is KotlinSliceUsage) {
|
||||
var behaviour = usage.behaviour
|
||||
while (behaviour != null) {
|
||||
append(behaviour.testPresentationPrefix)
|
||||
behaviour = behaviour.originalBehaviour
|
||||
usage.mode.inlineCallStack.forEach {
|
||||
append("(INLINE CALL ${it.function?.name}) ")
|
||||
}
|
||||
usage.mode.behaviourStack.reversed().joinTo(this, separator = "") { it.testPresentationPrefix }
|
||||
}
|
||||
chunks.slice(1 until chunks.size).joinTo(
|
||||
this,
|
||||
|
||||
@@ -190,6 +190,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
|
||||
runTest("idea/testData/slicer/inflow/ifExpression.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("inlineFunctionManyCalls.kt")
|
||||
public void testInlineFunctionManyCalls() throws Exception {
|
||||
runTest("idea/testData/slicer/inflow/inlineFunctionManyCalls.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("lambdaImplicitParameter.kt")
|
||||
public void testLambdaImplicitParameter() throws Exception {
|
||||
runTest("idea/testData/slicer/inflow/lambdaImplicitParameter.kt");
|
||||
|
||||
Reference in New Issue
Block a user