1use rayon::iter::{IntoParallelIterator, ParallelIterator};
2use std::marker::PhantomData;
3
4use crate::DynError;
5pub use crate::block::{BasicBlock, BlockId, BlockKind, BlockRelation};
6use crate::block::{
7 BlockAlias, BlockCall, BlockClass, BlockConst, BlockEnum, BlockField, BlockFunc, BlockImpl,
8 BlockInterface, BlockModule, BlockParameter, BlockReturn, BlockRoot, BlockTrait,
9};
10use crate::context::{CompileCtxt, CompileUnit};
11use crate::graph::UnitGraph;
12use crate::ir::HirNode;
13use crate::lang_def::LanguageTrait;
14use crate::visit::HirVisitor;
15
16#[derive(Debug, Clone, Copy, Default)]
17pub struct GraphBuildConfig;
18
19#[derive(Debug, Clone, Copy, Default)]
20pub struct GraphBuildOption {
21 pub sequential: bool,
22}
23
24impl GraphBuildOption {
25 pub fn new() -> Self {
26 Self::default()
27 }
28
29 pub fn with_sequential(mut self, sequential: bool) -> Self {
30 self.sequential = sequential;
31 self
32 }
33}
34
35#[derive(Debug)]
36struct GraphBuilder<'tcx, Language> {
37 unit: CompileUnit<'tcx>,
38 root: Option<BlockId>,
39 children_stack: Vec<Vec<(BlockId, BlockKind)>>,
41 parent_kind_stack: Vec<BlockKind>,
43 _config: GraphBuildConfig,
44 _marker: PhantomData<Language>,
45}
46
47impl<'tcx, Language: LanguageTrait> GraphBuilder<'tcx, Language> {
48 fn new(unit: CompileUnit<'tcx>, config: GraphBuildConfig) -> Self {
49 Self {
50 unit,
51 root: None,
52 children_stack: Vec::new(),
53 parent_kind_stack: Vec::new(),
54 _config: config,
55 _marker: PhantomData,
56 }
57 }
58
59 fn next_id(&self) -> BlockId {
60 self.unit.reserve_block_id()
61 }
62
63 fn resolve_type_info(
66 &self,
67 symbol: Option<&'tcx crate::symbol::Symbol>,
68 ) -> (String, Option<BlockId>) {
69 let sym = match symbol {
70 Some(s) => s,
71 None => return (String::new(), None),
72 };
73
74 if sym.kind() == crate::symbol::SymKind::EnumVariant {
77 return (String::new(), None);
78 }
79
80 if let Some(type_sym_id) = sym.type_of()
82 && let Some(type_sym) = self.unit.opt_get_symbol(type_sym_id)
83 {
84 let effective_type = if type_sym.kind() == crate::symbol::SymKind::TypeParameter
86 && let Some(bound_id) = type_sym.type_of()
87 {
88 self.unit.opt_get_symbol(bound_id).unwrap_or(type_sym)
89 } else {
90 type_sym
91 };
92
93 let type_name = self
94 .unit
95 .resolve_interned_owned(effective_type.name)
96 .unwrap_or_default();
97 let type_block_id = effective_type.block_id();
98 return (type_name, type_block_id);
99 }
100
101 let type_name = self
103 .unit
104 .resolve_interned_owned(sym.name)
105 .unwrap_or_default();
106 let type_block_id = sym.block_id();
107 (type_name, type_block_id)
108 }
109
110 fn extract_symbol(
115 &self,
116 node: HirNode<'tcx>,
117 kind: BlockKind,
118 ) -> Option<&'tcx crate::symbol::Symbol> {
119 if kind == BlockKind::Impl {
122 return None;
123 }
124
125 if let Some(scope) = node.as_scope()
127 && let Some(sym) = scope.opt_symbol()
128 {
129 return Some(sym);
130 }
131
132 if kind == BlockKind::Field
134 && let Some(ident) = node.ident_by_field(&self.unit, Language::name_field())
135 && let Some(sym) = ident.opt_symbol()
136 {
137 return Some(sym);
138 }
139
140 if kind == BlockKind::Parameter {
143 for ident in node.collect_idents(&self.unit) {
144 if let Some(sym) = ident.opt_symbol()
145 && matches!(sym.kind(), crate::symbol::SymKind::Variable)
146 {
147 return Some(sym);
148 }
149 }
150 }
151
152 if let Some(ident) = node.find_ident(&self.unit)
154 && let Some(sym) = ident.opt_symbol()
155 {
156 return Some(sym);
157 }
158
159 for child in node.children(&self.unit) {
161 if let Some(ident) = child.as_ident()
162 && let Some(sym) = ident.opt_symbol()
163 {
164 return Some(sym);
165 }
166 }
167
168 None
169 }
170
171 fn create_block(
172 &self,
173 id: BlockId,
174 node: HirNode<'tcx>,
175 kind: BlockKind,
176 parent: Option<BlockId>,
177 children: Vec<BlockId>,
178 ) -> BasicBlock<'tcx> {
179 let symbol = self.extract_symbol(node, kind);
181
182 match kind {
185 BlockKind::Root => {
186 let file_name = node
188 .as_file()
189 .map(|file| file.file_path.clone())
190 .or_else(|| self.unit.file_path().map(|s| s.to_string()));
191 let block = BlockRoot::new_with_symbol(
192 id,
193 node,
194 parent,
195 children,
196 file_name.clone(),
197 symbol,
198 );
199
200 if let Some(scope_node) = node.as_scope()
204 && let Some(scope) = scope_node.opt_scope()
205 {
206 use crate::symbol::SymKind;
207
208 let meta = self.unit.unit_meta();
210 if let Some(ref pkg_name) = meta.package_name {
211 block.set_crate_name(pkg_name.clone());
212 }
213 if let Some(ref pkg_root) = meta.package_root {
214 block.set_crate_root(pkg_root.display().to_string());
215 }
216 if let Some(ref mod_name) = meta.module_name {
217 block.set_module_path(mod_name.clone());
218 }
219 if let Some(ref mod_root) = meta.module_root {
220 block.set_module_root(mod_root.display().to_string());
221 }
222
223 if meta.package_name.is_none()
225 && let Some(crate_sym) = scope.find_parent_by_kind(SymKind::Crate)
226 && let Some(name) = self.unit.cc.interner.resolve_owned(crate_sym.name)
227 {
228 block.set_crate_name(name);
229 }
230 if meta.module_name.is_none()
231 && let Some(module_sym) = scope.find_parent_by_kind(SymKind::Module)
232 && let Some(name) = self.unit.cc.interner.resolve_owned(module_sym.name)
233 {
234 block.set_module_path(name);
235 }
236 }
237
238 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
239 BasicBlock::Root(block_ref)
240 }
241 BlockKind::Func | BlockKind::Method => {
242 let block = BlockFunc::new_with_symbol(id, node, kind, parent, children, symbol);
243 if kind == BlockKind::Method {
244 block.set_is_method(true);
245 }
246 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
247 BasicBlock::Func(block_ref)
248 }
249 BlockKind::Class => {
250 let block = BlockClass::new_with_symbol(id, node, parent, children, symbol);
251 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
252 BasicBlock::Class(block_ref)
253 }
254 BlockKind::Trait => {
255 let block = BlockTrait::new_with_symbol(id, node, parent, children, symbol);
256 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
257 BasicBlock::Trait(block_ref)
258 }
259 BlockKind::Interface => {
260 let block = BlockInterface::new_with_symbol(id, node, parent, children, symbol);
261 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
262 BasicBlock::Interface(block_ref)
263 }
264 BlockKind::Call => {
265 let stmt = BlockCall::new_with_symbol(id, node, parent, children, symbol);
267 if let Some(callee_sym) = node.ident_symbol(&self.unit)
269 && let Some(callee_block_id) = callee_sym.block_id()
270 {
271 stmt.set_callee(callee_block_id);
272 }
273 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, stmt);
274 BasicBlock::Call(block_ref)
275 }
276 BlockKind::Enum => {
277 let enum_ty = BlockEnum::new_with_symbol(id, node, parent, children, symbol);
278 let block_ref = self
279 .unit
280 .cc
281 .block_arena
282 .alloc_with_id(id.0 as usize, enum_ty);
283 BasicBlock::Enum(block_ref)
284 }
285 BlockKind::Const => {
286 let mut stmt = BlockConst::new_with_symbol(id, node, parent, children, symbol);
287 if let Some(ident) = node.find_ident(&self.unit) {
289 stmt.name = ident.name.to_string();
290 }
291 let (type_name, type_ref) = self.resolve_type_info(symbol);
293 stmt.set_type_info(type_name, type_ref);
294 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, stmt);
295 BasicBlock::Const(block_ref)
296 }
297 BlockKind::Impl => {
298 let mut block = BlockImpl::new(id, node, parent, children);
300
301 if let Some(target_ident) = node.ident_by_field(&self.unit, Language::type_field())
303 && let Some(sym) = target_ident.opt_symbol()
304 {
305 let resolved = sym
307 .type_of()
308 .and_then(|id| self.unit.opt_get_symbol(id))
309 .unwrap_or(sym);
310 block.set_target_info(resolved.block_id(), Some(sym));
312 }
313
314 if let Some(trait_ident) = node.ident_by_field(&self.unit, Language::trait_field())
316 && let Some(sym) = trait_ident.opt_symbol()
317 {
318 let resolved = sym
320 .type_of()
321 .and_then(|id| self.unit.opt_get_symbol(id))
322 .unwrap_or(sym);
323 block.set_trait_info(resolved.block_id(), Some(resolved));
324 }
325
326 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
327 BasicBlock::Impl(block_ref)
328 }
329 BlockKind::Field => {
330 let mut block = BlockField::new_with_symbol(id, node, parent, children, symbol);
331 if let Some(ident) = node.ident_by_field(&self.unit, Language::name_field()) {
334 block.name = ident.name.to_string();
335 } else if let Some(ident) = node.find_ident(&self.unit) {
336 block.name = ident.name.to_string();
338 }
339 let (type_name, type_ref) = self.resolve_type_info(symbol);
341 block.set_type_info(type_name, type_ref);
342 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
343 BasicBlock::Field(block_ref)
344 }
345 BlockKind::Parameter => {
346 let mut block = BlockParameter::new_with_symbol(id, node, parent, children, symbol);
347 if let Some(ident) = node.collect_idents(&self.unit).into_iter().find(|ident| {
349 ident
350 .opt_symbol()
351 .is_some_and(|sym| matches!(sym.kind(), crate::symbol::SymKind::Variable))
352 }) {
353 block.name = ident.name.to_string();
354 } else if let Some(ident) = node.find_ident(&self.unit) {
355 block.name = ident.name.to_string();
356 } else if let Some(text) = node.find_text(&self.unit) {
357 block.name = text.to_string();
359 }
360 let (type_name, type_ref) = self.resolve_type_info(symbol);
361 block.set_type_info(type_name, type_ref);
362 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
363 BasicBlock::Parameter(block_ref)
364 }
365 BlockKind::Return => {
366 let mut block = BlockReturn::new_with_symbol(id, node, parent, children, symbol);
368 let (type_name, type_ref) = self.resolve_type_info(symbol);
369 block.set_type_info(type_name, type_ref);
370 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
371 BasicBlock::Return(block_ref)
372 }
373 BlockKind::Alias => {
374 let mut block = BlockAlias::new_with_symbol(id, node, parent, children, symbol);
375 if let Some(ident) = node.find_ident(&self.unit) {
377 block.name = ident.name.to_string();
378 }
379 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
380 BasicBlock::Alias(block_ref)
381 }
382 BlockKind::Module => {
383 let name = node
385 .find_ident(&self.unit)
386 .map(|ident| ident.name.to_string())
387 .unwrap_or_default();
388 let is_inline = !children.is_empty();
390 let block = BlockModule::new_with_symbol(
391 id, node, parent, children, name, is_inline, symbol,
392 );
393 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
394 BasicBlock::Module(block_ref)
395 }
396 _ => {
397 panic!("unknown block kind: {kind}")
398 }
399 }
400 }
401
402 fn build_block(
403 &mut self,
404 _unit: CompileUnit<'tcx>,
405 node: HirNode<'tcx>,
406 parent: BlockId,
407 recursive: bool,
408 ) {
409 let id = self.next_id();
410 let field_kind = Language::block_kind(node.field_id());
412 let mut block_kind = if field_kind != BlockKind::Undefined {
413 field_kind
414 } else {
415 Language::block_kind(node.kind_id())
416 };
417 assert_ne!(block_kind, BlockKind::Undefined);
418
419 if block_kind == BlockKind::Func {
423 let is_method_by_symbol = node
424 .opt_symbol()
425 .is_some_and(|sym| sym.kind() == crate::symbol::SymKind::Method);
426
427 let is_in_impl = self
429 .parent_kind_stack
430 .last()
431 .is_some_and(|&k| k == BlockKind::Impl);
432
433 if is_method_by_symbol || is_in_impl {
434 block_kind = BlockKind::Method;
435 }
436 }
437
438 if self.root.is_none() {
439 self.root = Some(id);
440 }
441
442 if block_kind != BlockKind::Impl && block_kind != BlockKind::Return {
447 node.set_block_id(id);
448 }
449
450 let children_with_kinds = if recursive {
451 self.children_stack.push(Vec::new());
452 self.parent_kind_stack.push(block_kind);
453 self.visit_children(self.unit, node, id);
454 self.parent_kind_stack.pop();
455 self.children_stack.pop().unwrap()
456 } else {
457 Vec::new()
458 };
459
460 let child_ids: Vec<BlockId> = children_with_kinds.iter().map(|(id, _)| *id).collect();
461 let block = self.create_block(id, node, block_kind, Some(parent), child_ids);
462 self.populate_block_fields(node, &block, &children_with_kinds);
463 self.unit.insert_block(id, block, parent);
464
465 if let Some(children) = self.children_stack.last_mut() {
466 children.push((id, block_kind));
467 }
468 }
469
470 fn build_block_with_kind_and_index(
473 &mut self,
474 _unit: CompileUnit<'tcx>,
475 node: HirNode<'tcx>,
476 parent: BlockId,
477 block_kind: BlockKind,
478 index: usize,
479 ) {
480 let id = self.next_id();
481
482 if self.root.is_none() {
483 self.root = Some(id);
484 }
485
486 let child_ids = Vec::new();
488
489 let block = if block_kind == BlockKind::Field {
491 self.create_tuple_field_block(id, node, Some(parent), child_ids, index)
492 } else {
493 self.create_block(id, node, block_kind, Some(parent), child_ids)
494 };
495
496 self.unit.insert_block(id, block, parent);
497
498 if let Some(children) = self.children_stack.last_mut() {
499 children.push((id, block_kind));
500 }
501 }
502
503 fn create_tuple_field_block(
505 &self,
506 id: BlockId,
507 node: HirNode<'tcx>,
508 parent: Option<BlockId>,
509 children: Vec<BlockId>,
510 index: usize,
511 ) -> BasicBlock<'tcx> {
512 let mut type_symbol = node
518 .find_ident(&self.unit)
519 .and_then(|ident| ident.opt_symbol());
520
521 if type_symbol.is_none() {
523 for child in node.children(&self.unit) {
524 if let Some(sym) = child.opt_symbol() {
525 type_symbol = Some(sym);
526 break;
527 }
528 }
529 }
530 if type_symbol.is_none()
532 && let Some(scope) = node.as_scope()
533 && let Some(ident) = *scope.ident.read()
534 {
535 type_symbol = ident.opt_symbol();
536 }
537 if type_symbol.is_none() {
539 type_symbol = node.opt_symbol();
540 }
541
542 let mut block = BlockField::new_with_symbol(id, node, parent, children, type_symbol);
543 block.name = index.to_string();
544 let (type_name, type_ref) = self.resolve_type_info(type_symbol);
546 block.set_type_info(type_name, type_ref);
547 let block_ref = self.unit.cc.block_arena.alloc_with_id(id.0 as usize, block);
548 BasicBlock::Field(block_ref)
549 }
550
551 fn populate_block_fields(
553 &self,
554 _node: HirNode<'tcx>,
555 block: &BasicBlock<'tcx>,
556 children: &[(BlockId, BlockKind)],
557 ) {
558 match block {
559 BasicBlock::Func(func) => {
560 for &(child_id, child_kind) in children {
561 match child_kind {
562 BlockKind::Parameter => func.add_parameter(child_id),
563 BlockKind::Return => func.set_returns(child_id),
564 _ => {}
565 }
566 }
567 }
568 BasicBlock::Class(class) => {
569 for &(child_id, child_kind) in children {
570 match child_kind {
571 BlockKind::Field => class.add_field(child_id),
572 BlockKind::Func | BlockKind::Method => class.add_method(child_id),
573 _ => {}
574 }
575 }
576 }
577 BasicBlock::Enum(enum_block) => {
578 for &(child_id, child_kind) in children {
579 if child_kind == BlockKind::Field {
580 enum_block.add_variant(child_id);
581 }
582 }
583 }
584 BasicBlock::Trait(trait_block) => {
585 for &(child_id, child_kind) in children {
586 if matches!(child_kind, BlockKind::Func | BlockKind::Method) {
587 trait_block.add_method(child_id);
588 }
589 }
590 }
591 BasicBlock::Interface(iface_block) => {
592 for &(child_id, child_kind) in children {
593 match child_kind {
594 BlockKind::Field => iface_block.add_field(child_id),
595 BlockKind::Func | BlockKind::Method => iface_block.add_method(child_id),
596 _ => {}
597 }
598 }
599 }
600 BasicBlock::Impl(impl_block) => {
601 for &(child_id, child_kind) in children {
603 if matches!(child_kind, BlockKind::Func | BlockKind::Method) {
604 impl_block.add_method(child_id);
605 }
606 }
607 }
610 _ => {}
611 }
612 }
613
614 fn effective_block_kind(node: HirNode<'tcx>) -> BlockKind {
616 let field_kind = Language::block_kind(node.field_id());
617 if field_kind != BlockKind::Undefined {
618 field_kind
619 } else {
620 Language::block_kind(node.kind_id())
621 }
622 }
623
624 fn is_block_kind(kind: BlockKind) -> bool {
626 matches!(
627 kind,
628 BlockKind::Func
629 | BlockKind::Method
630 | BlockKind::Class
631 | BlockKind::Trait
632 | BlockKind::Interface
633 | BlockKind::Enum
634 | BlockKind::Const
635 | BlockKind::Impl
636 | BlockKind::Field
637 | BlockKind::Parameter
638 | BlockKind::Return
639 | BlockKind::Call
640 | BlockKind::Root
641 | BlockKind::Alias
642 | BlockKind::Module
643 )
644 }
645}
646
647impl<'tcx, Language: LanguageTrait> HirVisitor<'tcx> for GraphBuilder<'tcx, Language> {
648 fn visit_children(&mut self, unit: CompileUnit<'tcx>, node: HirNode<'tcx>, parent: BlockId) {
649 let parent_kind_id = node.kind_id();
650 let children = node.child_ids();
651 let children_vec: Vec<_> = children.iter().map(|id| unit.hir_node(*id)).collect();
652 let mut tuple_field_index = 0usize;
653
654 for child in children_vec.iter() {
658 let base_kind = Self::effective_block_kind(*child);
661 let context_kind =
662 Language::block_kind_with_parent(child.kind_id(), child.field_id(), parent_kind_id);
663
664 if context_kind != base_kind && Self::is_block_kind(context_kind) {
665 self.build_block_with_kind_and_index(
668 unit,
669 *child,
670 parent,
671 context_kind,
672 tuple_field_index,
673 );
674 tuple_field_index += 1;
675 } else if context_kind == BlockKind::Undefined && Self::is_block_kind(base_kind) {
676 self.visit_children(unit, *child, parent);
679 } else {
680 self.visit_node(unit, *child, parent);
682 }
683 }
684 }
685
686 fn visit_file(&mut self, unit: CompileUnit<'tcx>, node: HirNode<'tcx>, parent: BlockId) {
687 self.children_stack.push(Vec::new());
688 self.build_block(unit, node, parent, true);
689 }
690
691 fn visit_internal(&mut self, unit: CompileUnit<'tcx>, node: HirNode<'tcx>, parent: BlockId) {
692 let kind = Self::effective_block_kind(node);
693 if Self::is_block_kind(kind) && kind != BlockKind::Root {
694 self.build_block(unit, node, parent, false);
695 } else {
696 self.visit_children(unit, node, parent);
697 }
698 }
699
700 fn visit_scope(&mut self, unit: CompileUnit<'tcx>, node: HirNode<'tcx>, parent: BlockId) {
701 let kind = Self::effective_block_kind(node);
702 if Self::is_block_kind(kind) {
703 self.build_block(unit, node, parent, true);
704 } else {
705 self.visit_children(unit, node, parent);
706 }
707 }
708
709 fn visit_ident(&mut self, unit: CompileUnit<'tcx>, node: HirNode<'tcx>, parent: BlockId) {
710 let kind = Self::effective_block_kind(node);
711 if Self::is_block_kind(kind) {
712 self.build_block(unit, node, parent, false);
713 } else {
714 self.visit_children(unit, node, parent);
715 }
716 }
717}
718
719pub fn build_unit_graph<'tcx, L: LanguageTrait>(
720 unit: CompileUnit<'tcx>,
721 unit_index: usize,
722 config: GraphBuildConfig,
723) -> Result<Option<UnitGraph>, DynError> {
724 let root_hir = unit.file_root_id().ok_or("missing file start HIR id")?;
725 let mut builder = GraphBuilder::<L>::new(unit, config);
726 let root_node = unit.hir_node(root_hir);
727 builder.visit_node(unit, root_node, BlockId::ROOT_PARENT);
728
729 match builder.root {
731 Some(root_block) => Ok(Some(UnitGraph::new(unit_index, root_block))),
732 None => Ok(None),
733 }
734}
735
736pub fn build_llmcc_graph<'tcx, L: LanguageTrait>(
738 cc: &'tcx CompileCtxt<'tcx>,
739 config: GraphBuildOption,
740) -> Result<Vec<UnitGraph>, DynError> {
741 let unit_graphs: Vec<UnitGraph> = if config.sequential {
742 (0..cc.get_files().len())
743 .map(|index| {
744 let unit = cc.compile_unit(index);
745 build_unit_graph::<L>(unit, index, GraphBuildConfig)
746 })
747 .collect::<Result<Vec<_>, DynError>>()?
748 .into_iter()
749 .flatten()
750 .collect()
751 } else {
752 (0..cc.get_files().len())
753 .into_par_iter()
754 .map(|index| {
755 let unit = cc.compile_unit(index);
756 build_unit_graph::<L>(unit, index, GraphBuildConfig)
757 })
758 .collect::<Result<Vec<_>, DynError>>()?
759 .into_iter()
760 .flatten()
761 .collect()
762 };
763
764 Ok(unit_graphs)
767}