Skip to main content

glua_code_analysis/db_index/flow/
flow_tree.rs

1use std::collections::HashMap;
2
3use glua_parser::{LuaAstPtr, LuaExpr, LuaSyntaxId};
4
5use crate::{FlowId, FlowNode, LuaDeclId};
6
7/// Metadata for BranchLabel nodes that enables the merge-skip optimisation.
8///
9/// When the backward flow walk hits a BranchLabel, it normally merges the types
10/// from every antecedent branch.  For variables NOT modified in any branch (and
11/// all branches are alive), the merge is guaranteed to produce the same type as
12/// the node before the branch (`common_predecessor`).  The walk can skip
13/// directly to that predecessor, turning an O(branches × depth) merge into O(1).
14#[derive(Debug, Clone)]
15pub struct BranchLabelInfo {
16    /// FlowId of the node immediately before the if/elseif/else split.
17    pub common_predecessor: FlowId,
18    /// `true` when any `Assignment(_, NameOnly|Mixed)` node was created inside
19    /// the branches — meaning a local/global name may have been reassigned.
20    pub has_name_assigns: bool,
21    /// `true` when any `Assignment(_, IndexOnly|Mixed)` node was created inside
22    /// the branches — meaning a field/index may have been reassigned.
23    pub has_index_assigns: bool,
24    /// `true` when any `ImplFunc` or `TagCast` node was created inside
25    /// the branches — these can modify the type of a named or indexed variable.
26    pub has_casts_or_implfunc: bool,
27    /// `true` when any `TrueCondition` or `FalseCondition` node was created
28    /// inside the branch *blocks* (not the outer if's condition).  Assert-like
29    /// patterns create inner conditions that can narrow variables beyond what
30    /// the outer condition/merge would cancel out.
31    pub has_inner_conditions: bool,
32}
33
34#[derive(Debug)]
35pub struct FlowTree {
36    decl_bind_expr_ref: HashMap<LuaDeclId, LuaAstPtr<LuaExpr>>,
37    flow_nodes: Vec<FlowNode>,
38    multiple_antecedents: Vec<Vec<FlowId>>,
39    // labels: HashMap<LuaClosureId, HashMap<SmolStr, FlowId>>,
40    bindings: HashMap<LuaSyntaxId, FlowId>,
41    /// Per-BranchLabel metadata used to skip redundant merges.
42    branch_label_info: HashMap<FlowId, BranchLabelInfo>,
43}
44
45impl FlowTree {
46    pub fn new(
47        decl_bind_expr_ref: HashMap<LuaDeclId, LuaAstPtr<LuaExpr>>,
48        flow_nodes: Vec<FlowNode>,
49        multiple_antecedents: Vec<Vec<FlowId>>,
50        // labels: HashMap<LuaClosureId, HashMap<SmolStr, FlowId>>,
51        bindings: HashMap<LuaSyntaxId, FlowId>,
52        branch_label_info: HashMap<FlowId, BranchLabelInfo>,
53    ) -> Self {
54        Self {
55            decl_bind_expr_ref,
56            flow_nodes,
57            multiple_antecedents,
58            bindings,
59            branch_label_info,
60        }
61    }
62
63    pub fn get_flow_id(&self, syntax_id: LuaSyntaxId) -> Option<FlowId> {
64        self.bindings.get(&syntax_id).cloned()
65    }
66
67    pub fn get_flow_node(&self, flow_id: FlowId) -> Option<&FlowNode> {
68        self.flow_nodes.get(flow_id.0 as usize)
69    }
70
71    pub fn get_multi_antecedents(&self, id: u32) -> Option<&[FlowId]> {
72        self.multiple_antecedents
73            .get(id as usize)
74            .map(|v| v.as_slice())
75    }
76
77    pub fn get_decl_ref_expr(&self, decl_id: &LuaDeclId) -> Option<LuaAstPtr<LuaExpr>> {
78        self.decl_bind_expr_ref.get(decl_id).cloned()
79    }
80
81    pub fn get_branch_label_info(&self, flow_id: FlowId) -> Option<&BranchLabelInfo> {
82        self.branch_label_info.get(&flow_id)
83    }
84}