emmylua_code_analysis/db_index/flow/
flow_node.rs1use emmylua_parser::{
2 LuaAssignStat, LuaAstNode, LuaAstPtr, LuaChunk, LuaClosureExpr, LuaDocTagCast, LuaExpr,
3 LuaForStat, LuaFuncStat, LuaSyntaxKind, LuaSyntaxNode,
4};
5use internment::ArcIntern;
6use rowan::{TextRange, TextSize};
7use smol_str::SmolStr;
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Default)]
11pub struct FlowId(pub u32);
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum FlowAntecedent {
16 Single(FlowId),
18 Multiple(u32),
20}
21
22#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct FlowNode {
25 pub id: FlowId,
26 pub kind: FlowNodeKind,
27 pub antecedent: Option<FlowAntecedent>,
28}
29
30#[derive(Debug, Clone, PartialEq, Eq)]
32pub enum FlowNodeKind {
33 Start,
35 Unreachable,
37 BranchLabel,
39 LoopLabel,
41 NamedLabel(ArcIntern<SmolStr>),
43 DeclPosition(TextSize),
45 Assignment(LuaAstPtr<LuaAssignStat>),
47 TrueCondition(LuaAstPtr<LuaExpr>),
49 FalseCondition(LuaAstPtr<LuaExpr>),
51 ImplFunc(LuaAstPtr<LuaFuncStat>),
53 ForIStat(LuaAstPtr<LuaForStat>),
55 TagCast(LuaAstPtr<LuaDocTagCast>),
57 Break,
59 Return,
61}
62
63#[allow(unused)]
64impl FlowNodeKind {
65 pub fn is_branch_label(&self) -> bool {
66 matches!(self, FlowNodeKind::BranchLabel)
67 }
68
69 pub fn is_loop_label(&self) -> bool {
70 matches!(self, FlowNodeKind::LoopLabel)
71 }
72
73 pub fn is_named_label(&self) -> bool {
74 matches!(self, FlowNodeKind::NamedLabel(_))
75 }
76
77 pub fn is_change_flow(&self) -> bool {
78 matches!(self, FlowNodeKind::Break | FlowNodeKind::Return)
79 }
80
81 pub fn is_assignment(&self) -> bool {
82 matches!(self, FlowNodeKind::Assignment(_))
83 }
84
85 pub fn is_conditional(&self) -> bool {
86 matches!(
87 self,
88 FlowNodeKind::TrueCondition(_) | FlowNodeKind::FalseCondition(_)
89 )
90 }
91
92 pub fn is_unreachable(&self) -> bool {
93 matches!(self, FlowNodeKind::Unreachable)
94 }
95}
96
97#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
98pub struct LuaClosureId(TextRange);
99
100impl LuaClosureId {
101 pub fn from_closure(closure_expr: LuaClosureExpr) -> Self {
102 Self(closure_expr.get_range())
103 }
104
105 pub fn from_chunk(chunk: LuaChunk) -> Self {
106 Self(chunk.get_range())
107 }
108
109 pub fn from_node(node: &LuaSyntaxNode) -> Self {
110 let flow_id = node.ancestors().find_map(|node| match node.kind().into() {
111 LuaSyntaxKind::ClosureExpr => {
112 LuaClosureExpr::cast(node).map(LuaClosureId::from_closure)
113 }
114 LuaSyntaxKind::Chunk => LuaChunk::cast(node).map(LuaClosureId::from_chunk),
115 _ => None,
116 });
117
118 flow_id.unwrap_or_else(|| LuaClosureId(TextRange::default()))
119 }
120
121 pub fn get_position(&self) -> TextSize {
122 self.0.start()
123 }
124
125 pub fn get_range(&self) -> TextRange {
126 self.0
127 }
128}