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