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}