Tracking of incoming lambda's for outflow analysis
This commit is contained in:
@@ -6,12 +6,10 @@
|
||||
package org.jetbrains.kotlin.idea.slicer
|
||||
|
||||
import com.intellij.psi.PsiCall
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.search.LocalSearchScope
|
||||
import com.intellij.psi.search.SearchScope
|
||||
import com.intellij.psi.search.searches.ReferencesSearch
|
||||
import com.intellij.usageView.UsageInfo
|
||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
||||
@@ -27,15 +25,14 @@ import org.jetbrains.kotlin.idea.refactoring.changeSignature.KotlinValVar
|
||||
import org.jetbrains.kotlin.idea.refactoring.changeSignature.toValVar
|
||||
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
|
||||
import org.jetbrains.kotlin.idea.references.ReferenceAccess
|
||||
import org.jetbrains.kotlin.idea.references.mainReference
|
||||
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.idea.search.ideaExtensions.KotlinTargetElementEvaluator
|
||||
import org.jetbrains.kotlin.lexer.KtTokens
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.psi.psiUtil.collectDescendantsOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
|
||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||
import org.jetbrains.kotlin.resolve.BindingContext
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
@@ -141,10 +138,23 @@ class InflowSlicer(
|
||||
is KtNamedFunction -> expression.takeIf { expression.name == null }
|
||||
else -> null
|
||||
}
|
||||
val currentBehaviour = mode.currentBehaviour
|
||||
if (lambda != null) {
|
||||
if (mode.currentBehaviour is LambdaResultInflowBehaviour) {
|
||||
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())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
valueParameters.getOrNull(currentBehaviour.argumentIndex)?.passToProcessor(mode.dropBehaviour())
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -195,12 +205,12 @@ class InflowSlicer(
|
||||
processCalls(functionLiteral, false, ArgumentSliceProducer(parameterDescriptor))
|
||||
}
|
||||
} else {
|
||||
accessedDeclaration.passDeclarationToProcessorWithOverriders(createdAt.element)
|
||||
accessedDeclaration.passDeclarationToProcessorWithOverriders()
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
accessedDeclaration?.passDeclarationToProcessorWithOverriders(createdAt.element)
|
||||
accessedDeclaration?.passDeclarationToProcessorWithOverriders()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -216,9 +226,10 @@ class InflowSlicer(
|
||||
val referencedDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return
|
||||
val referencedDeclaration = (referencedDescriptor as? DeclarationDescriptorWithSource)?.originalSource?.getPsi()
|
||||
?: return
|
||||
if (mode.currentBehaviour is LambdaResultInflowBehaviour) {
|
||||
if (currentBehaviour is LambdaResultInflowBehaviour) {
|
||||
referencedDeclaration.passToProcessor(mode.dropBehaviour())
|
||||
}
|
||||
//TODO: LambdaArgumentInflowBehaviour
|
||||
}
|
||||
|
||||
else -> return
|
||||
@@ -231,7 +242,7 @@ class InflowSlicer(
|
||||
(resolvedCall.dispatchReceiver as? ExpressionReceiver)?.expression
|
||||
?.passToProcessorAsValue(mode.withBehaviour(LambdaResultInflowBehaviour))
|
||||
} else {
|
||||
resultingDescriptor.originalSource.getPsi()?.passDeclarationToProcessorWithOverriders(createdAt.element)
|
||||
resultingDescriptor.originalSource.getPsi()?.passToProcessorInCallMode(createdAt.element, withOverriders = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -306,27 +317,6 @@ class InflowSlicer(
|
||||
}
|
||||
}
|
||||
|
||||
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(newMode) }
|
||||
|
||||
if (this is KtCallableDeclaration && isExpectDeclaration()) {
|
||||
resolveToDescriptorIfAny(BodyResolveMode.FULL)
|
||||
?.actualsForExpected()
|
||||
?.forEach {
|
||||
(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)
|
||||
@@ -339,4 +329,12 @@ class InflowSlicer(
|
||||
|
||||
super.processCalls(callable, includeOverriders, sliceProducer)
|
||||
}
|
||||
|
||||
private fun KtFunctionLiteral.implicitItUsages(): Collection<KtSimpleNameExpression> {
|
||||
return collectDescendantsOfType(fun(expression: KtSimpleNameExpression): Boolean {
|
||||
if (expression.getQualifiedExpressionForSelector() != null || expression.getReferencedName() != "it") return false
|
||||
val lBrace = KotlinTargetElementEvaluator.findLambdaOpenLBraceForGeneratedIt(expression.mainReference) ?: return false
|
||||
return lBrace == this.lBrace.node.treeNext.psi
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
data class LambdaArgumentInflowBehaviour(val argumentIndex: Int) : 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 ARGUMENT IN] "
|
||||
}
|
||||
@@ -6,7 +6,6 @@
|
||||
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
|
||||
@@ -16,6 +15,8 @@ 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
|
||||
@@ -23,13 +24,13 @@ 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
|
||||
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,
|
||||
@@ -202,12 +203,46 @@ class OutflowSlicer(
|
||||
|
||||
is CallInstruction -> {
|
||||
if (!processIfReceiverValue(instruction, pseudoValue)) {
|
||||
instruction.arguments[pseudoValue]?.originalSource?.getPsi()?.passCalledDeclarationToProcessor(instruction.element)
|
||||
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 receiverPseudoValue = instruction.receiverValues.entries.singleOrNull()?.key
|
||||
?: return@processPseudocodeUsages
|
||||
if (receiverPseudoValue.createdAt != null) {
|
||||
when (val createdAt = receiverPseudoValue.createdAt) {
|
||||
is ReadValueInstruction -> {
|
||||
val accessedDescriptor = createdAt.target.accessedDescriptor ?: return@processPseudocodeUsages
|
||||
val accessedDeclaration = accessedDescriptor.originalSource.getPsi() ?: return@processPseudocodeUsages
|
||||
when (accessedDescriptor) {
|
||||
is ValueParameterDescriptor -> {
|
||||
//TODO: argument index is not always correct - first argument can be receiver
|
||||
val newMode = mode.withBehaviour(LambdaArgumentInflowBehaviour(accessedDescriptor.index))
|
||||
accessedDeclaration.passToProcessor(newMode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is ReturnValueInstruction -> {
|
||||
instruction.subroutine.passToProcessor()
|
||||
val subroutine = instruction.subroutine
|
||||
if (subroutine is KtNamedFunction) {
|
||||
val (newMode, callElement) = mode.popInlineFunctionCall(subroutine)
|
||||
if (newMode != null) {
|
||||
callElement?.passToProcessor(newMode)
|
||||
return@processPseudocodeUsages
|
||||
}
|
||||
}
|
||||
|
||||
subroutine.passToProcessor()
|
||||
}
|
||||
|
||||
is MagicInstruction -> {
|
||||
@@ -228,13 +263,13 @@ class OutflowSlicer(
|
||||
Call.CallType.DEFAULT -> {
|
||||
if (receiverValue == resolvedCall.extensionReceiver) {
|
||||
val targetDeclaration = resolvedCall.resultingDescriptor.originalSource.getPsi()
|
||||
(targetDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passCalledDeclarationToProcessor(instruction.element)
|
||||
(targetDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessorInCallMode(instruction.element)
|
||||
}
|
||||
}
|
||||
|
||||
Call.CallType.INVOKE -> {
|
||||
if (receiverValue == resolvedCall.dispatchReceiver && mode.currentBehaviour is LambdaCallsBehaviour) {
|
||||
instruction.element.passCalledDeclarationToProcessor(instruction.element, mode)
|
||||
instruction.element.passToProcessor()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -278,20 +313,4 @@ 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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@ import com.intellij.psi.search.SearchScope
|
||||
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.Pseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
|
||||
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
|
||||
@@ -25,7 +26,12 @@ import org.jetbrains.kotlin.idea.findUsages.KotlinPropertyFindUsagesOptions
|
||||
import org.jetbrains.kotlin.idea.findUsages.handlers.SliceUsageProcessor
|
||||
import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages
|
||||
import org.jetbrains.kotlin.idea.findUsages.processAllUsages
|
||||
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.expectedDescriptor
|
||||
import org.jetbrains.kotlin.idea.util.hasInlineModifier
|
||||
import org.jetbrains.kotlin.idea.util.isExpectDeclaration
|
||||
import org.jetbrains.kotlin.psi.*
|
||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||
@@ -65,6 +71,52 @@ abstract class Slicer(
|
||||
processor.process(KotlinSliceUsage(this, parentUsage, mode, forcedExpressionMode = true))
|
||||
}
|
||||
|
||||
protected fun PsiElement.passToProcessorInCallMode(callElement: KtElement, withOverriders: Boolean = false) {
|
||||
val newMode = when (this) {
|
||||
is KtNamedFunction -> this.callMode(callElement)
|
||||
|
||||
is KtParameter -> ownerFunction.callMode(callElement)
|
||||
|
||||
is KtTypeReference -> {
|
||||
val declaration = parent
|
||||
require(declaration is KtCallableDeclaration)
|
||||
require(this == declaration.receiverTypeReference)
|
||||
declaration.callMode(callElement)
|
||||
}
|
||||
|
||||
else -> mode
|
||||
}
|
||||
|
||||
if (withOverriders) {
|
||||
passDeclarationToProcessorWithOverriders(newMode)
|
||||
} else {
|
||||
passToProcessor(newMode)
|
||||
}
|
||||
}
|
||||
|
||||
protected fun PsiElement.passDeclarationToProcessorWithOverriders(mode: KotlinSliceAnalysisMode = this@Slicer.mode) {
|
||||
passToProcessor(mode)
|
||||
|
||||
HierarchySearchRequest(this, analysisScope)
|
||||
.searchOverriders()
|
||||
.forEach { it.namedUnwrappedElement?.passToProcessor(mode) }
|
||||
|
||||
if (this is KtCallableDeclaration && isExpectDeclaration()) {
|
||||
resolveToDescriptorIfAny(BodyResolveMode.FULL)
|
||||
?.actualsForExpected()
|
||||
?.forEach {
|
||||
(it as? DeclarationDescriptorWithSource)?.originalSource?.getPsi()?.passToProcessor(mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// FLOW: OUT
|
||||
|
||||
fun foo(<caret>p: String) {
|
||||
val v1 = p.let { value -> bar(value) }
|
||||
|
||||
val v2 = p.let { it }
|
||||
|
||||
val v3 = p.let {
|
||||
val it = "a"
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
fun bar(s: String) = s
|
||||
|
||||
inline fun <T, R> T.let(block: (T) -> R): R {
|
||||
return block(this)
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
3 fun foo(<bold>p: String</bold>) {
|
||||
4 val v1 = <bold>p</bold>.let { value -> bar(value) }
|
||||
16 (INLINE CALL let) inline fun <T, R> <bold>T</bold>.let(block: (T) -> R): R {
|
||||
17 (INLINE CALL let) return block(<bold>this</bold>)
|
||||
16 (INLINE CALL let) [LAMBDA ARGUMENT IN] inline fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
4 [LAMBDA ARGUMENT IN] val v1 = p.let <bold>{ value -> bar(value) }</bold>
|
||||
4 val v1 = p.let { <bold>value</bold> -> bar(value) }
|
||||
4 val v1 = p.let { value -> bar(<bold>value</bold>) }
|
||||
14 fun bar(<bold>s: String</bold>) = s
|
||||
14 fun bar(s: String) = <bold>s</bold>
|
||||
14 fun <bold>bar(s: String) = s</bold>
|
||||
4 val v1 = p.let { value -> <bold>bar(value)</bold> }
|
||||
4 val v1 = p.let <bold>{ value -> bar(value) }</bold>
|
||||
4 [LAMBDA CALLS] val v1 = p.let <bold>{ value -> bar(value) }</bold>
|
||||
16 (INLINE CALL let) [LAMBDA CALLS] inline fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
17 (INLINE CALL let) return <bold>block(this)</bold>
|
||||
4 val v1 = p.<bold>let { value -> bar(value) }</bold>
|
||||
4 val <bold>v1 = p.let { value -> bar(value) }</bold>
|
||||
6 val v2 = <bold>p</bold>.let { it }
|
||||
16 (INLINE CALL let) inline fun <T, R> <bold>T</bold>.let(block: (T) -> R): R {
|
||||
17 (INLINE CALL let) return block(<bold>this</bold>)
|
||||
16 (INLINE CALL let) [LAMBDA ARGUMENT IN] inline fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
6 [LAMBDA ARGUMENT IN] val v2 = p.let <bold>{ it }</bold>
|
||||
6 val v2 = p.let { <bold>it</bold> }
|
||||
6 val v2 = p.let <bold>{ it }</bold>
|
||||
6 [LAMBDA CALLS] val v2 = p.let <bold>{ it }</bold>
|
||||
16 (INLINE CALL let) [LAMBDA CALLS] inline fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
17 (INLINE CALL let) return <bold>block(this)</bold>
|
||||
6 val v2 = p.<bold>let { it }</bold>
|
||||
6 val <bold>v2 = p.let { it }</bold>
|
||||
8 val v3 = <bold>p</bold>.let {
|
||||
16 (INLINE CALL let) inline fun <T, R> <bold>T</bold>.let(block: (T) -> R): R {
|
||||
17 (INLINE CALL let) return block(<bold>this</bold>)
|
||||
16 (INLINE CALL let) [LAMBDA ARGUMENT IN] inline fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
8 [LAMBDA ARGUMENT IN] val v3 = p.let <bold>{</bold>
|
||||
@@ -0,0 +1,18 @@
|
||||
// FLOW: OUT
|
||||
|
||||
fun foo(<caret>p: String) {
|
||||
val v1 = p.let { value -> bar(value) }
|
||||
|
||||
val v2 = p.let { it }
|
||||
|
||||
val v3 = p.let {
|
||||
val it = "a"
|
||||
it
|
||||
}
|
||||
}
|
||||
|
||||
fun bar(s: String) = s
|
||||
|
||||
fun <T, R> T.let(block: (T) -> R): R {
|
||||
return block(this)
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
3 fun foo(<bold>p: String</bold>) {
|
||||
4 val v1 = <bold>p</bold>.let { value -> bar(value) }
|
||||
16 fun <T, R> <bold>T</bold>.let(block: (T) -> R): R {
|
||||
17 return block(<bold>this</bold>)
|
||||
16 [LAMBDA ARGUMENT IN] fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
4 [LAMBDA ARGUMENT IN] val v1 = p.let <bold>{ value -> bar(value) }</bold>
|
||||
4 val v1 = p.let { <bold>value</bold> -> bar(value) }
|
||||
4 val v1 = p.let { value -> bar(<bold>value</bold>) }
|
||||
14 fun bar(<bold>s: String</bold>) = s
|
||||
14 fun bar(s: String) = <bold>s</bold>
|
||||
14 fun <bold>bar(s: String) = s</bold>
|
||||
4 val v1 = p.let { value -> <bold>bar(value)</bold> }
|
||||
4 val v1 = p.let <bold>{ value -> bar(value) }</bold>
|
||||
4 [LAMBDA CALLS] val v1 = p.let <bold>{ value -> bar(value) }</bold>
|
||||
16 [LAMBDA CALLS] fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
17 return <bold>block(this)</bold>
|
||||
16 fun <T, R> T.<bold>let(block: (T) -> R): R {</bold>
|
||||
4 val v1 = p.<bold>let { value -> bar(value) }</bold>
|
||||
4 val <bold>v1 = p.let { value -> bar(value) }</bold>
|
||||
6 val v2 = p.<bold>let { it }</bold>
|
||||
6 val <bold>v2 = p.let { it }</bold>
|
||||
8 val v3 = p.<bold>let {</bold>
|
||||
8 val <bold>v3 = p.let {</bold>
|
||||
6 [LAMBDA ARGUMENT IN] val v2 = p.let <bold>{ it }</bold>
|
||||
6 val v2 = p.let { <bold>it</bold> }
|
||||
6 val v2 = p.let <bold>{ it }</bold>
|
||||
6 [LAMBDA CALLS] val v2 = p.let <bold>{ it }</bold>
|
||||
16 [LAMBDA CALLS] DUPLICATE: fun <T, R> T.let(<bold>block: (T) -> R</bold>): R {
|
||||
8 [LAMBDA ARGUMENT IN] val v3 = p.let <bold>{</bold>
|
||||
6 val v2 = <bold>p</bold>.let { it }
|
||||
16 DUPLICATE: fun <T, R> <bold>T</bold>.let(block: (T) -> R): R {
|
||||
8 val v3 = <bold>p</bold>.let {
|
||||
16 DUPLICATE: fun <T, R> <bold>T</bold>.let(block: (T) -> R): R {
|
||||
@@ -623,6 +623,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
|
||||
runTest("idea/testData/slicer/outflow/lambdaResultWithDirectCallViaAssignment.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("letResult.kt")
|
||||
public void testLetResult() throws Exception {
|
||||
runTest("idea/testData/slicer/outflow/letResult.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("localVariableUsages.kt")
|
||||
public void testLocalVariableUsages() throws Exception {
|
||||
runTest("idea/testData/slicer/outflow/localVariableUsages.kt");
|
||||
@@ -633,6 +638,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
|
||||
runTest("idea/testData/slicer/outflow/memberPropertyUsages.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonInlineLetResult.kt")
|
||||
public void testNonInlineLetResult() throws Exception {
|
||||
runTest("idea/testData/slicer/outflow/nonInlineLetResult.kt");
|
||||
}
|
||||
|
||||
@TestMetadata("nonLocalReturn.kt")
|
||||
public void testNonLocalReturn() throws Exception {
|
||||
runTest("idea/testData/slicer/outflow/nonLocalReturn.kt");
|
||||
|
||||
Reference in New Issue
Block a user