llmcc_core/
block.rs

1use parking_lot::RwLock;
2use std::collections::HashSet;
3use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
4use strum_macros::{Display, EnumIter, EnumString, FromRepr};
5
6use crate::context::CompileUnit;
7use crate::declare_arena;
8use crate::ir::HirNode;
9use crate::symbol::Symbol;
10
11declare_arena!(BlockArena {
12    bb: BasicBlock<'a>,
13    blk_root: BlockRoot<'a>,
14    blk_module: BlockModule<'a>,
15    blk_func: BlockFunc<'a>,
16    blk_class: BlockClass<'a>,
17    blk_trait: BlockTrait<'a>,
18    blk_interface: BlockInterface<'a>,
19    blk_impl: BlockImpl<'a>,
20    blk_call: BlockCall<'a>,
21    blk_enum: BlockEnum<'a>,
22    blk_field: BlockField<'a>,
23    blk_const: BlockConst<'a>,
24    blk_parameter: BlockParameter<'a>,
25    blk_return: BlockReturn<'a>,
26    blk_alias: BlockAlias<'a>,
27});
28
29#[derive(
30    Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter, EnumString, FromRepr, Display, Default,
31)]
32#[strum(serialize_all = "snake_case")]
33pub enum BlockKind {
34    #[default]
35    Undefined,
36    Root,
37    Module,
38    Func,
39    Method,
40    Closure,
41    Call,
42    Class,
43    Trait,
44    Interface,
45    Enum,
46    Const,
47    Impl,
48    Field,
49    Scope,
50    Parameter,
51    Return,
52    Alias,
53}
54
55#[derive(Debug, Clone)]
56pub enum BasicBlock<'blk> {
57    Undefined,
58    Root(&'blk BlockRoot<'blk>),
59    Module(&'blk BlockModule<'blk>),
60    Func(&'blk BlockFunc<'blk>),
61    Call(&'blk BlockCall<'blk>),
62    Enum(&'blk BlockEnum<'blk>),
63    Class(&'blk BlockClass<'blk>),
64    Trait(&'blk BlockTrait<'blk>),
65    Interface(&'blk BlockInterface<'blk>),
66    Impl(&'blk BlockImpl<'blk>),
67    Const(&'blk BlockConst<'blk>),
68    Field(&'blk BlockField<'blk>),
69    Parameter(&'blk BlockParameter<'blk>),
70    Return(&'blk BlockReturn<'blk>),
71    Alias(&'blk BlockAlias<'blk>),
72    Block,
73}
74
75impl<'blk> BasicBlock<'blk> {
76    /// Format the block label (content inside the parentheses)
77    /// Delegates to each block type's format method
78    pub fn format_block(&self, unit: CompileUnit<'blk>) -> String {
79        match self {
80            BasicBlock::Root(root) => root.format(),
81            BasicBlock::Module(module) => module.format(),
82            BasicBlock::Func(func) => func.format(unit),
83            BasicBlock::Class(class) => class.format(),
84            BasicBlock::Trait(trait_blk) => trait_blk.format(),
85            BasicBlock::Interface(iface) => iface.format(),
86            BasicBlock::Impl(impl_blk) => impl_blk.format(),
87            BasicBlock::Call(call) => call.format(),
88            BasicBlock::Enum(enum_blk) => enum_blk.format(),
89            BasicBlock::Const(const_blk) => const_blk.format(),
90            BasicBlock::Field(field) => field.format(),
91            BasicBlock::Parameter(param) => param.format(),
92            BasicBlock::Return(ret) => ret.format(),
93            BasicBlock::Alias(alias) => alias.format(),
94            BasicBlock::Undefined | BasicBlock::Block => "undefined".to_string(),
95        }
96    }
97
98    /// Format extra entries for dependencies (rendered as pseudo-children)
99    /// Only applicable to Func and Class blocks, returns empty for others
100    pub fn format_deps(&self, unit: CompileUnit<'blk>) -> Vec<String> {
101        match self {
102            BasicBlock::Func(func) => func.format_deps(unit),
103            BasicBlock::Class(class) => class.format_deps(unit),
104            _ => Vec::new(),
105        }
106    }
107
108    /// Format the suffix to appear after the closing parenthesis
109    /// (Currently unused - type info is now inline in format_block)
110    pub fn format_suffix(&self) -> Option<String> {
111        None
112    }
113
114    pub fn id(&self) -> BlockId {
115        self.block_id()
116    }
117
118    /// Get the base block information regardless of variant
119    pub fn base(&self) -> Option<&BlockBase<'blk>> {
120        match self {
121            BasicBlock::Undefined | BasicBlock::Block => None,
122            BasicBlock::Root(block) => Some(&block.base),
123            BasicBlock::Module(block) => Some(&block.base),
124            BasicBlock::Func(block) => Some(&block.base),
125            BasicBlock::Class(block) => Some(&block.base),
126            BasicBlock::Trait(block) => Some(&block.base),
127            BasicBlock::Interface(block) => Some(&block.base),
128            BasicBlock::Impl(block) => Some(&block.base),
129            BasicBlock::Call(block) => Some(&block.base),
130            BasicBlock::Enum(block) => Some(&block.base),
131            BasicBlock::Const(block) => Some(&block.base),
132            BasicBlock::Field(block) => Some(&block.base),
133            BasicBlock::Parameter(block) => Some(&block.base),
134            BasicBlock::Return(block) => Some(&block.base),
135            BasicBlock::Alias(block) => Some(&block.base),
136        }
137    }
138
139    /// Get the block ID
140    pub fn block_id(&self) -> BlockId {
141        self.base().unwrap().id
142    }
143
144    /// Get the block kind
145    pub fn kind(&self) -> BlockKind {
146        self.base().map(|base| base.kind).unwrap_or_default()
147    }
148
149    /// Get the HIR node
150    pub fn node(&self) -> &HirNode<'blk> {
151        self.base().map(|base| &base.node).unwrap()
152    }
153
154    pub fn opt_node(&self) -> Option<&HirNode<'blk>> {
155        self.base().map(|base| &base.node)
156    }
157
158    /// Get the symbol that defines this block (if any)
159    pub fn symbol(&self) -> Option<&'blk Symbol> {
160        self.base().and_then(|base| base.symbol())
161    }
162
163    /// Get the children block IDs
164    pub fn children(&self) -> Vec<BlockId> {
165        self.base()
166            .map(|base| base.get_children())
167            .unwrap_or_default()
168    }
169
170    pub fn child_count(&self) -> usize {
171        self.children().len()
172    }
173
174    /// Check if this is a specific kind of block
175    pub fn is_kind(&self, kind: BlockKind) -> bool {
176        self.kind() == kind
177    }
178
179    /// Get the inner BlockRoot if this is a Root block
180    pub fn as_root(&self) -> Option<&'blk BlockRoot<'blk>> {
181        match self {
182            BasicBlock::Root(r) => Some(r),
183            _ => None,
184        }
185    }
186
187    /// Get the inner BlockModule if this is a Module block
188    pub fn as_module(&self) -> Option<&'blk BlockModule<'blk>> {
189        match self {
190            BasicBlock::Module(m) => Some(m),
191            _ => None,
192        }
193    }
194
195    /// Get the inner BlockFunc if this is a Func or Method block
196    pub fn as_func(&self) -> Option<&'blk BlockFunc<'blk>> {
197        match self {
198            BasicBlock::Func(f) => Some(f),
199            _ => None,
200        }
201    }
202
203    /// Get the inner BlockClass if this is a Class block
204    pub fn as_class(&self) -> Option<&'blk BlockClass<'blk>> {
205        match self {
206            BasicBlock::Class(c) => Some(c),
207            _ => None,
208        }
209    }
210
211    /// Get the inner BlockTrait if this is a Trait block
212    pub fn as_trait(&self) -> Option<&'blk BlockTrait<'blk>> {
213        match self {
214            BasicBlock::Trait(t) => Some(t),
215            _ => None,
216        }
217    }
218
219    /// Get the inner BlockInterface if this is an Interface block
220    pub fn as_interface(&self) -> Option<&'blk BlockInterface<'blk>> {
221        match self {
222            BasicBlock::Interface(i) => Some(i),
223            _ => None,
224        }
225    }
226
227    /// Get the inner BlockImpl if this is an Impl block
228    pub fn as_impl(&self) -> Option<&'blk BlockImpl<'blk>> {
229        match self {
230            BasicBlock::Impl(i) => Some(i),
231            _ => None,
232        }
233    }
234
235    /// Get the inner BlockEnum if this is an Enum block
236    pub fn as_enum(&self) -> Option<&'blk BlockEnum<'blk>> {
237        match self {
238            BasicBlock::Enum(e) => Some(e),
239            _ => None,
240        }
241    }
242
243    /// Get the inner BlockField if this is a Field block
244    pub fn as_field(&self) -> Option<&'blk BlockField<'blk>> {
245        match self {
246            BasicBlock::Field(f) => Some(f),
247            _ => None,
248        }
249    }
250
251    /// Get the inner BlockCall if this is a Call block
252    pub fn as_call(&self) -> Option<&'blk BlockCall<'blk>> {
253        match self {
254            BasicBlock::Call(c) => Some(c),
255            _ => None,
256        }
257    }
258}
259
260#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, Default, PartialOrd, Ord)]
261pub struct BlockId(pub u32);
262
263/// Global counter for allocating unique Block IDs
264static BLOCK_ID_COUNTER: AtomicU32 = AtomicU32::new(1);
265
266/// Reset global BlockId counter (primarily for deterministic tests)
267pub fn reset_block_id_counter() {
268    BLOCK_ID_COUNTER.store(1, Ordering::Relaxed);
269}
270
271impl std::fmt::Display for BlockId {
272    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
273        write!(f, "{}", self.0)
274    }
275}
276
277impl BlockId {
278    pub fn new(id: u32) -> Self {
279        Self(id)
280    }
281
282    /// Allocate a new unique BlockId
283    pub fn allocate() -> Self {
284        let id = BLOCK_ID_COUNTER.fetch_add(1, Ordering::Relaxed);
285        BlockId(id)
286    }
287
288    /// Get the next BlockId that will be allocated (useful for diagnostics)
289    pub fn next() -> Self {
290        BlockId(BLOCK_ID_COUNTER.load(Ordering::Relaxed))
291    }
292
293    pub fn as_u32(self) -> u32 {
294        self.0
295    }
296
297    pub const ROOT_PARENT: BlockId = BlockId(u32::MAX);
298
299    pub fn is_root_parent(self) -> bool {
300        self.0 == u32::MAX
301    }
302}
303
304#[derive(
305    Debug, Clone, Copy, PartialEq, Eq, Hash, EnumIter, EnumString, FromRepr, Display, Default,
306)]
307#[strum(serialize_all = "snake_case")]
308pub enum BlockRelation {
309    #[default]
310    Unknown,
311
312    // ========== Structural Relations ==========
313    /// Parent contains child (Root→Func, Class→Method, etc.)
314    Contains,
315    /// Child is contained by parent
316    ContainedBy,
317
318    // ========== Function/Method Relations ==========
319    /// Func/Method → Parameters block
320    HasParameters,
321    /// Func/Method → Return block
322    HasReturn,
323    /// Func/Method → Func/Method it calls
324    Calls,
325    /// Func/Method is called by another Func/Method
326    CalledBy,
327
328    // ========== Type Relations ==========
329    /// Class/Enum → Field blocks
330    HasField,
331    /// Field → Class/Enum that owns it
332    FieldOf,
333    /// Field/Parameter/Return → Type definition (the type of this element)
334    TypeOf,
335    /// Type definition → Field/Parameter/Return that uses this type
336    TypeFor,
337    /// Impl → Type it implements for
338    ImplFor,
339    /// Type → Impl blocks for this type
340    HasImpl,
341    /// Impl/Trait → Method blocks
342    HasMethod,
343    /// Method → Impl/Trait/Class that owns it
344    MethodOf,
345    /// Type → Trait it implements
346    Implements,
347    /// Trait → Types that implement it
348    ImplementedBy,
349
350    // ========== Generic Reference ==========
351    /// Uses a type/const/function
352    Uses,
353    /// Is used by
354    UsedBy,
355
356    // ========== Inheritance ==========
357    /// Trait/Interface extends another (TypeScript extends, Rust supertraits)
358    Extends,
359    /// Trait/Interface is extended by another
360    ExtendedBy,
361}
362
363#[derive(Debug)]
364pub struct BlockBase<'blk> {
365    pub id: BlockId,
366    pub node: HirNode<'blk>,
367    pub kind: BlockKind,
368    pub parent: RwLock<Option<BlockId>>,
369    pub children: RwLock<Vec<BlockId>>,
370    /// Direct reference to the symbol that defines this block.
371    /// Set during block building. Enables: block.symbol().type_of().block_id()
372    pub symbol: Option<&'blk Symbol>,
373    /// Types this block depends on (used for arch graph edges)
374    /// For impl blocks: type arguments from trait reference (e.g., User in `impl Repository<User>`)
375    /// For structs/enums: could include generic bounds or trait objects
376    pub type_deps: RwLock<HashSet<BlockId>>,
377}
378
379impl<'blk> BlockBase<'blk> {
380    pub fn new(
381        id: BlockId,
382        node: HirNode<'blk>,
383        kind: BlockKind,
384        parent: Option<BlockId>,
385        children: Vec<BlockId>,
386    ) -> Self {
387        Self {
388            id,
389            node,
390            kind,
391            parent: RwLock::new(parent),
392            children: RwLock::new(children),
393            symbol: None,
394            type_deps: RwLock::new(HashSet::new()),
395        }
396    }
397
398    /// Create a new BlockBase with symbol reference
399    pub fn with_symbol(
400        id: BlockId,
401        node: HirNode<'blk>,
402        kind: BlockKind,
403        parent: Option<BlockId>,
404        children: Vec<BlockId>,
405        symbol: Option<&'blk Symbol>,
406    ) -> Self {
407        Self {
408            id,
409            node,
410            kind,
411            parent: RwLock::new(parent),
412            children: RwLock::new(children),
413            symbol,
414            type_deps: RwLock::new(HashSet::new()),
415        }
416    }
417
418    /// Get the symbol that defines this block (if any)
419    pub fn symbol(&self) -> Option<&'blk Symbol> {
420        self.symbol
421    }
422
423    pub fn opt_get_name(&self) -> Option<&str> {
424        self.node
425            .as_scope()
426            .and_then(|scope| *scope.ident.read())
427            .map(|ident| ident.name)
428    }
429
430    pub fn add_child(&self, child_id: BlockId) {
431        let mut children = self.children.write();
432        if !children.contains(&child_id) {
433            children.push(child_id);
434        }
435    }
436
437    pub fn remove_child(&self, child_id: BlockId) {
438        self.children.write().retain(|&id| id != child_id);
439    }
440
441    pub fn get_children(&self) -> Vec<BlockId> {
442        self.children.read().clone()
443    }
444
445    pub fn set_parent(&self, parent_id: BlockId) {
446        *self.parent.write() = Some(parent_id);
447    }
448
449    pub fn get_parent(&self) -> Option<BlockId> {
450        *self.parent.read()
451    }
452
453    /// Add a type dependency to this block
454    pub fn add_type_dep(&self, type_id: BlockId) {
455        self.type_deps.write().insert(type_id);
456    }
457
458    /// Get all type dependencies for this block
459    pub fn get_type_deps(&self) -> HashSet<BlockId> {
460        self.type_deps.read().clone()
461    }
462}
463
464#[derive(Debug)]
465pub struct BlockRoot<'blk> {
466    pub base: BlockBase<'blk>,
467    pub file_name: Option<String>,
468    /// Crate name from Cargo.toml [package] name
469    pub crate_name: RwLock<Option<String>>,
470    /// Crate/package root directory path
471    pub crate_root: RwLock<Option<String>>,
472    /// Module path relative to crate root (e.g., "utils::helpers")
473    pub module_path: RwLock<Option<String>>,
474    /// Module root directory path
475    pub module_root: RwLock<Option<String>>,
476}
477
478impl<'blk> BlockRoot<'blk> {
479    pub fn new(
480        id: BlockId,
481        node: HirNode<'blk>,
482        parent: Option<BlockId>,
483        children: Vec<BlockId>,
484        file_name: Option<String>,
485    ) -> Self {
486        Self::new_with_symbol(id, node, parent, children, file_name, None)
487    }
488
489    pub fn new_with_symbol(
490        id: BlockId,
491        node: HirNode<'blk>,
492        parent: Option<BlockId>,
493        children: Vec<BlockId>,
494        file_name: Option<String>,
495        symbol: Option<&'blk Symbol>,
496    ) -> Self {
497        let base = BlockBase::with_symbol(id, node, BlockKind::Root, parent, children, symbol);
498        Self {
499            base,
500            file_name,
501            crate_name: RwLock::new(None),
502            crate_root: RwLock::new(None),
503            module_path: RwLock::new(None),
504            module_root: RwLock::new(None),
505        }
506    }
507
508    pub fn set_crate_name(&self, name: String) {
509        *self.crate_name.write() = Some(name);
510    }
511
512    pub fn get_crate_name(&self) -> Option<String> {
513        self.crate_name.read().clone()
514    }
515
516    pub fn set_crate_root(&self, root: String) {
517        *self.crate_root.write() = Some(root);
518    }
519
520    pub fn get_crate_root(&self) -> Option<String> {
521        self.crate_root.read().clone()
522    }
523
524    pub fn set_module_path(&self, path: String) {
525        *self.module_path.write() = Some(path);
526    }
527
528    pub fn get_module_path(&self) -> Option<String> {
529        self.module_path.read().clone()
530    }
531
532    pub fn set_module_root(&self, root: String) {
533        *self.module_root.write() = Some(root);
534    }
535
536    pub fn get_module_root(&self) -> Option<String> {
537        self.module_root.read().clone()
538    }
539
540    pub fn format(&self) -> String {
541        let name = self.base.opt_get_name().unwrap_or("");
542        if let Some(file_name) = &self.file_name {
543            format!(
544                "{}:{} {} ({})",
545                self.base.kind, self.base.id, name, file_name
546            )
547        } else {
548            format!("{}:{} {}", self.base.kind, self.base.id, name)
549        }
550    }
551}
552
553/// Block representing a module declaration (`mod foo` or `mod foo { ... }`)
554#[derive(Debug)]
555pub struct BlockModule<'blk> {
556    pub base: BlockBase<'blk>,
557    /// Module name (e.g., "utils" for `mod utils;`)
558    pub name: String,
559    /// Whether this is an inline module (`mod foo { ... }`) vs file module (`mod foo;`)
560    pub is_inline: bool,
561}
562
563impl<'blk> BlockModule<'blk> {
564    pub fn new(
565        id: BlockId,
566        node: HirNode<'blk>,
567        parent: Option<BlockId>,
568        children: Vec<BlockId>,
569        name: String,
570        is_inline: bool,
571    ) -> Self {
572        Self::new_with_symbol(id, node, parent, children, name, is_inline, None)
573    }
574
575    pub fn new_with_symbol(
576        id: BlockId,
577        node: HirNode<'blk>,
578        parent: Option<BlockId>,
579        children: Vec<BlockId>,
580        name: String,
581        is_inline: bool,
582        symbol: Option<&'blk Symbol>,
583    ) -> Self {
584        let base = BlockBase::with_symbol(id, node, BlockKind::Module, parent, children, symbol);
585        Self {
586            base,
587            name,
588            is_inline,
589        }
590    }
591
592    pub fn format(&self) -> String {
593        let inline_marker = if self.is_inline { " (inline)" } else { "" };
594        format!(
595            "{}:{} {}{}",
596            self.base.kind, self.base.id, self.name, inline_marker
597        )
598    }
599}
600
601#[derive(Debug)]
602pub struct BlockFunc<'blk> {
603    pub base: BlockBase<'blk>,
604    pub name: String,
605    pub parameters: RwLock<Vec<BlockId>>,
606    pub returns: RwLock<Option<BlockId>>,
607    /// Types used inside function body (excludes parameter/return types)
608    /// Examples: local variable types, static method receivers (Foo::method)
609    pub type_deps: RwLock<HashSet<BlockId>>,
610    /// Functions/methods called by this function
611    pub func_deps: RwLock<HashSet<BlockId>>,
612    /// Whether this is a method (inside impl block) vs a free function
613    pub is_method: AtomicBool,
614}
615
616impl<'blk> BlockFunc<'blk> {
617    pub fn new(
618        id: BlockId,
619        node: HirNode<'blk>,
620        kind: BlockKind,
621        parent: Option<BlockId>,
622        children: Vec<BlockId>,
623    ) -> Self {
624        Self::new_with_symbol(id, node, kind, parent, children, None)
625    }
626
627    pub fn new_with_symbol(
628        id: BlockId,
629        node: HirNode<'blk>,
630        kind: BlockKind,
631        parent: Option<BlockId>,
632        children: Vec<BlockId>,
633        symbol: Option<&'blk Symbol>,
634    ) -> Self {
635        let base = BlockBase::with_symbol(id, node, kind, parent, children, symbol);
636        let name = base.opt_get_name().unwrap_or("").to_string();
637        Self {
638            base,
639            name,
640            parameters: RwLock::new(Vec::new()),
641            returns: RwLock::new(None),
642            type_deps: RwLock::new(HashSet::new()),
643            func_deps: RwLock::new(HashSet::new()),
644            is_method: AtomicBool::new(false),
645        }
646    }
647
648    pub fn set_is_method(&self, is_method: bool) {
649        self.is_method.store(is_method, Ordering::Relaxed);
650    }
651
652    pub fn is_method(&self) -> bool {
653        self.is_method.load(Ordering::Relaxed)
654    }
655
656    pub fn add_parameter(&self, param: BlockId) {
657        self.parameters.write().push(param);
658    }
659
660    pub fn get_parameters(&self) -> Vec<BlockId> {
661        self.parameters.read().clone()
662    }
663
664    pub fn set_returns(&self, ret: BlockId) {
665        *self.returns.write() = Some(ret);
666    }
667
668    pub fn get_returns(&self) -> Option<BlockId> {
669        *self.returns.read()
670    }
671
672    pub fn add_type_dep(&self, type_id: BlockId) {
673        self.type_deps.write().insert(type_id);
674    }
675
676    pub fn get_type_deps(&self) -> HashSet<BlockId> {
677        self.type_deps.read().clone()
678    }
679
680    pub fn add_func_dep(&self, func_id: BlockId) {
681        self.func_deps.write().insert(func_id);
682    }
683
684    pub fn get_func_deps(&self) -> HashSet<BlockId> {
685        self.func_deps.read().clone()
686    }
687
688    pub fn format(&self, _unit: CompileUnit<'blk>) -> String {
689        format!("{}:{} {}", self.base.kind, self.base.id, self.name)
690    }
691
692    /// Format dependency entries as pseudo-children (to be rendered after real children)
693    /// Returns lines like "@tdep:3 Bar" and "@fdep:5 process"
694    pub fn format_deps(&self, unit: CompileUnit<'blk>) -> Vec<String> {
695        let mut deps = Vec::new();
696
697        // Add type_deps
698        let type_deps = self.get_type_deps();
699        if !type_deps.is_empty() {
700            let mut sorted: Vec<_> = type_deps.iter().collect();
701            sorted.sort();
702            for dep_id in sorted {
703                let dep_block = unit.bb(*dep_id);
704                let dep_name = dep_block
705                    .base()
706                    .and_then(|b| b.opt_get_name())
707                    .unwrap_or("");
708                deps.push(format!("@tdep:{dep_id} {dep_name}"));
709            }
710        }
711
712        // Add func_deps
713        let func_deps = self.get_func_deps();
714        if !func_deps.is_empty() {
715            let mut sorted: Vec<_> = func_deps.iter().collect();
716            sorted.sort();
717            for dep_id in sorted {
718                let dep_block = unit.bb(*dep_id);
719                let dep_name = dep_block
720                    .base()
721                    .and_then(|b| b.opt_get_name())
722                    .unwrap_or("");
723                deps.push(format!("@fdep:{dep_id} {dep_name}"));
724            }
725        }
726
727        deps
728    }
729}
730
731#[derive(Debug)]
732pub struct BlockCall<'blk> {
733    pub base: BlockBase<'blk>,
734    pub callee: RwLock<Option<BlockId>>,
735    pub args: RwLock<Vec<BlockId>>,
736}
737
738impl<'blk> BlockCall<'blk> {
739    pub fn new(
740        id: BlockId,
741        node: HirNode<'blk>,
742        parent: Option<BlockId>,
743        children: Vec<BlockId>,
744    ) -> Self {
745        Self::new_with_symbol(id, node, parent, children, None)
746    }
747
748    pub fn new_with_symbol(
749        id: BlockId,
750        node: HirNode<'blk>,
751        parent: Option<BlockId>,
752        children: Vec<BlockId>,
753        symbol: Option<&'blk Symbol>,
754    ) -> Self {
755        let base = BlockBase::with_symbol(id, node, BlockKind::Call, parent, children, symbol);
756        Self {
757            base,
758            callee: RwLock::new(None),
759            args: RwLock::new(Vec::new()),
760        }
761    }
762
763    pub fn set_callee(&self, callee_id: BlockId) {
764        *self.callee.write() = Some(callee_id);
765    }
766
767    pub fn add_arg(&self, arg_id: BlockId) {
768        self.args.write().push(arg_id);
769    }
770
771    pub fn get_callee(&self) -> Option<BlockId> {
772        *self.callee.read()
773    }
774
775    pub fn get_args(&self) -> Vec<BlockId> {
776        self.args.read().clone()
777    }
778
779    pub fn format(&self) -> String {
780        format!("{}:{}", self.base.kind, self.base.id)
781    }
782}
783
784#[derive(Debug)]
785pub struct BlockClass<'blk> {
786    pub base: BlockBase<'blk>,
787    pub name: String,
788    pub fields: RwLock<Vec<BlockId>>,
789    pub methods: RwLock<Vec<BlockId>>,
790    /// Extended class (for TypeScript/JavaScript class inheritance)
791    /// A class can only extend one other class (single inheritance)
792    pub extends: RwLock<Option<(String, Option<BlockId>)>>,
793}
794
795impl<'blk> BlockClass<'blk> {
796    pub fn new(
797        id: BlockId,
798        node: HirNode<'blk>,
799        parent: Option<BlockId>,
800        children: Vec<BlockId>,
801    ) -> Self {
802        Self::new_with_symbol(id, node, parent, children, None)
803    }
804
805    pub fn new_with_symbol(
806        id: BlockId,
807        node: HirNode<'blk>,
808        parent: Option<BlockId>,
809        children: Vec<BlockId>,
810        symbol: Option<&'blk Symbol>,
811    ) -> Self {
812        let base = BlockBase::with_symbol(id, node, BlockKind::Class, parent, children, symbol);
813        let name = base.opt_get_name().unwrap_or("").to_string();
814        Self {
815            base,
816            name,
817            fields: RwLock::new(Vec::new()),
818            methods: RwLock::new(Vec::new()),
819            extends: RwLock::new(None),
820        }
821    }
822
823    pub fn add_field(&self, field_id: BlockId) {
824        self.fields.write().push(field_id);
825    }
826
827    pub fn add_method(&self, method_id: BlockId) {
828        self.methods.write().push(method_id);
829    }
830
831    pub fn get_fields(&self) -> Vec<BlockId> {
832        self.fields.read().clone()
833    }
834
835    pub fn get_methods(&self) -> Vec<BlockId> {
836        self.methods.read().clone()
837    }
838
839    /// Set the extended class
840    pub fn set_extends(&self, name: String, block_id: Option<BlockId>) {
841        *self.extends.write() = Some((name, block_id));
842    }
843
844    /// Get the extended class
845    pub fn get_extends(&self) -> Option<(String, Option<BlockId>)> {
846        self.extends.read().clone()
847    }
848
849    pub fn format(&self) -> String {
850        let extends = self.extends.read();
851        if let Some((name, id)) = extends.as_ref() {
852            if let Some(block_id) = id {
853                format!(
854                    "{}:{} {} @extends:{} {}",
855                    self.base.kind, self.base.id, self.name, block_id, name
856                )
857            } else {
858                format!(
859                    "{}:{} {} @extends {}",
860                    self.base.kind, self.base.id, self.name, name
861                )
862            }
863        } else {
864            format!("{}:{} {}", self.base.kind, self.base.id, self.name)
865        }
866    }
867
868    /// Format dependency entries as pseudo-children (to be rendered after real children)
869    /// Returns lines like "@tdep:3 Bar" for implemented interfaces
870    pub fn format_deps(&self, unit: CompileUnit<'blk>) -> Vec<String> {
871        let mut deps = Vec::new();
872
873        // Add type_deps (includes implemented interfaces)
874        let type_deps = self.base.get_type_deps();
875        if !type_deps.is_empty() {
876            let mut sorted: Vec<_> = type_deps.iter().collect();
877            sorted.sort();
878            for dep_id in sorted {
879                let dep_block = unit.bb(*dep_id);
880                let dep_name = dep_block
881                    .base()
882                    .and_then(|b| b.opt_get_name())
883                    .unwrap_or("");
884                deps.push(format!("@tdep:{} {}", dep_id, dep_name));
885            }
886        }
887
888        deps
889    }
890}
891
892#[derive(Debug)]
893pub struct BlockTrait<'blk> {
894    pub base: BlockBase<'blk>,
895    pub name: String,
896    pub methods: RwLock<Vec<BlockId>>,
897}
898
899impl<'blk> BlockTrait<'blk> {
900    pub fn new(
901        id: BlockId,
902        node: HirNode<'blk>,
903        parent: Option<BlockId>,
904        children: Vec<BlockId>,
905    ) -> Self {
906        Self::new_with_symbol(id, node, parent, children, None)
907    }
908
909    pub fn new_with_symbol(
910        id: BlockId,
911        node: HirNode<'blk>,
912        parent: Option<BlockId>,
913        children: Vec<BlockId>,
914        symbol: Option<&'blk Symbol>,
915    ) -> Self {
916        let base = BlockBase::with_symbol(id, node, BlockKind::Trait, parent, children, symbol);
917        let name = base.opt_get_name().unwrap_or("").to_string();
918        Self {
919            base,
920            name,
921            methods: RwLock::new(Vec::new()),
922        }
923    }
924
925    pub fn add_method(&self, method_id: BlockId) {
926        self.methods.write().push(method_id);
927    }
928
929    pub fn get_methods(&self) -> Vec<BlockId> {
930        self.methods.read().clone()
931    }
932
933    pub fn format(&self) -> String {
934        format!("{}:{} {}", self.base.kind, self.base.id, self.name)
935    }
936}
937
938/// Block representing a TypeScript interface declaration
939#[derive(Debug)]
940pub struct BlockInterface<'blk> {
941    pub base: BlockBase<'blk>,
942    pub name: String,
943    pub methods: RwLock<Vec<BlockId>>,
944    pub fields: RwLock<Vec<BlockId>>,
945    /// Extended interfaces (for TypeScript extends)
946    pub extends: RwLock<Vec<(String, Option<BlockId>)>>,
947}
948
949impl<'blk> BlockInterface<'blk> {
950    pub fn new(
951        id: BlockId,
952        node: HirNode<'blk>,
953        parent: Option<BlockId>,
954        children: Vec<BlockId>,
955    ) -> Self {
956        Self::new_with_symbol(id, node, parent, children, None)
957    }
958
959    pub fn new_with_symbol(
960        id: BlockId,
961        node: HirNode<'blk>,
962        parent: Option<BlockId>,
963        children: Vec<BlockId>,
964        symbol: Option<&'blk Symbol>,
965    ) -> Self {
966        let base = BlockBase::with_symbol(id, node, BlockKind::Interface, parent, children, symbol);
967        let name = base.opt_get_name().unwrap_or("").to_string();
968        Self {
969            base,
970            name,
971            methods: RwLock::new(Vec::new()),
972            fields: RwLock::new(Vec::new()),
973            extends: RwLock::new(Vec::new()),
974        }
975    }
976
977    pub fn add_method(&self, method_id: BlockId) {
978        self.methods.write().push(method_id);
979    }
980
981    pub fn get_methods(&self) -> Vec<BlockId> {
982        self.methods.read().clone()
983    }
984
985    pub fn add_field(&self, field_id: BlockId) {
986        self.fields.write().push(field_id);
987    }
988
989    pub fn get_fields(&self) -> Vec<BlockId> {
990        self.fields.read().clone()
991    }
992
993    /// Add an extended interface
994    pub fn add_extends(&self, name: String, block_id: Option<BlockId>) {
995        self.extends.write().push((name, block_id));
996    }
997
998    /// Get extended interfaces
999    pub fn get_extends(&self) -> Vec<(String, Option<BlockId>)> {
1000        self.extends.read().clone()
1001    }
1002
1003    pub fn format(&self) -> String {
1004        let extends = self.extends.read();
1005        if extends.is_empty() {
1006            format!("{}:{} {}", self.base.kind, self.base.id, self.name)
1007        } else {
1008            let extends_list: Vec<_> = extends
1009                .iter()
1010                .map(|(name, id)| {
1011                    if let Some(block_id) = id {
1012                        format!("@extends:{} {}", block_id, name)
1013                    } else {
1014                        format!("@extends {}", name)
1015                    }
1016                })
1017                .collect();
1018            format!(
1019                "{}:{} {} {}",
1020                self.base.kind,
1021                self.base.id,
1022                self.name,
1023                extends_list.join(" ")
1024            )
1025        }
1026    }
1027}
1028
1029#[derive(Debug)]
1030pub struct BlockImpl<'blk> {
1031    pub base: BlockBase<'blk>,
1032    pub name: String,
1033    /// Target type block ID (resolved during connect_blocks if needed)
1034    pub target: RwLock<Option<BlockId>>,
1035    /// Target type symbol (for deferred block_id resolution)
1036    pub target_sym: Option<&'blk Symbol>,
1037    /// Trait block ID (resolved during connect_blocks if needed)
1038    pub trait_ref: RwLock<Option<BlockId>>,
1039    /// Trait symbol (for deferred block_id resolution)
1040    pub trait_sym: Option<&'blk Symbol>,
1041    pub methods: RwLock<Vec<BlockId>>,
1042}
1043
1044impl<'blk> BlockImpl<'blk> {
1045    pub fn new(
1046        id: BlockId,
1047        node: HirNode<'blk>,
1048        parent: Option<BlockId>,
1049        children: Vec<BlockId>,
1050    ) -> Self {
1051        Self::new_with_symbol(id, node, parent, children, None)
1052    }
1053
1054    pub fn new_with_symbol(
1055        id: BlockId,
1056        node: HirNode<'blk>,
1057        parent: Option<BlockId>,
1058        children: Vec<BlockId>,
1059        symbol: Option<&'blk Symbol>,
1060    ) -> Self {
1061        let base = BlockBase::with_symbol(id, node, BlockKind::Impl, parent, children, symbol);
1062        let name = base.opt_get_name().unwrap_or("").to_string();
1063        Self {
1064            base,
1065            name,
1066            target: RwLock::new(None),
1067            target_sym: None,
1068            trait_ref: RwLock::new(None),
1069            trait_sym: None,
1070            methods: RwLock::new(Vec::new()),
1071        }
1072    }
1073
1074    /// Set target with both block_id (if available) and symbol (for deferred resolution)
1075    pub fn set_target_info(&mut self, block_id: Option<BlockId>, sym: Option<&'blk Symbol>) {
1076        *self.target.write() = block_id;
1077        self.target_sym = sym;
1078    }
1079
1080    /// Set trait with both block_id (if available) and symbol (for deferred resolution)
1081    pub fn set_trait_info(&mut self, block_id: Option<BlockId>, sym: Option<&'blk Symbol>) {
1082        *self.trait_ref.write() = block_id;
1083        self.trait_sym = sym;
1084    }
1085
1086    pub fn set_target(&self, target_id: BlockId) {
1087        *self.target.write() = Some(target_id);
1088    }
1089
1090    pub fn set_trait_ref(&self, trait_id: BlockId) {
1091        *self.trait_ref.write() = Some(trait_id);
1092    }
1093
1094    pub fn add_method(&self, method_id: BlockId) {
1095        self.methods.write().push(method_id);
1096    }
1097
1098    pub fn get_target(&self) -> Option<BlockId> {
1099        *self.target.read()
1100    }
1101
1102    pub fn get_trait_ref(&self) -> Option<BlockId> {
1103        *self.trait_ref.read()
1104    }
1105
1106    pub fn get_methods(&self) -> Vec<BlockId> {
1107        self.methods.read().clone()
1108    }
1109
1110    pub fn format(&self) -> String {
1111        let mut parts = vec![format!("{}:{} {}", self.base.kind, self.base.id, self.name)];
1112        if let Some(target_id) = self.get_target() {
1113            parts.push(format!("@type:{target_id}"));
1114        }
1115        if let Some(trait_id) = self.get_trait_ref() {
1116            parts.push(format!("@trait:{trait_id}"));
1117        }
1118        parts.join(" ")
1119    }
1120}
1121
1122#[derive(Debug)]
1123pub struct BlockEnum<'blk> {
1124    pub base: BlockBase<'blk>,
1125    pub name: String,
1126    pub variants: RwLock<Vec<BlockId>>,
1127}
1128
1129impl<'blk> BlockEnum<'blk> {
1130    pub fn new(
1131        id: BlockId,
1132        node: HirNode<'blk>,
1133        parent: Option<BlockId>,
1134        children: Vec<BlockId>,
1135    ) -> Self {
1136        Self::new_with_symbol(id, node, parent, children, None)
1137    }
1138
1139    pub fn new_with_symbol(
1140        id: BlockId,
1141        node: HirNode<'blk>,
1142        parent: Option<BlockId>,
1143        children: Vec<BlockId>,
1144        symbol: Option<&'blk Symbol>,
1145    ) -> Self {
1146        let base = BlockBase::with_symbol(id, node, BlockKind::Enum, parent, children, symbol);
1147        let name = base.opt_get_name().unwrap_or("").to_string();
1148        Self {
1149            base,
1150            name,
1151            variants: RwLock::new(Vec::new()),
1152        }
1153    }
1154
1155    pub fn add_variant(&self, variant_id: BlockId) {
1156        self.variants.write().push(variant_id);
1157    }
1158
1159    pub fn get_variants(&self) -> Vec<BlockId> {
1160        self.variants.read().clone()
1161    }
1162
1163    pub fn format(&self) -> String {
1164        format!("{}:{} {}", self.base.kind, self.base.id, self.name)
1165    }
1166}
1167
1168#[derive(Debug)]
1169pub struct BlockConst<'blk> {
1170    pub base: BlockBase<'blk>,
1171    pub name: String,
1172    /// Type name for display (e.g., "i32", "String")
1173    pub type_name: String,
1174    /// Block ID of the type definition (for user-defined types)
1175    pub type_ref: RwLock<Option<BlockId>>,
1176}
1177
1178impl<'blk> BlockConst<'blk> {
1179    pub fn new(
1180        id: BlockId,
1181        node: HirNode<'blk>,
1182        parent: Option<BlockId>,
1183        children: Vec<BlockId>,
1184    ) -> Self {
1185        Self::new_with_symbol(id, node, parent, children, None)
1186    }
1187
1188    pub fn new_with_symbol(
1189        id: BlockId,
1190        node: HirNode<'blk>,
1191        parent: Option<BlockId>,
1192        children: Vec<BlockId>,
1193        symbol: Option<&'blk Symbol>,
1194    ) -> Self {
1195        let base = BlockBase::with_symbol(id, node, BlockKind::Const, parent, children, symbol);
1196        let name = base.opt_get_name().unwrap_or("").to_string();
1197        Self {
1198            base,
1199            name,
1200            type_name: String::new(),
1201            type_ref: RwLock::new(None),
1202        }
1203    }
1204
1205    /// Set type info for this const block (used during block building)
1206    pub fn set_type_info(&mut self, type_name: String, type_ref: Option<BlockId>) {
1207        self.type_name = type_name;
1208        *self.type_ref.write() = type_ref;
1209    }
1210
1211    /// Set type reference (used during connect_blocks for cross-file resolution)
1212    pub fn set_type_ref(&self, type_ref: BlockId) {
1213        *self.type_ref.write() = Some(type_ref);
1214    }
1215
1216    /// Get the type reference
1217    pub fn get_type_ref(&self) -> Option<BlockId> {
1218        *self.type_ref.read()
1219    }
1220
1221    pub fn format(&self) -> String {
1222        if !self.type_name.is_empty() {
1223            if let Some(type_id) = *self.type_ref.read() {
1224                return format!(
1225                    "{}:{} {} @type:{} {}",
1226                    self.base.kind, self.base.id, self.name, type_id, self.type_name
1227                );
1228            } else {
1229                return format!(
1230                    "{}:{} {} @type {}",
1231                    self.base.kind, self.base.id, self.name, self.type_name
1232                );
1233            }
1234        }
1235        format!("{}:{} {}", self.base.kind, self.base.id, self.name)
1236    }
1237}
1238
1239#[derive(Debug)]
1240pub struct BlockField<'blk> {
1241    pub base: BlockBase<'blk>,
1242    pub name: String,
1243    /// Type name for display (e.g., "i32", "String")
1244    pub type_name: String,
1245    /// Block ID of the type definition (for user-defined types)
1246    pub type_ref: RwLock<Option<BlockId>>,
1247}
1248
1249impl<'blk> BlockField<'blk> {
1250    pub fn new(
1251        id: BlockId,
1252        node: HirNode<'blk>,
1253        parent: Option<BlockId>,
1254        children: Vec<BlockId>,
1255    ) -> Self {
1256        Self::new_with_symbol(id, node, parent, children, None)
1257    }
1258
1259    pub fn new_with_symbol(
1260        id: BlockId,
1261        node: HirNode<'blk>,
1262        parent: Option<BlockId>,
1263        children: Vec<BlockId>,
1264        symbol: Option<&'blk Symbol>,
1265    ) -> Self {
1266        let base = BlockBase::with_symbol(id, node, BlockKind::Field, parent, children, symbol);
1267        // Try to get name from the node's scope first, then from ident
1268        let name = base
1269            .opt_get_name()
1270            .map(|s| s.to_string())
1271            .or_else(|| {
1272                // For field nodes, the identifier child has the name
1273                node.as_scope()
1274                    .and_then(|scope| scope.opt_ident())
1275                    .map(|ident| ident.name.to_string())
1276            })
1277            .unwrap_or_default();
1278        Self {
1279            base,
1280            name,
1281            type_name: String::new(),
1282            type_ref: RwLock::new(None),
1283        }
1284    }
1285
1286    /// Set type info for this field block (used during block building)
1287    pub fn set_type_info(&mut self, type_name: String, type_ref: Option<BlockId>) {
1288        self.type_name = type_name;
1289        *self.type_ref.write() = type_ref;
1290    }
1291
1292    /// Set type reference (used during connect_blocks for cross-file resolution)
1293    pub fn set_type_ref(&self, type_ref: BlockId) {
1294        *self.type_ref.write() = Some(type_ref);
1295    }
1296
1297    /// Get the type reference
1298    pub fn get_type_ref(&self) -> Option<BlockId> {
1299        *self.type_ref.read()
1300    }
1301
1302    pub fn format(&self) -> String {
1303        if !self.type_name.is_empty() {
1304            if let Some(type_id) = *self.type_ref.read() {
1305                return format!(
1306                    "{}:{} {} @type:{} {}",
1307                    self.base.kind, self.base.id, self.name, type_id, self.type_name
1308                );
1309            } else {
1310                return format!(
1311                    "{}:{} {} @type {}",
1312                    self.base.kind, self.base.id, self.name, self.type_name
1313                );
1314            }
1315        }
1316        format!("{}:{} {}", self.base.kind, self.base.id, self.name)
1317    }
1318}
1319
1320/// Represents a single function/method parameter as its own block
1321#[derive(Debug)]
1322pub struct BlockParameter<'blk> {
1323    pub base: BlockBase<'blk>,
1324    /// Parameter name (e.g., "x", "self")
1325    pub name: String,
1326    /// Type name for display (e.g., "i32", "String")
1327    pub type_name: String,
1328    /// Block ID of the type definition (for user-defined types)
1329    pub type_ref: RwLock<Option<BlockId>>,
1330}
1331
1332impl<'blk> BlockParameter<'blk> {
1333    /// Create a new BlockParameter for function/method parameters
1334    pub fn new(
1335        id: BlockId,
1336        node: HirNode<'blk>,
1337        parent: Option<BlockId>,
1338        children: Vec<BlockId>,
1339    ) -> Self {
1340        Self::new_with_symbol(id, node, parent, children, None)
1341    }
1342
1343    pub fn new_with_symbol(
1344        id: BlockId,
1345        node: HirNode<'blk>,
1346        parent: Option<BlockId>,
1347        children: Vec<BlockId>,
1348        symbol: Option<&'blk Symbol>,
1349    ) -> Self {
1350        let base = BlockBase::with_symbol(id, node, BlockKind::Parameter, parent, children, symbol);
1351        // Try to get name from the node's scope first, then from ident
1352        let name = base
1353            .opt_get_name()
1354            .map(|s| s.to_string())
1355            .or_else(|| {
1356                // For parameter nodes, the identifier child has the name
1357                node.as_scope()
1358                    .and_then(|scope| scope.opt_ident())
1359                    .map(|ident| ident.name.to_string())
1360            })
1361            .unwrap_or_default();
1362        Self {
1363            base,
1364            name,
1365            type_name: String::new(),
1366            type_ref: RwLock::new(None),
1367        }
1368    }
1369
1370    /// Set type info for this parameter block (used during block building)
1371    pub fn set_type_info(&mut self, type_name: String, type_ref: Option<BlockId>) {
1372        self.type_name = type_name;
1373        *self.type_ref.write() = type_ref;
1374    }
1375
1376    /// Set type reference (used during connect_blocks for cross-file resolution)
1377    pub fn set_type_ref(&self, type_ref: BlockId) {
1378        *self.type_ref.write() = Some(type_ref);
1379    }
1380
1381    /// Get the type reference
1382    pub fn get_type_ref(&self) -> Option<BlockId> {
1383        *self.type_ref.read()
1384    }
1385
1386    pub fn format(&self) -> String {
1387        if !self.type_name.is_empty() {
1388            if let Some(type_id) = *self.type_ref.read() {
1389                return format!(
1390                    "{}:{} {} @type:{} {}",
1391                    self.base.kind, self.base.id, self.name, type_id, self.type_name
1392                );
1393            } else {
1394                return format!(
1395                    "{}:{} {} @type {}",
1396                    self.base.kind, self.base.id, self.name, self.type_name
1397                );
1398            }
1399        }
1400        format!("{}:{} {}", self.base.kind, self.base.id, self.name)
1401    }
1402}
1403
1404/// Represents a function/method return type as its own block
1405#[derive(Debug)]
1406pub struct BlockReturn<'blk> {
1407    pub base: BlockBase<'blk>,
1408    /// Type name for display (e.g., "i32", "String")
1409    pub type_name: String,
1410    /// Block ID of the type definition (for user-defined types)
1411    pub type_ref: RwLock<Option<BlockId>>,
1412}
1413
1414impl<'blk> BlockReturn<'blk> {
1415    pub fn new(
1416        id: BlockId,
1417        node: HirNode<'blk>,
1418        parent: Option<BlockId>,
1419        children: Vec<BlockId>,
1420    ) -> Self {
1421        Self::new_with_symbol(id, node, parent, children, None)
1422    }
1423
1424    pub fn new_with_symbol(
1425        id: BlockId,
1426        node: HirNode<'blk>,
1427        parent: Option<BlockId>,
1428        children: Vec<BlockId>,
1429        symbol: Option<&'blk Symbol>,
1430    ) -> Self {
1431        let base = BlockBase::with_symbol(id, node, BlockKind::Return, parent, children, symbol);
1432        Self {
1433            base,
1434            type_name: String::new(),
1435            type_ref: RwLock::new(None),
1436        }
1437    }
1438
1439    /// Set type info for this return block (used during block building)
1440    pub fn set_type_info(&mut self, type_name: String, type_ref: Option<BlockId>) {
1441        self.type_name = type_name;
1442        *self.type_ref.write() = type_ref;
1443    }
1444
1445    /// Set type reference (used during connect_blocks for cross-file resolution)
1446    pub fn set_type_ref(&self, type_ref: BlockId) {
1447        *self.type_ref.write() = Some(type_ref);
1448    }
1449
1450    /// Get the type reference
1451    pub fn get_type_ref(&self) -> Option<BlockId> {
1452        *self.type_ref.read()
1453    }
1454
1455    pub fn format(&self) -> String {
1456        if !self.type_name.is_empty() {
1457            if let Some(type_id) = *self.type_ref.read() {
1458                return format!(
1459                    "{}:{} @type:{} {}",
1460                    self.base.kind, self.base.id, type_id, self.type_name
1461                );
1462            } else {
1463                return format!(
1464                    "{}:{} @type {}",
1465                    self.base.kind, self.base.id, self.type_name
1466                );
1467            }
1468        }
1469        format!("{}:{}", self.base.kind, self.base.id)
1470    }
1471}
1472
1473#[derive(Debug)]
1474pub struct BlockAlias<'blk> {
1475    pub base: BlockBase<'blk>,
1476    pub name: String,
1477}
1478
1479impl<'blk> BlockAlias<'blk> {
1480    pub fn new(
1481        id: BlockId,
1482        node: HirNode<'blk>,
1483        parent: Option<BlockId>,
1484        children: Vec<BlockId>,
1485    ) -> Self {
1486        Self::new_with_symbol(id, node, parent, children, None)
1487    }
1488
1489    pub fn new_with_symbol(
1490        id: BlockId,
1491        node: HirNode<'blk>,
1492        parent: Option<BlockId>,
1493        children: Vec<BlockId>,
1494        symbol: Option<&'blk Symbol>,
1495    ) -> Self {
1496        let base = BlockBase::with_symbol(id, node, BlockKind::Alias, parent, children, symbol);
1497        let name = base.opt_get_name().unwrap_or("").to_string();
1498        Self { base, name }
1499    }
1500
1501    pub fn format(&self) -> String {
1502        format!("{}:{} {}", self.base.kind, self.base.id, self.name)
1503    }
1504}