FIR CFA: require path awareness in fixed point iteration
It really doesn't make sense to collect control flow data without checking paths, since that'll produce incorrect results for try-catch.
This commit is contained in:
+1
-7
@@ -222,9 +222,6 @@ object FirCallsEffectAnalyzer : FirControlFlowChecker() {
|
|||||||
|
|
||||||
override val constructor: (PersistentMap<FirBasedSymbol<*>, EventOccurrencesRange>) -> LambdaInvocationInfo =
|
override val constructor: (PersistentMap<FirBasedSymbol<*>, EventOccurrencesRange>) -> LambdaInvocationInfo =
|
||||||
::LambdaInvocationInfo
|
::LambdaInvocationInfo
|
||||||
|
|
||||||
override val empty: () -> LambdaInvocationInfo =
|
|
||||||
::EMPTY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PathAwareLambdaInvocationInfo(
|
class PathAwareLambdaInvocationInfo(
|
||||||
@@ -236,9 +233,6 @@ object FirCallsEffectAnalyzer : FirControlFlowChecker() {
|
|||||||
|
|
||||||
override val constructor: (PersistentMap<EdgeLabel, LambdaInvocationInfo>) -> PathAwareLambdaInvocationInfo =
|
override val constructor: (PersistentMap<EdgeLabel, LambdaInvocationInfo>) -> PathAwareLambdaInvocationInfo =
|
||||||
::PathAwareLambdaInvocationInfo
|
::PathAwareLambdaInvocationInfo
|
||||||
|
|
||||||
override val empty: () -> PathAwareLambdaInvocationInfo =
|
|
||||||
::EMPTY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class InvocationDataCollector(
|
private class InvocationDataCollector(
|
||||||
@@ -249,7 +243,7 @@ object FirCallsEffectAnalyzer : FirControlFlowChecker() {
|
|||||||
|
|
||||||
override fun visitFunctionCallNode(
|
override fun visitFunctionCallNode(
|
||||||
node: FunctionCallNode,
|
node: FunctionCallNode,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwareLambdaInvocationInfo>>
|
data: PathAwareLambdaInvocationInfo
|
||||||
): PathAwareLambdaInvocationInfo {
|
): PathAwareLambdaInvocationInfo {
|
||||||
var dataForNode = visitUnionNode(node, data)
|
var dataForNode = visitUnionNode(node, data)
|
||||||
|
|
||||||
|
|||||||
+12
-9
@@ -33,10 +33,10 @@ fun ControlFlowGraph.traverse(
|
|||||||
|
|
||||||
// ---------------------- Path-sensitive data collection -----------------------
|
// ---------------------- Path-sensitive data collection -----------------------
|
||||||
|
|
||||||
fun <I> ControlFlowGraph.collectDataForNode(
|
fun <I : PathAwareControlFlowInfo<I, *>> ControlFlowGraph.collectDataForNode(
|
||||||
direction: TraverseDirection,
|
direction: TraverseDirection,
|
||||||
initialInfo: I,
|
initialInfo: I,
|
||||||
visitor: ControlFlowGraphVisitor<I, Collection<Pair<EdgeLabel, I>>>,
|
visitor: PathAwareControlFlowGraphVisitor<I>,
|
||||||
visitSubGraphs: Boolean = true
|
visitSubGraphs: Boolean = true
|
||||||
): Map<CFGNode<*>, I> {
|
): Map<CFGNode<*>, I> {
|
||||||
val nodeMap = LinkedHashMap<CFGNode<*>, I>()
|
val nodeMap = LinkedHashMap<CFGNode<*>, I>()
|
||||||
@@ -51,10 +51,10 @@ fun <I> ControlFlowGraph.collectDataForNode(
|
|||||||
return nodeMap
|
return nodeMap
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun <I> ControlFlowGraph.collectDataForNodeInternal(
|
private fun <I : PathAwareControlFlowInfo<I, *>> ControlFlowGraph.collectDataForNodeInternal(
|
||||||
direction: TraverseDirection,
|
direction: TraverseDirection,
|
||||||
initialInfo: I,
|
initialInfo: I,
|
||||||
visitor: ControlFlowGraphVisitor<I, Collection<Pair<EdgeLabel, I>>>,
|
visitor: PathAwareControlFlowGraphVisitor<I>,
|
||||||
nodeMap: MutableMap<CFGNode<*>, I>,
|
nodeMap: MutableMap<CFGNode<*>, I>,
|
||||||
visitSubGraphs: Boolean = true
|
visitSubGraphs: Boolean = true
|
||||||
): Boolean {
|
): Boolean {
|
||||||
@@ -68,18 +68,21 @@ private fun <I> ControlFlowGraph.collectDataForNodeInternal(
|
|||||||
TraverseDirection.Forward -> node.previousCfgNodes
|
TraverseDirection.Forward -> node.previousCfgNodes
|
||||||
TraverseDirection.Backward -> node.followingCfgNodes
|
TraverseDirection.Backward -> node.followingCfgNodes
|
||||||
}
|
}
|
||||||
// One noticeable different against the path-unaware version is, here, we pair the control-flow info with the label.
|
|
||||||
val previousData =
|
val previousData =
|
||||||
previousNodes.mapNotNull {
|
previousNodes.mapNotNull {
|
||||||
val k = when (direction) {
|
val k = when (direction) {
|
||||||
TraverseDirection.Forward -> node.edgeFrom(it).label
|
TraverseDirection.Forward -> node.edgeFrom(it)
|
||||||
TraverseDirection.Backward -> node.edgeTo(it).label
|
TraverseDirection.Backward -> node.edgeTo(it)
|
||||||
}
|
}
|
||||||
val v = nodeMap[it] ?: return@mapNotNull null
|
val v = nodeMap[it] ?: return@mapNotNull null
|
||||||
k to v
|
visitor.visitEdge(it, node, k, v)
|
||||||
}
|
}
|
||||||
|
val reduced = if (node is UnionNodeMarker)
|
||||||
|
previousData.reduceOrNull { a, b -> a.plus(b) }
|
||||||
|
else
|
||||||
|
previousData.reduceOrNull { a, b -> a.merge(b) }
|
||||||
val data = nodeMap[node]
|
val data = nodeMap[node]
|
||||||
val newData = node.accept(visitor, previousData)
|
val newData = node.accept(visitor, reduced ?: visitor.emptyInfo)
|
||||||
val hasChanged = newData != data
|
val hasChanged = newData != data
|
||||||
changed = changed or hasChanged
|
changed = changed or hasChanged
|
||||||
if (hasChanged) {
|
if (hasChanged) {
|
||||||
|
|||||||
-2
@@ -13,8 +13,6 @@ abstract class ControlFlowInfo<S : ControlFlowInfo<S, K, V>, K : Any, V : Any> p
|
|||||||
|
|
||||||
protected abstract val constructor: (PersistentMap<K, V>) -> S
|
protected abstract val constructor: (PersistentMap<K, V>) -> S
|
||||||
|
|
||||||
protected abstract val empty: () -> S
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
override fun equals(other: Any?): Boolean {
|
||||||
return map == (other as? ControlFlowInfo<*, *, *>)?.map
|
return map == (other as? ControlFlowInfo<*, *, *>)?.map
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-13
@@ -7,22 +7,16 @@ package org.jetbrains.kotlin.fir.analysis.cfa.util
|
|||||||
|
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.CFGNode
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraphVisitor
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.ControlFlowGraphVisitor
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.EdgeLabel
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.Edge
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.UnionNodeMarker
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.UnionNodeMarker
|
||||||
import org.jetbrains.kotlin.utils.addToStdlib.foldMap
|
|
||||||
|
|
||||||
abstract class PathAwareControlFlowGraphVisitor<P : PathAwareControlFlowInfo<P, *>>
|
abstract class PathAwareControlFlowGraphVisitor<P : PathAwareControlFlowInfo<P, *>> : ControlFlowGraphVisitor<P, P>() {
|
||||||
: ControlFlowGraphVisitor<P, Collection<Pair<EdgeLabel, P>>>() {
|
abstract val emptyInfo: P
|
||||||
|
|
||||||
protected abstract val emptyInfo: P
|
open fun visitEdge(from: CFGNode<*>, to: CFGNode<*>, metadata: Edge, data: P): P =
|
||||||
|
data.applyLabel(to, metadata.label) ?: emptyInfo
|
||||||
|
|
||||||
override fun visitNode(node: CFGNode<*>, data: Collection<Pair<EdgeLabel, P>>): P {
|
override fun visitNode(node: CFGNode<*>, data: P): P = data
|
||||||
if (data.isEmpty()) return emptyInfo
|
|
||||||
return data.foldMap({ (label, info) -> info.applyLabel(node, label) }) { a, b -> a.merge(b) }
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun <T> visitUnionNode(node: T, data: Collection<Pair<EdgeLabel, P>>): P where T : CFGNode<*>, T : UnionNodeMarker {
|
override fun <T> visitUnionNode(node: T, data: P): P where T : CFGNode<*>, T : UnionNodeMarker = data
|
||||||
if (data.isEmpty()) return emptyInfo
|
|
||||||
return data.foldMap({ (label, info) -> info.applyLabel(node, label) }) { a, b -> a.plus(b) }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -16,7 +16,7 @@ abstract class PathAwareControlFlowInfo<P : PathAwareControlFlowInfo<P, S>, S :
|
|||||||
internal val infoAtNormalPath: S
|
internal val infoAtNormalPath: S
|
||||||
get() = map.getValue(NormalPath)
|
get() = map.getValue(NormalPath)
|
||||||
|
|
||||||
fun applyLabel(node: CFGNode<*>, label: EdgeLabel): P {
|
fun applyLabel(node: CFGNode<*>, label: EdgeLabel): P? {
|
||||||
if (label.isNormal) {
|
if (label.isNormal) {
|
||||||
// Special case: when we exit the try expression, null label means a normal path.
|
// Special case: when we exit the try expression, null label means a normal path.
|
||||||
// Filter out any info bound to non-null label
|
// Filter out any info bound to non-null label
|
||||||
@@ -28,7 +28,7 @@ abstract class PathAwareControlFlowInfo<P : PathAwareControlFlowInfo<P, S>, S :
|
|||||||
constructor(persistentMapOf(NormalPath to infoAtNormalPath))
|
constructor(persistentMapOf(NormalPath to infoAtNormalPath))
|
||||||
} else {
|
} else {
|
||||||
/* This means no info for normal path. */
|
/* This means no info for normal path. */
|
||||||
empty()
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// In general, null label means no additional path info, hence return `this` as-is.
|
// In general, null label means no additional path info, hence return `this` as-is.
|
||||||
@@ -58,7 +58,7 @@ abstract class PathAwareControlFlowInfo<P : PathAwareControlFlowInfo<P, S>, S :
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
/* This means no info for the specific label. */
|
/* This means no info for the specific label. */
|
||||||
empty()
|
null
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// { |-> ... } // empty path info
|
// { |-> ... } // empty path info
|
||||||
|
|||||||
-6
@@ -49,9 +49,6 @@ class PropertyInitializationInfo(
|
|||||||
|
|
||||||
override val constructor: (PersistentMap<FirPropertySymbol, EventOccurrencesRange>) -> PropertyInitializationInfo =
|
override val constructor: (PersistentMap<FirPropertySymbol, EventOccurrencesRange>) -> PropertyInitializationInfo =
|
||||||
::PropertyInitializationInfo
|
::PropertyInitializationInfo
|
||||||
|
|
||||||
override val empty: () -> PropertyInitializationInfo =
|
|
||||||
::EMPTY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class PathAwarePropertyInitializationInfo(
|
class PathAwarePropertyInitializationInfo(
|
||||||
@@ -63,7 +60,4 @@ class PathAwarePropertyInitializationInfo(
|
|||||||
|
|
||||||
override val constructor: (PersistentMap<EdgeLabel, PropertyInitializationInfo>) -> PathAwarePropertyInitializationInfo =
|
override val constructor: (PersistentMap<EdgeLabel, PropertyInitializationInfo>) -> PathAwarePropertyInitializationInfo =
|
||||||
::PathAwarePropertyInitializationInfo
|
::PathAwarePropertyInitializationInfo
|
||||||
|
|
||||||
override val empty: () -> PathAwarePropertyInitializationInfo =
|
|
||||||
::EMPTY
|
|
||||||
}
|
}
|
||||||
|
|||||||
+24
-62
@@ -8,7 +8,6 @@ package org.jetbrains.kotlin.fir.analysis.cfa.util
|
|||||||
import kotlinx.collections.immutable.PersistentMap
|
import kotlinx.collections.immutable.PersistentMap
|
||||||
import kotlinx.collections.immutable.persistentMapOf
|
import kotlinx.collections.immutable.persistentMapOf
|
||||||
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
import org.jetbrains.kotlin.contracts.description.EventOccurrencesRange
|
||||||
import org.jetbrains.kotlin.fir.expressions.FirStatement
|
|
||||||
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
|
import org.jetbrains.kotlin.fir.references.toResolvedPropertySymbol
|
||||||
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
import org.jetbrains.kotlin.fir.resolve.dfa.cfg.*
|
||||||
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
import org.jetbrains.kotlin.fir.symbols.impl.FirPropertySymbol
|
||||||
@@ -32,7 +31,7 @@ class PropertyInitializationInfoCollector(
|
|||||||
|
|
||||||
override fun visitVariableAssignmentNode(
|
override fun visitVariableAssignmentNode(
|
||||||
node: VariableAssignmentNode,
|
node: VariableAssignmentNode,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
data: PathAwarePropertyInitializationInfo
|
||||||
): PathAwarePropertyInitializationInfo {
|
): PathAwarePropertyInitializationInfo {
|
||||||
val dataForNode = visitNode(node, data)
|
val dataForNode = visitNode(node, data)
|
||||||
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return dataForNode
|
val symbol = node.fir.calleeReference.toResolvedPropertySymbol() ?: return dataForNode
|
||||||
@@ -45,7 +44,7 @@ class PropertyInitializationInfoCollector(
|
|||||||
|
|
||||||
override fun visitVariableDeclarationNode(
|
override fun visitVariableDeclarationNode(
|
||||||
node: VariableDeclarationNode,
|
node: VariableDeclarationNode,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
data: PathAwarePropertyInitializationInfo
|
||||||
): PathAwarePropertyInitializationInfo {
|
): PathAwarePropertyInitializationInfo {
|
||||||
val dataForNode = visitNode(node, data)
|
val dataForNode = visitNode(node, data)
|
||||||
return processVariableWithAssignment(
|
return processVariableWithAssignment(
|
||||||
@@ -78,72 +77,35 @@ class PropertyInitializationInfoCollector(
|
|||||||
// Data flows of declared/assigned variables in loops
|
// Data flows of declared/assigned variables in loops
|
||||||
// --------------------------------------------------
|
// --------------------------------------------------
|
||||||
|
|
||||||
private fun enterCapturingStatement(statement: FirStatement): Set<FirPropertySymbol> =
|
override fun visitEdge(
|
||||||
declaredVariableCollector.enterCapturingStatement(statement)
|
from: CFGNode<*>,
|
||||||
|
to: CFGNode<*>,
|
||||||
private fun exitCapturingStatement(statement: FirStatement) {
|
metadata: Edge,
|
||||||
declaredVariableCollector.exitCapturingStatement(statement)
|
data: PathAwarePropertyInitializationInfo
|
||||||
}
|
|
||||||
|
|
||||||
// A merge point for a loop with `continue`
|
|
||||||
override fun visitLoopEnterNode(
|
|
||||||
node: LoopEnterNode,
|
|
||||||
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
|
||||||
): PathAwarePropertyInitializationInfo {
|
): PathAwarePropertyInitializationInfo {
|
||||||
val declaredVariableSymbolsInLoop = enterCapturingStatement(node.fir)
|
val result = super.visitEdge(from, to, metadata, data)
|
||||||
if (declaredVariableSymbolsInLoop.isEmpty())
|
if (metadata.label != LoopBackPath) return result
|
||||||
return visitNode(node, data)
|
val declaredVariableSymbolsInCapturedScope = when (to) {
|
||||||
|
is LoopEnterNode -> declaredVariableCollector.declaredVariablesPerElement[to.fir]
|
||||||
return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data)
|
is LoopBlockEnterNode -> declaredVariableCollector.declaredVariablesPerElement[to.fir]
|
||||||
}
|
is LoopConditionEnterNode -> declaredVariableCollector.declaredVariablesPerElement[to.loop]
|
||||||
|
else -> return result
|
||||||
// A merge point for while loop
|
|
||||||
override fun visitLoopConditionEnterNode(
|
|
||||||
node: LoopConditionEnterNode,
|
|
||||||
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
|
||||||
): PathAwarePropertyInitializationInfo {
|
|
||||||
val declaredVariableSymbolsInLoop = declaredVariableCollector.declaredVariablesPerElement[node.loop]
|
|
||||||
if (declaredVariableSymbolsInLoop.isEmpty())
|
|
||||||
return visitNode(node, data)
|
|
||||||
|
|
||||||
return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
// A merge point for do-while loop
|
|
||||||
override fun visitLoopBlockEnterNode(
|
|
||||||
node: LoopBlockEnterNode,
|
|
||||||
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
|
||||||
): PathAwarePropertyInitializationInfo {
|
|
||||||
val declaredVariableSymbolsInLoop = declaredVariableCollector.declaredVariablesPerElement[node.fir]
|
|
||||||
if (declaredVariableSymbolsInLoop.isEmpty())
|
|
||||||
return visitNode(node, data)
|
|
||||||
|
|
||||||
return filterDeclaredVariableSymbolsInCapturedScope(node, declaredVariableSymbolsInLoop, data)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun filterDeclaredVariableSymbolsInCapturedScope(
|
|
||||||
node: CFGNode<*>,
|
|
||||||
declaredVariableSymbolsInCapturedScope: Collection<FirPropertySymbol>,
|
|
||||||
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
|
||||||
): PathAwarePropertyInitializationInfo {
|
|
||||||
var filteredData = data
|
|
||||||
for (variableSymbol in declaredVariableSymbolsInCapturedScope) {
|
|
||||||
filteredData = filteredData.map { (label, pathAwareInfo) ->
|
|
||||||
label to if (label is LoopBackPath) {
|
|
||||||
removeRange(pathAwareInfo, variableSymbol, ::PathAwarePropertyInitializationInfo)
|
|
||||||
} else {
|
|
||||||
pathAwareInfo
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return visitNode(node, filteredData)
|
return declaredVariableSymbolsInCapturedScope.fold(data) { filteredData, variableSymbol ->
|
||||||
|
removeRange(filteredData, variableSymbol, ::PathAwarePropertyInitializationInfo)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visitLoopEnterNode(node: LoopEnterNode, data: PathAwarePropertyInitializationInfo): PathAwarePropertyInitializationInfo {
|
||||||
|
declaredVariableCollector.enterCapturingStatement(node.fir)
|
||||||
|
return visitNode(node, data)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visitLoopExitNode(
|
override fun visitLoopExitNode(
|
||||||
node: LoopExitNode,
|
node: LoopExitNode,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwarePropertyInitializationInfo>>
|
data: PathAwarePropertyInitializationInfo
|
||||||
): PathAwarePropertyInitializationInfo {
|
): PathAwarePropertyInitializationInfo {
|
||||||
exitCapturingStatement(node.fir)
|
declaredVariableCollector.exitCapturingStatement(node.fir)
|
||||||
return visitNode(node, data)
|
return visitNode(node, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+6
-12
@@ -136,9 +136,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
override val constructor: (PersistentMap<FirPropertySymbol, VariableStatus>) -> VariableStatusInfo =
|
override val constructor: (PersistentMap<FirPropertySymbol, VariableStatus>) -> VariableStatusInfo =
|
||||||
::VariableStatusInfo
|
::VariableStatusInfo
|
||||||
|
|
||||||
override val empty: () -> VariableStatusInfo =
|
|
||||||
::EMPTY
|
|
||||||
|
|
||||||
override fun merge(other: VariableStatusInfo): VariableStatusInfo {
|
override fun merge(other: VariableStatusInfo): VariableStatusInfo {
|
||||||
var result = this
|
var result = this
|
||||||
for (symbol in keys.union(other.keys)) {
|
for (symbol in keys.union(other.keys)) {
|
||||||
@@ -163,9 +160,6 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
|
|
||||||
override val constructor: (PersistentMap<EdgeLabel, VariableStatusInfo>) -> PathAwareVariableStatusInfo =
|
override val constructor: (PersistentMap<EdgeLabel, VariableStatusInfo>) -> PathAwareVariableStatusInfo =
|
||||||
::PathAwareVariableStatusInfo
|
::PathAwareVariableStatusInfo
|
||||||
|
|
||||||
override val empty: () -> PathAwareVariableStatusInfo =
|
|
||||||
::EMPTY
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class ValueWritesWithoutReading(
|
private class ValueWritesWithoutReading(
|
||||||
@@ -184,19 +178,19 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
|
|
||||||
override fun visitNode(
|
override fun visitNode(
|
||||||
node: CFGNode<*>,
|
node: CFGNode<*>,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
data: PathAwareVariableStatusInfo
|
||||||
): PathAwareVariableStatusInfo =
|
): PathAwareVariableStatusInfo =
|
||||||
super.visitNode(node, data).withAnnotationsFrom(node)
|
super.visitNode(node, data).withAnnotationsFrom(node)
|
||||||
|
|
||||||
override fun <T> visitUnionNode(
|
override fun <T> visitUnionNode(
|
||||||
node: T,
|
node: T,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
data: PathAwareVariableStatusInfo
|
||||||
): PathAwareVariableStatusInfo where T : CFGNode<*>, T : UnionNodeMarker =
|
): PathAwareVariableStatusInfo where T : CFGNode<*>, T : UnionNodeMarker =
|
||||||
super.visitUnionNode(node, data).withAnnotationsFrom(node)
|
super.visitUnionNode(node, data).withAnnotationsFrom(node)
|
||||||
|
|
||||||
override fun visitVariableDeclarationNode(
|
override fun visitVariableDeclarationNode(
|
||||||
node: VariableDeclarationNode,
|
node: VariableDeclarationNode,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
data: PathAwareVariableStatusInfo
|
||||||
): PathAwareVariableStatusInfo {
|
): PathAwareVariableStatusInfo {
|
||||||
val dataForNode = visitNode(node, data)
|
val dataForNode = visitNode(node, data)
|
||||||
if (node.fir.source?.kind is KtFakeSourceElementKind) return dataForNode
|
if (node.fir.source?.kind is KtFakeSourceElementKind) return dataForNode
|
||||||
@@ -228,7 +222,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
|
|
||||||
override fun visitVariableAssignmentNode(
|
override fun visitVariableAssignmentNode(
|
||||||
node: VariableAssignmentNode,
|
node: VariableAssignmentNode,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
data: PathAwareVariableStatusInfo
|
||||||
): PathAwareVariableStatusInfo {
|
): PathAwareVariableStatusInfo {
|
||||||
val dataForNode = visitNode(node, data)
|
val dataForNode = visitNode(node, data)
|
||||||
val symbol = node.fir.lValue.toResolvedPropertySymbol() ?: return dataForNode
|
val symbol = node.fir.lValue.toResolvedPropertySymbol() ?: return dataForNode
|
||||||
@@ -257,7 +251,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
|
|
||||||
override fun visitQualifiedAccessNode(
|
override fun visitQualifiedAccessNode(
|
||||||
node: QualifiedAccessNode,
|
node: QualifiedAccessNode,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
data: PathAwareVariableStatusInfo
|
||||||
): PathAwareVariableStatusInfo {
|
): PathAwareVariableStatusInfo {
|
||||||
val dataForNode = visitNode(node, data)
|
val dataForNode = visitNode(node, data)
|
||||||
return visitQualifiedAccesses(dataForNode, node.fir)
|
return visitQualifiedAccesses(dataForNode, node.fir)
|
||||||
@@ -293,7 +287,7 @@ object UnusedChecker : AbstractFirPropertyInitializationChecker() {
|
|||||||
|
|
||||||
override fun visitFunctionCallNode(
|
override fun visitFunctionCallNode(
|
||||||
node: FunctionCallNode,
|
node: FunctionCallNode,
|
||||||
data: Collection<Pair<EdgeLabel, PathAwareVariableStatusInfo>>
|
data: PathAwareVariableStatusInfo
|
||||||
): PathAwareVariableStatusInfo {
|
): PathAwareVariableStatusInfo {
|
||||||
val dataForNode = visitUnionNode(node, data)
|
val dataForNode = visitUnionNode(node, data)
|
||||||
val reference = node.fir.calleeReference.resolved ?: return dataForNode
|
val reference = node.fir.calleeReference.resolved ?: return dataForNode
|
||||||
|
|||||||
Reference in New Issue
Block a user