1use strum_macros::{Display, EnumIter, EnumString, FromRepr};
2use tree_sitter::Node;
3
4use crate::context::CompileUnit;
5use crate::declare_arena;
6use crate::symbol::{Scope, Symbol};
7
8declare_arena!([
10 hir_root: HirRoot<'tcx>,
11 hir_text: HirText<'tcx>,
12 hir_internal: HirInternal<'tcx>,
13 hir_scope: HirScope<'tcx>,
14 hir_file: HirFile<'tcx>,
15 hir_ident: HirIdent<'tcx>,
16 symbol: Symbol,
17 scope: Scope<'tcx>,
18]);
19
20#[derive(
21 Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter, EnumString, FromRepr, Display, Default,
22)]
23#[strum(serialize_all = "snake_case")]
24pub enum HirKind {
25 #[default]
26 Undefined,
27 Error,
28 File,
29 Scope,
30 Text,
31 Internal,
32 Comment,
33 Identifier,
34}
35
36#[derive(Debug, Clone, Copy, Default)]
37pub enum HirNode<'hir> {
38 #[default]
39 Undefined,
40 Root(&'hir HirRoot<'hir>),
41 Text(&'hir HirText<'hir>),
42 Internal(&'hir HirInternal<'hir>),
43 Scope(&'hir HirScope<'hir>),
44 File(&'hir HirFile<'hir>),
45 Ident(&'hir HirIdent<'hir>),
46}
47
48impl<'hir> HirNode<'hir> {
49 pub fn format_node(&self, unit: CompileUnit<'hir>) -> String {
50 let id = self.hir_id();
51 let kind = self.kind();
52 let mut f = format!("{}:{}", kind, id);
53
54 if let Some(scope) = unit.opt_get_scope(id) {
61 f.push_str(&format!(" s:{}", scope.format_compact()));
62 }
63
64 f
65 }
66
67 pub fn base(&self) -> Option<&HirBase<'hir>> {
69 match self {
70 HirNode::Undefined => None,
71 HirNode::Root(node) => Some(&node.base),
72 HirNode::Text(node) => Some(&node.base),
73 HirNode::Internal(node) => Some(&node.base),
74 HirNode::Scope(node) => Some(&node.base),
75 HirNode::File(node) => Some(&node.base),
76 HirNode::Ident(node) => Some(&node.base),
77 }
78 }
79
80 pub fn kind(&self) -> HirKind {
82 self.base().map_or(HirKind::Undefined, |base| base.kind)
83 }
84
85 pub fn is_kind(&self, kind: HirKind) -> bool {
87 self.kind() == kind
88 }
89
90 pub fn field_id(&self) -> u16 {
91 self.base().unwrap().field_id
92 }
93
94 pub fn children(&self) -> &[HirId] {
96 self.base().map_or(&[], |base| &base.children)
97 }
98
99 pub fn kind_id(&self) -> u16 {
100 self.base().unwrap().node.kind_id()
101 }
102
103 pub fn hir_id(&self) -> HirId {
104 self.base().unwrap().hir_id
105 }
106
107 pub fn start_byte(&self) -> usize {
108 self.base().unwrap().node.start_byte()
109 }
110
111 pub fn end_byte(&self) -> usize {
112 self.base().unwrap().node.end_byte()
113 }
114
115 pub fn child_count(&self) -> usize {
116 self.children().len()
117 }
118
119 pub fn inner_ts_node(&self) -> Node<'hir> {
120 self.base().unwrap().node
121 }
122
123 pub fn parent(&self) -> Option<HirId> {
124 self.base().and_then(|base| base.parent)
125 }
126
127 pub fn opt_child_by_field(
128 &self,
129 unit: CompileUnit<'hir>,
130 field_id: u16,
131 ) -> Option<HirNode<'hir>> {
132 self.base().unwrap().opt_child_by_field(unit, field_id)
133 }
134
135 pub fn child_by_field(&self, unit: CompileUnit<'hir>, field_id: u16) -> HirNode<'hir> {
136 self.opt_child_by_field(unit, field_id)
137 .unwrap_or_else(|| panic!("no child with field_id {}", field_id))
138 }
139
140 pub fn expect_ident_child_by_field(
141 &self,
142 unit: CompileUnit<'hir>,
143 field_id: u16,
144 ) -> &'hir HirIdent<'hir> {
145 self.opt_child_by_field(unit, field_id)
146 .map(|child| child.expect_ident())
147 .unwrap_or_else(|| panic!("no child with field_id {}", field_id))
148 }
149
150 pub fn opt_child_by_kind(
151 &self,
152 unit: CompileUnit<'hir>,
153 kind_id: u16,
154 ) -> Option<HirNode<'hir>> {
155 self.children()
156 .iter()
157 .map(|id| unit.hir_node(*id))
158 .find(|child| child.kind_id() == kind_id)
159 }
160
161 pub fn child_by_kind(&self, unit: CompileUnit<'hir>, kind_id: u16) -> HirNode<'hir> {
162 self.opt_child_by_kind(unit, kind_id)
163 .unwrap_or_else(|| panic!("no child with kind_id {}", kind_id))
164 }
165
166 pub fn find_ident(&self, unit: CompileUnit<'hir>) -> Option<&'hir HirIdent<'hir>> {
172 if let Some(ident) = self.as_ident() {
174 return Some(ident);
175 }
176
177 let children = match self {
179 HirNode::Root(r) => &r.base.children,
180 HirNode::Text(_) => return None,
181 HirNode::Internal(i) => &i.base.children,
182 HirNode::Scope(s) => &s.base.children,
183 HirNode::File(f) => &f.base.children,
184 HirNode::Ident(_) => return None,
185 HirNode::Undefined => return None,
186 };
187
188 for child_id in children {
190 let child = unit.hir_node(*child_id);
191 if let Some(ident) = child.find_ident(unit) {
192 return Some(ident);
193 }
194 }
195
196 None
197 }
198}
199
200macro_rules! impl_getters {
201 ($($variant:ident => $type:ty),* $(,)?) => {
202 impl<'hir> HirNode<'hir> {
203 $(
204 paste::paste! {
205 pub fn [<as_ $variant:lower>](&self) -> Option<$type> {
206 match self {
207 HirNode::$variant(r) => Some(r),
208 _ => None,
209 }
210 }
211
212 pub fn [<expect_ $variant:lower>](&self) -> $type {
213 match self {
214 HirNode::$variant(r) => r,
215 _ => panic!("Expected {} variant", stringify!($variant)),
216 }
217 }
218
219 pub fn [<is_ $variant:lower>](&self) -> bool {
220 matches!(self, HirNode::$variant(_))
221 }
222 }
223 )*
224 }
225 };
226}
227
228impl_getters! {
229 Root => &'hir HirRoot<'hir>,
230 Text => &'hir HirText<'hir>,
231 Internal => &'hir HirInternal<'hir>,
232 Scope => &'hir HirScope<'hir>,
233 File => &'hir HirFile<'hir>,
234 Ident => &'hir HirIdent<'hir>,
235}
236
237#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Default)]
238pub struct HirId(pub u32);
239
240impl std::fmt::Display for HirId {
241 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
242 write!(f, "{}", self.0)
243 }
244}
245
246#[derive(Debug, Clone)]
247pub struct HirBase<'hir> {
248 pub hir_id: HirId,
249 pub parent: Option<HirId>,
250 pub node: Node<'hir>,
251 pub kind: HirKind,
252 pub field_id: u16,
253 pub children: Vec<HirId>,
254}
255
256impl<'hir> HirBase<'hir> {
257 pub fn opt_child_by_fields(
258 &self,
259 unit: CompileUnit<'hir>,
260 fields_id: &[u16],
261 ) -> Option<HirNode<'hir>> {
262 self.children
263 .iter()
264 .map(|id| unit.hir_node(*id))
265 .find(|child| fields_id.contains(&child.field_id()))
266 }
267
268 pub fn opt_child_by_field(
269 &self,
270 unit: CompileUnit<'hir>,
271 field_id: u16,
272 ) -> Option<HirNode<'hir>> {
273 self.children
274 .iter()
275 .map(|id| unit.hir_node(*id))
276 .find(|child| child.field_id() == field_id)
277 }
278}
279
280#[derive(Debug, Clone)]
281pub struct HirRoot<'hir> {
282 pub base: HirBase<'hir>,
283 pub file_name: Option<String>,
284}
285
286impl<'hir> HirRoot<'hir> {
287 pub fn new(base: HirBase<'hir>, file_name: Option<String>) -> Self {
288 Self { base, file_name }
289 }
290}
291
292#[derive(Debug, Clone)]
293pub struct HirText<'hir> {
294 pub base: HirBase<'hir>,
295 pub text: String,
296}
297
298impl<'hir> HirText<'hir> {
299 pub fn new(base: HirBase<'hir>, text: String) -> Self {
300 Self { base, text }
301 }
302}
303
304#[derive(Debug, Clone)]
305pub struct HirInternal<'hir> {
306 pub base: HirBase<'hir>,
307}
308
309impl<'hir> HirInternal<'hir> {
310 pub fn new(base: HirBase<'hir>) -> Self {
311 Self { base }
312 }
313}
314
315#[derive(Debug, Clone)]
316pub struct HirScope<'hir> {
317 pub base: HirBase<'hir>,
318 pub ident: Option<&'hir HirIdent<'hir>>,
319}
320
321impl<'hir> HirScope<'hir> {
322 pub fn new(base: HirBase<'hir>, ident: Option<&'hir HirIdent<'hir>>) -> Self {
323 Self { base, ident }
324 }
325
326 pub fn owner_name(&self) -> String {
327 if let Some(id) = self.ident {
328 id.name.clone()
329 } else {
330 "unamed_scope".to_string()
331 }
332 }
333}
334
335#[derive(Debug, Clone)]
336pub struct HirIdent<'hir> {
337 pub base: HirBase<'hir>,
338 pub name: String,
339}
340
341impl<'hir> HirIdent<'hir> {
342 pub fn new(base: HirBase<'hir>, name: String) -> Self {
343 Self { base, name }
344 }
345}
346
347#[derive(Debug, Clone)]
348pub struct HirFile<'hir> {
349 pub base: HirBase<'hir>,
350 pub file_path: String,
351}
352
353impl<'hir> HirFile<'hir> {
354 pub fn new(base: HirBase<'hir>, file_path: String) -> Self {
355 Self { base, file_path }
356 }
357}