diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/ArgumentSliceProducer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/ArgumentSliceProducer.kt index 8b5ca351eb1..c53a800fbd6 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/ArgumentSliceProducer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/ArgumentSliceProducer.kt @@ -32,10 +32,10 @@ data class ArgumentSliceProducer private constructor( parameterDescriptor.containingDeclaration.isExtension ) - override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection? { + override fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection? { 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? { diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/CallSliceProducer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/CallSliceProducer.kt index 86fe5fd39cf..f8e3625a660 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/CallSliceProducer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/CallSliceProducer.kt @@ -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? { - if ((parent as? KotlinSliceUsage)?.behaviour is LambdaCallsBehaviour) { + override fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection? { + 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().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 + ) + } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt index 6dfe879cefc..beba45363e4 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/InflowSlicer.kt @@ -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) + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceAnalysisMode.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceAnalysisMode.kt new file mode 100644 index 00000000000..8681eafb3f9 --- /dev/null +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceAnalysisMode.kt @@ -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, val inlineCallStack: List) { + 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 { + 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()) + } +} diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt index d9ffe471023..81ab8f97e7b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt @@ -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) { // no children } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt.191 b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt.191 index a53590aac48..4659900f14d 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt.191 +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceDereferenceUsage.kt.191 @@ -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) { // no children } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt index 141a465c339..1bfea9556e9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceProvider.kt @@ -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? { 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? { diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt index 1dbec4645d3..aa1893a79ad 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsage.kt @@ -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 } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt index b9669c748af..123bb3b71c3 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/KotlinSliceUsageCellRenderer.kt @@ -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 { diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt index ead09c8685f..1b93e1d1514 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaCallsBehaviour.kt @@ -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) diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaResultInflowBehaviour.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaResultInflowBehaviour.kt index c818e4272fe..1288f09678b 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaResultInflowBehaviour.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/LambdaResultInflowBehaviour.kt @@ -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 } \ No newline at end of file diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt index 9058bec5033..90f7b0cfeeb 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/OutflowSlicer.kt @@ -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) + } } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/ReceiverSliceProducer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/ReceiverSliceProducer.kt index f76fc6ad729..9d3eaaeacb9 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/ReceiverSliceProducer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/ReceiverSliceProducer.kt @@ -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? { + override fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection? { 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)) } } } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/SliceProducer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/SliceProducer.kt index c8c71014884..db8d0571679 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/SliceProducer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/SliceProducer.kt @@ -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? + fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection? override fun equals(other: Any?): Boolean override fun hashCode(): Int object Trivial : SliceProducer { - override fun produce(usage: UsageInfo, behaviour: KotlinSliceUsage.SpecialBehaviour?, parent: SliceUsage): Collection? { + override fun produce(usage: UsageInfo, mode: KotlinSliceAnalysisMode, parent: SliceUsage): Collection? { 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 } diff --git a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt index 62752dae57e..aca267e8a72 100644 --- a/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt +++ b/idea/src/org/jetbrains/kotlin/idea/slicer/Slicer.kt @@ -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) } } } diff --git a/idea/testData/slicer/inflow/extensionLambdaReceiver.leafGroups.txt b/idea/testData/slicer/inflow/extensionLambdaReceiver.leafGroups.txt index 99e59179879..57bf3f8e49d 100644 --- a/idea/testData/slicer/inflow/extensionLambdaReceiver.leafGroups.txt +++ b/idea/testData/slicer/inflow/extensionLambdaReceiver.leafGroups.txt @@ -2,7 +2,8 @@ 5 val v = this 5 val v = this 4 [LAMBDA CALLS] with("A") { -9 [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { -10 return receiver.block() -9 inline fun with(receiver: T, block: T.() -> R): R { +9 (INLINE CALL with) [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { +10 (INLINE CALL with) return receiver.block() +9 (INLINE CALL with) inline fun with(receiver: T, block: T.() -> R): R { 4 with("A") { + diff --git a/idea/testData/slicer/inflow/extensionLambdaReceiver.results.txt b/idea/testData/slicer/inflow/extensionLambdaReceiver.results.txt index 577782e74cf..1243168bbab 100644 --- a/idea/testData/slicer/inflow/extensionLambdaReceiver.results.txt +++ b/idea/testData/slicer/inflow/extensionLambdaReceiver.results.txt @@ -1,7 +1,7 @@ 5 val v = this 5 val v = this 4 [LAMBDA CALLS] with("A") { -9 [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { -10 return receiver.block() -9 inline fun with(receiver: T, block: T.() -> R): R { +9 (INLINE CALL with) [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { +10 (INLINE CALL with) return receiver.block() +9 (INLINE CALL with) inline fun with(receiver: T, block: T.() -> R): R { 4 with("A") { diff --git a/idea/testData/slicer/inflow/inlineFunctionManyCalls.kt b/idea/testData/slicer/inflow/inlineFunctionManyCalls.kt new file mode 100644 index 00000000000..2bbf84d8fe1 --- /dev/null +++ b/idea/testData/slicer/inflow/inlineFunctionManyCalls.kt @@ -0,0 +1,60 @@ +// FLOW: IN + +fun 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 with(receiver: T, block: T.() -> R): R { + val result = receiver.block() + return result +} + +inline fun T.let(block: (T) -> R): R { + return block(this) +} + +fun withNoInline(receiver: T, block: T.() -> R): R { + val result = receiver.block() + return result +} + +fun T.letNoInline(block: (T) -> R): R { + return block(this) +} diff --git a/idea/testData/slicer/inflow/inlineFunctionManyCalls.leafGroups.txt b/idea/testData/slicer/inflow/inlineFunctionManyCalls.leafGroups.txt new file mode 100644 index 00000000000..5d4506757ef --- /dev/null +++ b/idea/testData/slicer/inflow/inlineFunctionManyCalls.leafGroups.txt @@ -0,0 +1,60 @@ +35 "D".letNoInline { +3 fun Any.extensionFun() { +36 it.extensionFun() +35 [LAMBDA CALLS] "D".letNoInline { +58 [LAMBDA CALLS] fun T.letNoInline(block: (T) -> R): R { +59 return block(this) +58 fun T.letNoInline(block: (T) -> R): R { +35 "D".letNoInline { + +39 "C".letNoInline { +3 fun Any.extensionFun() { +36 it.extensionFun() +35 [LAMBDA CALLS] "D".letNoInline { +58 [LAMBDA CALLS] fun T.letNoInline(block: (T) -> R): R { +59 return block(this) +58 fun T.letNoInline(block: (T) -> R): R { +39 "C".letNoInline { + +27 "A".let { +3 fun Any.extensionFun() { +28 it.extensionFun() +27 [LAMBDA CALLS] "A".let { +49 (INLINE CALL let) [LAMBDA CALLS] inline fun T.let(block: (T) -> R): R { +50 (INLINE CALL let) return block(this) +49 (INLINE CALL let) inline fun T.let(block: (T) -> R): R { +27 "A".let { + +19 withNoInline(1) { +3 fun Any.extensionFun() { +19 [LAMBDA CALLS] withNoInline(1) { +53 [LAMBDA CALLS] fun withNoInline(receiver: T, block: T.() -> R): R { +54 val result = receiver.block() +53 fun withNoInline(receiver: T, block: T.() -> R): R { +19 withNoInline(1) { + +7 with(123) { +3 fun Any.extensionFun() { +7 [LAMBDA CALLS] with(123) { +44 (INLINE CALL with) [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { +45 (INLINE CALL with) val result = receiver.block() +44 (INLINE CALL with) inline fun with(receiver: T, block: T.() -> R): R { +7 with(123) { + +23 withNoInline(2) { +3 fun Any.extensionFun() { +19 [LAMBDA CALLS] withNoInline(1) { +53 [LAMBDA CALLS] fun withNoInline(receiver: T, block: T.() -> R): R { +54 val result = receiver.block() +53 fun withNoInline(receiver: T, block: T.() -> R): R { +23 withNoInline(2) { + +11 with(456) { +3 fun Any.extensionFun() { +12 this.extensionFun() +11 [LAMBDA CALLS] with(456) { +44 (INLINE CALL with) [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { +45 (INLINE CALL with) val result = receiver.block() +44 (INLINE CALL with) inline fun with(receiver: T, block: T.() -> R): R { +11 with(456) { + diff --git a/idea/testData/slicer/inflow/inlineFunctionManyCalls.nullnessGroups.txt b/idea/testData/slicer/inflow/inlineFunctionManyCalls.nullnessGroups.txt new file mode 100644 index 00000000000..dddb38b2bf8 --- /dev/null +++ b/idea/testData/slicer/inflow/inlineFunctionManyCalls.nullnessGroups.txt @@ -0,0 +1,13 @@ +[NotNull Values] +7 with(123) { +3 fun Any.extensionFun() { +7 [LAMBDA CALLS] with(123) { +19 [LAMBDA CALLS] withNoInline(1) { +12 this.extensionFun() +3 fun Any.extensionFun() { +12 this.extensionFun() +36 it.extensionFun() +3 fun Any.extensionFun() { +28 it.extensionFun() +36 it.extensionFun() + diff --git a/idea/testData/slicer/inflow/inlineFunctionManyCalls.results.txt b/idea/testData/slicer/inflow/inlineFunctionManyCalls.results.txt new file mode 100644 index 00000000000..5f40fb9cc74 --- /dev/null +++ b/idea/testData/slicer/inflow/inlineFunctionManyCalls.results.txt @@ -0,0 +1,31 @@ +3 fun Any.extensionFun() { +7 [LAMBDA CALLS] with(123) { +44 (INLINE CALL with) [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { +45 (INLINE CALL with) val result = receiver.block() +44 (INLINE CALL with) inline fun with(receiver: T, block: T.() -> R): R { +7 with(123) { +12 this.extensionFun() +11 [LAMBDA CALLS] with(456) { +44 (INLINE CALL with) [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { +45 (INLINE CALL with) val result = receiver.block() +44 (INLINE CALL with) inline fun with(receiver: T, block: T.() -> R): R { +11 with(456) { +19 [LAMBDA CALLS] withNoInline(1) { +53 [LAMBDA CALLS] fun withNoInline(receiver: T, block: T.() -> R): R { +54 val result = receiver.block() +53 fun withNoInline(receiver: T, block: T.() -> R): R { +19 withNoInline(1) { +23 withNoInline(2) { +28 it.extensionFun() +27 [LAMBDA CALLS] "A".let { +49 (INLINE CALL let) [LAMBDA CALLS] inline fun T.let(block: (T) -> R): R { +50 (INLINE CALL let) return block(this) +49 (INLINE CALL let) inline fun T.let(block: (T) -> R): R { +27 "A".let { +36 it.extensionFun() +35 [LAMBDA CALLS] "D".letNoInline { +58 [LAMBDA CALLS] fun T.letNoInline(block: (T) -> R): R { +59 return block(this) +58 fun T.letNoInline(block: (T) -> R): R { +35 "D".letNoInline { +39 "C".letNoInline { diff --git a/idea/testData/slicer/inflow/nonLocalReturn.leafGroups.txt b/idea/testData/slicer/inflow/nonLocalReturn.leafGroups.txt index 05c32a16759..ab4bc6d7bd1 100644 --- a/idea/testData/slicer/inflow/nonLocalReturn.leafGroups.txt +++ b/idea/testData/slicer/inflow/nonLocalReturn.leafGroups.txt @@ -1,18 +1,18 @@ 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } -3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = f(a) 5 [LAMBDA IN] fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 [LAMBDA CALLS] fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } -3 [LAMBDA CALLS] inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) [LAMBDA CALLS] inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(a) 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } diff --git a/idea/testData/slicer/inflow/nonLocalReturn.results.txt b/idea/testData/slicer/inflow/nonLocalReturn.results.txt index 15d63ef1f0b..a22bf7a34d8 100644 --- a/idea/testData/slicer/inflow/nonLocalReturn.results.txt +++ b/idea/testData/slicer/inflow/nonLocalReturn.results.txt @@ -1,17 +1,17 @@ 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } -3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) [LAMBDA IN] inline fun foo(a: Int, f: (Int) -> Int) = f(a) 5 [LAMBDA IN] fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 [LAMBDA CALLS] fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } -3 [LAMBDA CALLS] inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) -3 inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) [LAMBDA CALLS] inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(a) +3 (INLINE CALL foo) inline fun foo(a: Int, f: (Int) -> Int) = f(a) 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } 5 fun bar(a: Int): Int = foo(a) { if (it > 0) it else return 0 } diff --git a/idea/testData/slicer/inflow/onFunctionReceiverType.leafGroups.txt b/idea/testData/slicer/inflow/onFunctionReceiverType.leafGroups.txt index 24f6300d368..730eb38e079 100644 --- a/idea/testData/slicer/inflow/onFunctionReceiverType.leafGroups.txt +++ b/idea/testData/slicer/inflow/onFunctionReceiverType.leafGroups.txt @@ -18,8 +18,8 @@ 18 with(123) { 8 fun Any.extensionFun() { 18 [LAMBDA CALLS] with(123) { -27 [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { -28 return receiver.block() -27 inline fun with(receiver: T, block: T.() -> R): R { +27 (INLINE CALL with) [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { +28 (INLINE CALL with) return receiver.block() +27 (INLINE CALL with) inline fun with(receiver: T, block: T.() -> R): R { 18 with(123) { diff --git a/idea/testData/slicer/inflow/onFunctionReceiverType.results.txt b/idea/testData/slicer/inflow/onFunctionReceiverType.results.txt index 12563321138..195f45364ee 100644 --- a/idea/testData/slicer/inflow/onFunctionReceiverType.results.txt +++ b/idea/testData/slicer/inflow/onFunctionReceiverType.results.txt @@ -5,7 +5,7 @@ 12 "".extensionFun() 14 1.extensionFun() 18 [LAMBDA CALLS] with(123) { -27 [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { -28 return receiver.block() -27 inline fun with(receiver: T, block: T.() -> R): R { +27 (INLINE CALL with) [LAMBDA CALLS] inline fun with(receiver: T, block: T.() -> R): R { +28 (INLINE CALL with) return receiver.block() +27 (INLINE CALL with) inline fun with(receiver: T, block: T.() -> R): R { 18 with(123) { diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java index 97a697a15ff..8f7503f1766 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerLeafGroupingTestGenerated.java @@ -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"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java index fab0949c929..0e5167ae3c6 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerNullnessGroupingTestGenerated.java @@ -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"); diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestUtil.kt b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestUtil.kt index 81f77492767..7e2ea3c72ed 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestUtil.kt +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTestUtil.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, diff --git a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java index a7efb332f2a..55b083f1cd3 100644 --- a/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java +++ b/idea/tests/org/jetbrains/kotlin/idea/slicer/SlicerTreeTestGenerated.java @@ -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");