
org.jetbrains.kotlin.fir.resolve.dfa.FirDataFlowAnalyzer.kt Maven / Gradle / Ivy
/*
* Copyright 2010-2019 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.fir.resolve.dfa
import org.jetbrains.kotlin.fir.FirElement
import org.jetbrains.kotlin.fir.FirResolvedCallableReference
import org.jetbrains.kotlin.fir.declarations.FirAnonymousInitializer
import org.jetbrains.kotlin.fir.declarations.FirFunction
import org.jetbrains.kotlin.fir.declarations.FirProperty
import org.jetbrains.kotlin.fir.expressions.*
import org.jetbrains.kotlin.fir.expressions.impl.FirThisReceiverExpressionImpl
import org.jetbrains.kotlin.fir.resolve.BodyResolveComponents
import org.jetbrains.kotlin.fir.resolve.ImplicitReceiverStackImpl
import org.jetbrains.kotlin.fir.resolve.dfa.Condition.*
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
import org.jetbrains.kotlin.fir.resolve.transformers.FirBodyResolveTransformer
import org.jetbrains.kotlin.fir.symbols.AbstractFirBasedSymbol
import org.jetbrains.kotlin.fir.symbols.CallableId
import org.jetbrains.kotlin.fir.symbols.FirSymbolOwner
import org.jetbrains.kotlin.fir.symbols.impl.FirNamedFunctionSymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
import org.jetbrains.kotlin.fir.symbols.impl.FirVariableSymbol
import org.jetbrains.kotlin.fir.types.ConeKotlinType
import org.jetbrains.kotlin.fir.types.coneTypeSafe
import org.jetbrains.kotlin.fir.types.coneTypeUnsafe
import org.jetbrains.kotlin.fir.types.isMarkedNullable
import org.jetbrains.kotlin.ir.expressions.IrConstKind
import org.jetbrains.kotlin.name.FqName
import org.jetbrains.kotlin.name.Name
import org.jetbrains.kotlin.utils.addToStdlib.safeAs
class FirDataFlowAnalyzer(transformer: FirBodyResolveTransformer) : BodyResolveComponents by transformer {
companion object {
private val KOTLIN_BOOLEAN_NOT = CallableId(FqName("kotlin"), FqName("Boolean"), Name.identifier("not"))
}
private val context: DataFlowInferenceContext get() = inferenceComponents.ctx as DataFlowInferenceContext
private val receiverStack: ImplicitReceiverStackImpl = transformer.implicitReceiverStack
private val graphBuilder = ControlFlowGraphBuilder()
private val logicSystem: LogicSystem = LogicSystemImpl(context)
private val variableStorage = DataFlowVariableStorage()
private val flowOnNodes = mutableMapOf, Flow>()
private val variablesForWhenConditions = mutableMapOf()
/*
* If there is no types from smartcasts function returns null
*
* Note that return value based on state of DataFlowAnalyzer (despite of stateless model of old frontend)
*/
fun getTypeUsingSmartcastInfo(qualifiedAccessExpression: FirQualifiedAccessExpression): Collection? {
/*
* DataFlowAnalyzer holds variables only for declarations that have some smartcast (or can have)
* If there is no useful information there is no data flow variable also
*/
val variable = qualifiedAccessExpression.realVariable?.variableUnderAlias ?: return null
return graphBuilder.lastNode.flow.getApprovedInfo(variable)?.exactType ?: return null
}
// ----------------------------------- Named function -----------------------------------
fun enterFunction(function: FirFunction<*>) {
val (functionEnterNode, previousNode) = graphBuilder.enterFunction(function)
if (previousNode == null) {
functionEnterNode.mergeIncomingFlow()
} else {
assert(functionEnterNode.previousNodes.isEmpty())
functionEnterNode.flow = logicSystem.forkFlow(previousNode.flow)
}
}
fun exitFunction(function: FirFunction<*>): ControlFlowGraph? {
val (node, graph) = graphBuilder.exitFunction(function)
node.mergeIncomingFlow()
for (valueParameter in function.valueParameters) {
variableStorage.removeRealVariable(valueParameter.symbol)
}
if (graphBuilder.isTopLevel()) {
flowOnNodes.clear()
variableStorage.reset()
}
return graph
}
// ----------------------------------- Property -----------------------------------
fun enterProperty(property: FirProperty) {
graphBuilder.enterProperty(property).mergeIncomingFlow()
}
fun exitProperty(property: FirProperty): ControlFlowGraph {
val (node, graph) = graphBuilder.exitProperty(property)
node.mergeIncomingFlow()
return graph
}
// ----------------------------------- Block -----------------------------------
fun enterBlock(block: FirBlock) {
graphBuilder.enterBlock(block).mergeIncomingFlow()
}
fun exitBlock(block: FirBlock) {
graphBuilder.exitBlock(block).mergeIncomingFlow()
}
// ----------------------------------- Operator call -----------------------------------
fun exitTypeOperatorCall(typeOperatorCall: FirTypeOperatorCall) {
val node = graphBuilder.exitTypeOperatorCall(typeOperatorCall).mergeIncomingFlow()
if (typeOperatorCall.operation !in FirOperation.TYPES) return
val type = typeOperatorCall.conversionTypeRef.coneTypeSafe() ?: return
val operandVariable = getOrCreateRealVariable(typeOperatorCall.argument)?.variableUnderAlias ?: return
val flow = node.flow
when (typeOperatorCall.operation) {
FirOperation.IS, FirOperation.NOT_IS -> {
val expressionVariable = getOrCreateSyntheticVariable(typeOperatorCall)
val trueInfo = FirDataFlowInfo(setOf(type), emptySet())
val falseInfo = FirDataFlowInfo(emptySet(), setOf(type))
fun chooseInfo(trueBranch: Boolean) =
if ((typeOperatorCall.operation == FirOperation.IS) == trueBranch) trueInfo else falseInfo
logicSystem.addConditionalInfo(
flow,
expressionVariable,
ConditionalFirDataFlowInfo(
EqTrue, operandVariable, chooseInfo(true)
)
)
logicSystem.addConditionalInfo(
flow,
expressionVariable,
ConditionalFirDataFlowInfo(
EqFalse, operandVariable, chooseInfo(false)
)
)
}
FirOperation.AS -> {
logicSystem.addApprovedInfo(flow, operandVariable, FirDataFlowInfo(setOf(type), emptySet()))
}
FirOperation.SAFE_AS -> {
val expressionVariable = getOrCreateSyntheticVariable(typeOperatorCall)
logicSystem.addConditionalInfo(
flow,
expressionVariable,
ConditionalFirDataFlowInfo(
NotEqNull, operandVariable, FirDataFlowInfo(setOf(type), emptySet())
)
)
logicSystem.addConditionalInfo(
flow,
expressionVariable,
ConditionalFirDataFlowInfo(
EqNull, operandVariable, FirDataFlowInfo(emptySet(), setOf(type))
)
)
}
else -> throw IllegalStateException()
}
node.flow = flow
}
fun exitOperatorCall(operatorCall: FirOperatorCall) {
val node = graphBuilder.exitOperatorCall(operatorCall).mergeIncomingFlow()
when (val operation = operatorCall.operation) {
FirOperation.EQ, FirOperation.NOT_EQ, FirOperation.IDENTITY, FirOperation.NOT_IDENTITY -> {
val leftOperand = operatorCall.arguments[0]
val rightOperand = operatorCall.arguments[1]
val leftConst = leftOperand as? FirConstExpression<*>
val rightConst = rightOperand as? FirConstExpression<*>
when {
leftConst?.kind == IrConstKind.Null -> processEqNull(node, rightOperand, operation)
rightConst?.kind == IrConstKind.Null -> processEqNull(node, leftOperand, operation)
leftConst != null -> processEqWithConst(node, rightOperand, leftConst, operation)
rightConst != null -> processEqWithConst(node, leftOperand, rightConst, operation)
operation != FirOperation.EQ && operation != FirOperation.IDENTITY -> return
else -> processEq(node, leftOperand, rightOperand, operation)
}
}
}
}
private fun processEqWithConst(node: OperatorCallNode, operand: FirExpression, const: FirConstExpression<*>, operation: FirOperation) {
val isEq = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> true
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> false
else -> return
}
val expressionVariable = getOrCreateVariable(node.fir)
val flow = node.flow
// not null for comparisons with constants
getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() }?.let { operandVariables ->
operandVariables.forEach { operandVariable ->
logicSystem.addConditionalInfo(
flow,
expressionVariable, ConditionalFirDataFlowInfo(
isEq.toEqBoolean(),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
)
)
}
}
// propagating facts for (... == true) and (... == false)
variableStorage[operand]?.let { operandVariable ->
if (const.kind != IrConstKind.Boolean) return@let
val constValue = (const.value as Boolean)
val shouldInvert = isEq xor constValue
flow.getConditionalInfos(operandVariable).forEach { info ->
logicSystem.addConditionalInfo(flow, expressionVariable, info.let { if (shouldInvert) it.invert() else it })
}
}
}
private fun processEq(node: OperatorCallNode, leftOperand: FirExpression, rightOperand: FirExpression, operation: FirOperation) {
val leftType = leftOperand.typeRef.coneTypeSafe() ?: return
val rightType = rightOperand.typeRef.coneTypeSafe() ?: return
when {
leftType.isMarkedNullable && rightType.isMarkedNullable -> return
leftType.isMarkedNullable -> processEqNull(node, leftOperand, FirOperation.NOT_EQ)
rightType.isMarkedNullable -> processEqNull(node, rightOperand, FirOperation.NOT_EQ)
}
// TODO: process EQUALITY
}
private fun processEqNull(node: OperatorCallNode, operand: FirExpression, operation: FirOperation) {
val flow = node.flow
val expressionVariable = getOrCreateVariable(node.fir)
variableStorage[operand]?.let { operandVariable ->
val condition = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> EqNull
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> NotEqNull
else -> throw IllegalArgumentException()
}
val facts = logicSystem.approveFact(operandVariable, condition, flow)
facts.forEach { (variable, info) ->
logicSystem.addConditionalInfo(
flow,
expressionVariable,
ConditionalFirDataFlowInfo(
EqTrue, variable, info
)
)
logicSystem.addConditionalInfo(
flow,
expressionVariable,
ConditionalFirDataFlowInfo(
EqFalse, variable, info.invert()
)
)
}
node.flow = flow
return
}
val operandVariables = getRealVariablesForSafeCallChain(operand).takeIf { it.isNotEmpty() } ?: return
val condition = when (operation) {
FirOperation.EQ, FirOperation.IDENTITY -> EqFalse
FirOperation.NOT_EQ, FirOperation.NOT_IDENTITY -> EqTrue
else -> throw IllegalArgumentException()
}
operandVariables.forEach { operandVariable ->
logicSystem.addConditionalInfo(
flow,
expressionVariable, ConditionalFirDataFlowInfo(
condition,
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.anyType.coneTypeUnsafe()), emptySet())
)
)
// TODO: design do we need casts to Nothing?
/*
flow = addConditionalInfo(
expressionVariable, UnapprovedFirDataFlowInfo(
eq(conditionValue.invert()!!),
operandVariable,
FirDataFlowInfo(setOf(session.builtinTypes.nullableNothingType.coneTypeUnsafe()), emptySet())
)
)
*/
}
}
// ----------------------------------- Jump -----------------------------------
fun exitJump(jump: FirJump<*>) {
graphBuilder.exitJump(jump).mergeIncomingFlow()
}
// ----------------------------------- When -----------------------------------
fun enterWhenExpression(whenExpression: FirWhenExpression) {
graphBuilder.enterWhenExpression(whenExpression).mergeIncomingFlow()
}
fun enterWhenBranchCondition(whenBranch: FirWhenBranch) {
val node = graphBuilder.enterWhenBranchCondition(whenBranch).mergeIncomingFlow()
val previousNode = node.previousNodes.single()
if (previousNode is WhenBranchConditionExitNode) {
node.flow = logicSystem.approveFactsInsideFlow(
variablesForWhenConditions.remove(previousNode)!!,
EqFalse,
node.flow,
shouldForkFlow = true,
shouldRemoveSynthetics = true
)
}
}
fun exitWhenBranchCondition(whenBranch: FirWhenBranch) {
val (conditionExitNode, branchEnterNode) = graphBuilder.exitWhenBranchCondition(whenBranch)
conditionExitNode.mergeIncomingFlow()
val conditionVariable = getOrCreateVariable(whenBranch.condition)
variablesForWhenConditions[conditionExitNode] = conditionVariable
branchEnterNode.flow = logicSystem.approveFactsInsideFlow(
conditionVariable,
EqTrue,
conditionExitNode.flow,
shouldForkFlow = true,
shouldRemoveSynthetics = false
)
}
fun exitWhenBranchResult(whenBranch: FirWhenBranch) {
graphBuilder.exitWhenBranchResult(whenBranch).mergeIncomingFlow()
}
fun exitWhenExpression(whenExpression: FirWhenExpression) {
val node = graphBuilder.exitWhenExpression(whenExpression)
val previousFlows = node.alivePreviousNodes.map { it.flow }
val flow = logicSystem.joinFlow(previousFlows)
node.flow = flow
// TODO
// val subjectSymbol = whenExpression.subjectVariable?.symbol
// if (subjectSymbol != null) {
// variableStorage[subjectSymbol]?.let { flow = flow.removeVariable(it) }
// }
// node.flow = flow
}
// ----------------------------------- While Loop -----------------------------------
fun enterWhileLoop(loop: FirLoop) {
val (loopEnterNode, loopConditionEnterNode) = graphBuilder.enterWhileLoop(loop)
loopEnterNode.mergeIncomingFlow()
loopConditionEnterNode.mergeIncomingFlow()
}
fun exitWhileLoopCondition(loop: FirLoop) {
val (loopConditionExitNode, loopBlockEnterNode) = graphBuilder.exitWhileLoopCondition(loop)
loopConditionExitNode.mergeIncomingFlow()
loopBlockEnterNode.mergeIncomingFlow()
}
fun exitWhileLoop(loop: FirLoop) {
val (blockExitNode, exitNode) = graphBuilder.exitWhileLoop(loop)
blockExitNode.mergeIncomingFlow()
exitNode.mergeIncomingFlow()
}
// ----------------------------------- Do while Loop -----------------------------------
fun enterDoWhileLoop(loop: FirLoop) {
val (loopEnterNode, loopBlockEnterNode) = graphBuilder.enterDoWhileLoop(loop)
loopEnterNode.mergeIncomingFlow()
loopBlockEnterNode.mergeIncomingFlow()
}
fun enterDoWhileLoopCondition(loop: FirLoop) {
val (loopBlockExitNode, loopConditionEnterNode) = graphBuilder.enterDoWhileLoopCondition(loop)
loopBlockExitNode.mergeIncomingFlow()
loopConditionEnterNode.mergeIncomingFlow()
}
fun exitDoWhileLoop(loop: FirLoop) {
val (loopConditionExitNode, loopExitNode) = graphBuilder.exitDoWhileLoop(loop)
loopConditionExitNode.mergeIncomingFlow()
loopExitNode.mergeIncomingFlow()
}
// ----------------------------------- Try-catch-finally -----------------------------------
fun enterTryExpression(tryExpression: FirTryExpression) {
val (tryExpressionEnterNode, tryMainBlockEnterNode) = graphBuilder.enterTryExpression(tryExpression)
tryExpressionEnterNode.mergeIncomingFlow()
tryMainBlockEnterNode.mergeIncomingFlow()
}
fun exitTryMainBlock(tryExpression: FirTryExpression) {
graphBuilder.exitTryMainBlock(tryExpression).mergeIncomingFlow()
}
fun enterCatchClause(catch: FirCatch) {
graphBuilder.enterCatchClause(catch).mergeIncomingFlow()
}
fun exitCatchClause(catch: FirCatch) {
graphBuilder.exitCatchClause(catch).mergeIncomingFlow()
}
fun enterFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.enterFinallyBlock(tryExpression).mergeIncomingFlow()
}
fun exitFinallyBlock(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitFinallyBlock(tryExpression).mergeIncomingFlow()
}
fun exitTryExpression(tryExpression: FirTryExpression) {
// TODO
graphBuilder.exitTryExpression(tryExpression).mergeIncomingFlow()
}
// ----------------------------------- Resolvable call -----------------------------------
fun exitQualifiedAccessExpression(qualifiedAccessExpression: FirQualifiedAccessExpression) {
graphBuilder.exitQualifiedAccessExpression(qualifiedAccessExpression).mergeIncomingFlow()
}
fun enterFunctionCall(functionCall: FirFunctionCall) {
// TODO: add processing in-place lambdas
}
fun exitFunctionCall(functionCall: FirFunctionCall) {
val node = graphBuilder.exitFunctionCall(functionCall).mergeIncomingFlow()
if (functionCall.isBooleanNot()) {
exitBooleanNot(functionCall, node)
return
}
}
private val FirElement.resolvedSymbol: AbstractFirBasedSymbol<*>?
get() {
val expression = (this as? FirWhenSubjectExpression)?.whenSubject?.whenExpression?.let {
it.subjectVariable?.symbol?.let { symbol -> return symbol }
it.subject
} ?: this
return (expression as? FirResolvable)?.resolvedSymbol
}
private val FirResolvable.resolvedSymbol: AbstractFirBasedSymbol<*>?
get() = (calleeReference as? FirResolvedCallableReference)?.resolvedSymbol
private fun FirFunctionCall.isBooleanNot(): Boolean {
val symbol = calleeReference.safeAs()?.resolvedSymbol as? FirNamedFunctionSymbol ?: return false
return symbol.callableId == KOTLIN_BOOLEAN_NOT
}
fun exitConstExpresion(constExpression: FirConstExpression<*>) {
graphBuilder.exitConstExpresion(constExpression).mergeIncomingFlow()
}
fun exitVariableDeclaration(variable: FirVariable<*>) {
val node = graphBuilder.exitVariableDeclaration(variable).mergeIncomingFlow()
val initializer = variable.initializer ?: return
/*
* That part is needed for cases like that:
*
* val b = x is String
* ...
* if (b) {
* x.length
* }
*/
variableStorage[initializer]?.let { initializerVariable ->
assert(initializerVariable.isSynthetic())
val realVariable = getOrCreateRealVariable(variable.symbol)
logicSystem.changeVariableForConditionFlow(node.flow, initializerVariable, realVariable)
}
initializer.resolvedSymbol?.let { initializerSymbol: AbstractFirBasedSymbol<*> ->
val rhsVariable = getOrCreateRealVariable(initializerSymbol)
variableStorage.createAliasVariable(variable.symbol, rhsVariable)
}
}
fun exitVariableAssignment(assignment: FirVariableAssignment) {
graphBuilder.exitVariableAssignment(assignment).mergeIncomingFlow()
val lhsVariable = variableStorage[assignment.resolvedSymbol ?: return] ?: return
val rhsVariable = variableStorage[assignment.rValue.resolvedSymbol ?: return]?.takeIf { !it.isSynthetic() } ?: return
variableStorage.rebindAliasVariable(lhsVariable, rhsVariable)
}
fun exitThrowExceptionNode(throwExpression: FirThrowExpression) {
graphBuilder.exitThrowExceptionNode(throwExpression).mergeIncomingFlow()
}
// ----------------------------------- Boolean operators -----------------------------------
fun enterBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
graphBuilder.enterBinaryAnd(binaryLogicExpression).mergeIncomingFlow()
}
fun exitLeftBinaryAndArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryAndArgument(binaryLogicExpression)
exitLeftArgumentOfBinaryBooleanOperator(leftNode, rightNode, isAnd = true)
}
fun exitBinaryAnd(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryAnd(binaryLogicExpression)
exitBinaryBooleanOperator(binaryLogicExpression, node, isAnd = true)
}
fun enterBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
graphBuilder.enterBinaryOr(binaryLogicExpression).mergeIncomingFlow()
}
fun exitLeftBinaryOrArgument(binaryLogicExpression: FirBinaryLogicExpression) {
val (leftNode, rightNode) = graphBuilder.exitLeftBinaryOrArgument(binaryLogicExpression)
exitLeftArgumentOfBinaryBooleanOperator(leftNode, rightNode, isAnd = false)
}
fun exitBinaryOr(binaryLogicExpression: FirBinaryLogicExpression) {
val node = graphBuilder.exitBinaryOr(binaryLogicExpression)
exitBinaryBooleanOperator(binaryLogicExpression, node, isAnd = false)
}
private fun exitLeftArgumentOfBinaryBooleanOperator(leftNode: CFGNode<*>, rightNode: CFGNode<*>, isAnd: Boolean) {
val parentFlow = leftNode.alivePreviousNodes.first().flow
leftNode.flow = logicSystem.forkFlow(parentFlow)
val leftOperandVariable = getOrCreateVariable(leftNode.previousNodes.first().fir)
rightNode.flow = logicSystem.approveFactsInsideFlow(
leftOperandVariable,
if (isAnd) EqTrue else EqFalse,
parentFlow,
shouldForkFlow = true,
shouldRemoveSynthetics = false
)
}
private fun exitBinaryBooleanOperator(
binaryLogicExpression: FirBinaryLogicExpression,
node: AbstractBinaryExitNode<*>,
isAnd: Boolean
) {
val bothEvaluated = if (isAnd) EqTrue else EqFalse
val onlyLeftEvaluated = bothEvaluated.invert()
// Naming for all variables was chosen in assumption that we processing && expression
val flowFromLeft = node.leftOperandNode.flow
val flowFromRight = node.rightOperandNode.flow
val flow = node.mergeIncomingFlow().flow
val (leftVariable, rightVariable) = binaryLogicExpression.getVariables()
val andVariable = getOrCreateVariable(binaryLogicExpression)
val (conditionalFromLeft, conditionalFromRight, approvedFromRight) = logicSystem.collectInfoForBooleanOperator(
flowFromLeft,
leftVariable,
flowFromRight,
rightVariable
)
// left && right == True
// left || right == False
val approvedIfTrue: MutableApprovedInfos = mutableMapOf()
logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromLeft)
logicSystem.approveFactTo(approvedIfTrue, bothEvaluated, conditionalFromRight)
approvedFromRight.forEach { (variable, info) ->
approvedIfTrue.addInfo(variable, info)
}
approvedIfTrue.forEach { (variable, info) ->
logicSystem.addConditionalInfo(flow, andVariable, info.toConditional(bothEvaluated, variable))
}
// left && right == False
// left || right == True
val approvedIfFalse: MutableApprovedInfos = mutableMapOf()
val leftIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromLeft)
val rightIsFalse = logicSystem.approveFact(onlyLeftEvaluated, conditionalFromRight)
approvedIfFalse.mergeInfo(logicSystem.orForVerifiedFacts(leftIsFalse, rightIsFalse))
approvedIfFalse.forEach { (variable, info) ->
logicSystem.addConditionalInfo(flow, andVariable, info.toConditional(onlyLeftEvaluated, variable))
}
node.flow = flow
variableStorage.removeVariableIfSynthetic(leftVariable)
variableStorage.removeVariableIfSynthetic(rightVariable)
}
private fun exitBooleanNot(functionCall: FirFunctionCall, node: FunctionCallNode) {
val booleanExpressionVariable = getOrCreateVariable(node.previousNodes.first().fir)
val variable = getOrCreateVariable(functionCall)
logicSystem.changeVariableForConditionFlow(node.flow, booleanExpressionVariable, variable) { it.invert() }
}
// ----------------------------------- Annotations -----------------------------------
fun enterAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.enterAnnotationCall(annotationCall).mergeIncomingFlow()
}
fun exitAnnotationCall(annotationCall: FirAnnotationCall) {
graphBuilder.exitAnnotationCall(annotationCall).mergeIncomingFlow()
}
// ----------------------------------- Init block -----------------------------------
fun enterInitBlock(initBlock: FirAnonymousInitializer) {
graphBuilder.enterInitBlock(initBlock).mergeIncomingFlow()
}
fun exitInitBlock(initBlock: FirAnonymousInitializer) {
graphBuilder.exitInitBlock(initBlock).mergeIncomingFlow()
}
// -------------------------------------------------------------------------------------------------------------------------
private fun FirBinaryLogicExpression.getVariables(): Pair =
getOrCreateVariable(leftOperand) to getOrCreateVariable(rightOperand)
private var CFGNode<*>.flow: Flow
get() = flowOnNodes.getValue(this.origin)
set(value) {
flowOnNodes[this.origin] = value
}
private val CFGNode<*>.origin: CFGNode<*> get() = if (this is StubNode) previousNodes.first() else this
private fun > T.mergeIncomingFlow(): T = this.also { node ->
val previousFlows = node.alivePreviousNodes.map { it.flow }
node.flow = logicSystem.joinFlow(previousFlows)
}
// -------------------------------- get or create variable --------------------------------
private fun getOrCreateSyntheticVariable(fir: FirElement): SyntheticDataFlowVariable =
variableStorage.getOrCreateNewSyntheticVariable(fir)
private fun getOrCreateRealVariable(fir: FirElement): RealDataFlowVariable? {
if (fir is FirThisReceiverExpressionImpl) {
return variableStorage.getOrCreateNewThisRealVariable(fir.calleeReference.boundSymbol ?: return null)
}
val symbol = fir.resolvedSymbol ?: return null
return variableStorage.getOrCreateNewRealVariable(symbol)
}
private fun getOrCreateRealVariable(symbol: AbstractFirBasedSymbol<*>): RealDataFlowVariable =
variableStorage.getOrCreateNewRealVariable(symbol).variableUnderAlias
private fun getOrCreateVariable(fir: FirElement): DataFlowVariable {
val symbol = fir.resolvedSymbol
return if (symbol == null)
getOrCreateSyntheticVariable(fir)
else
getOrCreateRealVariable(symbol)
}
// -------------------------------- get variable --------------------------------
private val FirElement.realVariable: RealDataFlowVariable?
get() {
val symbol = if (this is FirThisReceiverExpressionImpl) {
calleeReference.boundSymbol
} else {
resolvedSymbol
} ?: return null
return variableStorage[symbol]
}
private fun getRealVariablesForSafeCallChain(call: FirExpression): Collection {
val result = mutableListOf()
fun collect(call: FirExpression) {
when (call) {
is FirQualifiedAccess -> {
if (call.safe) {
val explicitReceiver = call.explicitReceiver
require(explicitReceiver != null)
collect(explicitReceiver)
}
((call.calleeReference as? FirResolvedCallableReference)?.resolvedSymbol)?.let { symbol ->
if (symbol is FirVariableSymbol<*> || symbol is FirPropertySymbol) {
result += getOrCreateRealVariable(symbol)
}
}
}
is FirWhenSubjectExpression -> {
// TODO: check
call.whenSubject.whenExpression.subjectVariable?.let { result += getOrCreateRealVariable(it.symbol) }
call.whenSubject.whenExpression.subject?.let { collect(it) }
}
}
}
collect(call)
return result
}
private inner class LogicSystemImpl(context: DataFlowInferenceContext) : DelegatingLogicSystem(context) {
override fun processUpdatedReceiverVariable(flow: Flow, variable: RealDataFlowVariable) {
val symbol = (variable.fir as? FirSymbolOwner<*>)?.symbol ?: return
val index = receiverStack.getReceiverIndex(symbol) ?: return
val info = flow.getApprovedInfo(variable)
if (info == null) {
receiverStack.replaceReceiverType(index, receiverStack.getOriginalType(index))
} else {
val types = info.exactType.toMutableList().also {
it += receiverStack.getOriginalType(index)
}
receiverStack.replaceReceiverType(index, context.intersectTypesOrNull(types)!!)
}
}
override fun updateAllReceivers(flow: Flow) {
receiverStack.mapNotNull { variableStorage[it.boundSymbol] }.forEach { processUpdatedReceiverVariable(flow, it) }
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy