Tracking of incoming lambda's for outflow analysis
This commit is contained in:
@@ -6,12 +6,10 @@
|
|||||||
package org.jetbrains.kotlin.idea.slicer
|
package org.jetbrains.kotlin.idea.slicer
|
||||||
|
|
||||||
import com.intellij.psi.PsiCall
|
import com.intellij.psi.PsiCall
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.search.LocalSearchScope
|
import com.intellij.psi.search.LocalSearchScope
|
||||||
import com.intellij.psi.search.SearchScope
|
import com.intellij.psi.search.SearchScope
|
||||||
import com.intellij.psi.search.searches.ReferencesSearch
|
import com.intellij.psi.search.searches.ReferencesSearch
|
||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
|
||||||
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
import org.jetbrains.kotlin.builtins.functions.FunctionInvokeDescriptor
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
import org.jetbrains.kotlin.cfg.pseudocode.instructions.Instruction
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.instructions.eval.*
|
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.refactoring.changeSignature.toValVar
|
||||||
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
|
import org.jetbrains.kotlin.idea.references.KtPropertyDelegationMethodsReference
|
||||||
import org.jetbrains.kotlin.idea.references.ReferenceAccess
|
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.references.readWriteAccessWithFullExpression
|
||||||
import org.jetbrains.kotlin.idea.search.declarationsSearch.HierarchySearchRequest
|
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinTargetElementEvaluator
|
||||||
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.lexer.KtTokens
|
||||||
import org.jetbrains.kotlin.psi.*
|
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.forEachDescendantOfType
|
||||||
|
import org.jetbrains.kotlin.psi.psiUtil.getQualifiedExpressionForSelector
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
import org.jetbrains.kotlin.psi.psiUtil.getStrictParentOfType
|
||||||
import org.jetbrains.kotlin.resolve.BindingContext
|
import org.jetbrains.kotlin.resolve.BindingContext
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
@@ -141,10 +138,23 @@ class InflowSlicer(
|
|||||||
is KtNamedFunction -> expression.takeIf { expression.name == null }
|
is KtNamedFunction -> expression.takeIf { expression.name == null }
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
|
val currentBehaviour = mode.currentBehaviour
|
||||||
if (lambda != null) {
|
if (lambda != null) {
|
||||||
if (mode.currentBehaviour is LambdaResultInflowBehaviour) {
|
if (currentBehaviour is LambdaResultInflowBehaviour) {
|
||||||
lambda.passToProcessor(mode.dropBehaviour())
|
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
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,12 +205,12 @@ class InflowSlicer(
|
|||||||
processCalls(functionLiteral, false, ArgumentSliceProducer(parameterDescriptor))
|
processCalls(functionLiteral, false, ArgumentSliceProducer(parameterDescriptor))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
accessedDeclaration.passDeclarationToProcessorWithOverriders(createdAt.element)
|
accessedDeclaration.passDeclarationToProcessorWithOverriders()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
accessedDeclaration?.passDeclarationToProcessorWithOverriders(createdAt.element)
|
accessedDeclaration?.passDeclarationToProcessorWithOverriders()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,9 +226,10 @@ class InflowSlicer(
|
|||||||
val referencedDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return
|
val referencedDescriptor = bindingContext[BindingContext.REFERENCE_TARGET, callableRefExpr.callableReference] ?: return
|
||||||
val referencedDeclaration = (referencedDescriptor as? DeclarationDescriptorWithSource)?.originalSource?.getPsi()
|
val referencedDeclaration = (referencedDescriptor as? DeclarationDescriptorWithSource)?.originalSource?.getPsi()
|
||||||
?: return
|
?: return
|
||||||
if (mode.currentBehaviour is LambdaResultInflowBehaviour) {
|
if (currentBehaviour is LambdaResultInflowBehaviour) {
|
||||||
referencedDeclaration.passToProcessor(mode.dropBehaviour())
|
referencedDeclaration.passToProcessor(mode.dropBehaviour())
|
||||||
}
|
}
|
||||||
|
//TODO: LambdaArgumentInflowBehaviour
|
||||||
}
|
}
|
||||||
|
|
||||||
else -> return
|
else -> return
|
||||||
@@ -231,7 +242,7 @@ class InflowSlicer(
|
|||||||
(resolvedCall.dispatchReceiver as? ExpressionReceiver)?.expression
|
(resolvedCall.dispatchReceiver as? ExpressionReceiver)?.expression
|
||||||
?.passToProcessorAsValue(mode.withBehaviour(LambdaResultInflowBehaviour))
|
?.passToProcessorAsValue(mode.withBehaviour(LambdaResultInflowBehaviour))
|
||||||
} else {
|
} 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) {
|
override fun processCalls(callable: KtCallableDeclaration, includeOverriders: Boolean, sliceProducer: SliceProducer) {
|
||||||
if (callable is KtNamedFunction) {
|
if (callable is KtNamedFunction) {
|
||||||
val (newMode, callElement) = mode.popInlineFunctionCall(callable)
|
val (newMode, callElement) = mode.popInlineFunctionCall(callable)
|
||||||
@@ -339,4 +329,12 @@ class InflowSlicer(
|
|||||||
|
|
||||||
super.processCalls(callable, includeOverriders, sliceProducer)
|
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
|
package org.jetbrains.kotlin.idea.slicer
|
||||||
|
|
||||||
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access
|
import com.intellij.codeInsight.highlighting.ReadWriteAccessDetector.Access
|
||||||
import com.intellij.psi.PsiElement
|
|
||||||
import com.intellij.psi.PsiMethod
|
import com.intellij.psi.PsiMethod
|
||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
import org.jetbrains.kotlin.cfg.pseudocode.PseudoValue
|
||||||
@@ -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.cfg.pseudocode.instructions.jumps.ReturnValueInstruction
|
||||||
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
import org.jetbrains.kotlin.descriptors.CallableDescriptor
|
||||||
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
import org.jetbrains.kotlin.descriptors.DeclarationDescriptorWithSource
|
||||||
|
import org.jetbrains.kotlin.descriptors.FunctionDescriptor
|
||||||
|
import org.jetbrains.kotlin.descriptors.ValueParameterDescriptor
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
import org.jetbrains.kotlin.idea.caches.resolve.getResolutionFacade
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
import org.jetbrains.kotlin.idea.caches.resolve.resolveToCall
|
||||||
import org.jetbrains.kotlin.idea.caches.resolve.resolveToDescriptorIfAny
|
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.declarationsSearch.forEachOverridingElement
|
||||||
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector
|
import org.jetbrains.kotlin.idea.search.ideaExtensions.KotlinReadWriteAccessDetector
|
||||||
import org.jetbrains.kotlin.idea.util.actualsForExpected
|
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.util.isExpectDeclaration
|
||||||
import org.jetbrains.kotlin.psi.*
|
import org.jetbrains.kotlin.psi.*
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
import org.jetbrains.kotlin.psi.psiUtil.forEachDescendantOfType
|
||||||
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
|
import org.jetbrains.kotlin.psi.psiUtil.parameterIndex
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
import org.jetbrains.kotlin.resolve.source.getPsi
|
import org.jetbrains.kotlin.resolve.source.getPsi
|
||||||
|
import org.jetbrains.kotlin.util.OperatorNameConventions
|
||||||
|
|
||||||
class OutflowSlicer(
|
class OutflowSlicer(
|
||||||
element: KtElement,
|
element: KtElement,
|
||||||
@@ -202,12 +203,46 @@ class OutflowSlicer(
|
|||||||
|
|
||||||
is CallInstruction -> {
|
is CallInstruction -> {
|
||||||
if (!processIfReceiverValue(instruction, pseudoValue)) {
|
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 -> {
|
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 -> {
|
is MagicInstruction -> {
|
||||||
@@ -228,13 +263,13 @@ class OutflowSlicer(
|
|||||||
Call.CallType.DEFAULT -> {
|
Call.CallType.DEFAULT -> {
|
||||||
if (receiverValue == resolvedCall.extensionReceiver) {
|
if (receiverValue == resolvedCall.extensionReceiver) {
|
||||||
val targetDeclaration = resolvedCall.resultingDescriptor.originalSource.getPsi()
|
val targetDeclaration = resolvedCall.resultingDescriptor.originalSource.getPsi()
|
||||||
(targetDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passCalledDeclarationToProcessor(instruction.element)
|
(targetDeclaration as? KtCallableDeclaration)?.receiverTypeReference?.passToProcessorInCallMode(instruction.element)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Call.CallType.INVOKE -> {
|
Call.CallType.INVOKE -> {
|
||||||
if (receiverValue == resolvedCall.dispatchReceiver && mode.currentBehaviour is LambdaCallsBehaviour) {
|
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.slicer.JavaSliceUsage
|
||||||
import com.intellij.usageView.UsageInfo
|
import com.intellij.usageView.UsageInfo
|
||||||
import com.intellij.util.containers.addIfNotNull
|
import com.intellij.util.containers.addIfNotNull
|
||||||
|
import org.jetbrains.kotlin.asJava.namedUnwrappedElement
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
import org.jetbrains.kotlin.cfg.pseudocode.Pseudocode
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
|
import org.jetbrains.kotlin.cfg.pseudocode.containingDeclarationForPseudocode
|
||||||
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
|
import org.jetbrains.kotlin.cfg.pseudocode.getContainingPseudocode
|
||||||
@@ -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.handlers.SliceUsageProcessor
|
||||||
import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages
|
import org.jetbrains.kotlin.idea.findUsages.processAllExactUsages
|
||||||
import org.jetbrains.kotlin.idea.findUsages.processAllUsages
|
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.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.*
|
||||||
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
import org.jetbrains.kotlin.resolve.DescriptorUtils
|
||||||
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
import org.jetbrains.kotlin.resolve.lazy.BodyResolveMode
|
||||||
@@ -65,6 +71,52 @@ abstract class Slicer(
|
|||||||
processor.process(KotlinSliceUsage(this, parentUsage, mode, forcedExpressionMode = true))
|
processor.process(KotlinSliceUsage(this, parentUsage, mode, forcedExpressionMode = true))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected fun PsiElement.passToProcessorInCallMode(callElement: KtElement, withOverriders: Boolean = false) {
|
||||||
|
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(
|
protected open fun processCalls(
|
||||||
callable: KtCallableDeclaration,
|
callable: KtCallableDeclaration,
|
||||||
includeOverriders: Boolean,
|
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");
|
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")
|
@TestMetadata("localVariableUsages.kt")
|
||||||
public void testLocalVariableUsages() throws Exception {
|
public void testLocalVariableUsages() throws Exception {
|
||||||
runTest("idea/testData/slicer/outflow/localVariableUsages.kt");
|
runTest("idea/testData/slicer/outflow/localVariableUsages.kt");
|
||||||
@@ -633,6 +638,11 @@ public class SlicerTreeTestGenerated extends AbstractSlicerTreeTest {
|
|||||||
runTest("idea/testData/slicer/outflow/memberPropertyUsages.kt");
|
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")
|
@TestMetadata("nonLocalReturn.kt")
|
||||||
public void testNonLocalReturn() throws Exception {
|
public void testNonLocalReturn() throws Exception {
|
||||||
runTest("idea/testData/slicer/outflow/nonLocalReturn.kt");
|
runTest("idea/testData/slicer/outflow/nonLocalReturn.kt");
|
||||||
|
|||||||
Reference in New Issue
Block a user