rustc_mir_transform/coverage/
expansion.rs1use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry};
2use rustc_middle::mir::coverage::BasicCoverageBlock;
3use rustc_span::{ExpnId, ExpnKind, Span};
4
5#[derive(Clone, Copy, Debug)]
6pub(crate) struct SpanWithBcb {
7 pub(crate) span: Span,
8 pub(crate) bcb: BasicCoverageBlock,
9}
10
11#[derive(Debug)]
12pub(crate) struct ExpnTree {
13 nodes: FxIndexMap<ExpnId, ExpnNode>,
14}
15
16impl ExpnTree {
17 pub(crate) fn get(&self, expn_id: ExpnId) -> Option<&ExpnNode> {
18 self.nodes.get(&expn_id)
19 }
20
21 pub(crate) fn iter_node_and_descendants(
24 &self,
25 root_expn_id: ExpnId,
26 ) -> impl Iterator<Item = &ExpnNode> {
27 gen move {
28 let Some(root_node) = self.get(root_expn_id) else { return };
29 yield root_node;
30
31 let mut iter_stack = vec![root_node.child_expn_ids.iter()];
33
34 while let Some(curr_iter) = iter_stack.last_mut() {
35 let Some(&curr_id) = curr_iter.next() else {
37 iter_stack.pop();
38 continue;
39 };
40
41 let Some(node) = self.get(curr_id) else { continue };
43 yield node;
44
45 if !node.child_expn_ids.is_empty() {
47 iter_stack.push(node.child_expn_ids.iter());
48 }
49 }
50 }
51 }
52}
53
54#[derive(Debug)]
55pub(crate) struct ExpnNode {
56 #[expect(dead_code)]
59 pub(crate) expn_id: ExpnId,
60
61 pub(crate) expn_kind: ExpnKind,
63 pub(crate) call_site: Option<Span>,
65 pub(crate) call_site_expn_id: Option<ExpnId>,
68
69 pub(crate) spans: Vec<SpanWithBcb>,
71 pub(crate) child_expn_ids: FxIndexSet<ExpnId>,
73}
74
75impl ExpnNode {
76 fn new(expn_id: ExpnId) -> Self {
77 let expn_data = expn_id.expn_data();
78
79 let call_site = Some(expn_data.call_site).filter(|sp| !sp.is_dummy());
80 let call_site_expn_id = try { call_site?.ctxt().outer_expn() };
81
82 Self {
83 expn_id,
84
85 expn_kind: expn_data.kind,
86 call_site,
87 call_site_expn_id,
88
89 spans: vec![],
90 child_expn_ids: FxIndexSet::default(),
91 }
92 }
93}
94
95pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> ExpnTree {
98 let mut nodes = FxIndexMap::default();
99 let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id);
100
101 for span_with_bcb in spans {
102 let expn_id = span_with_bcb.span.ctxt().outer_expn();
104 let node = nodes.entry(expn_id).or_insert_with_key(new_node);
105 node.spans.push(span_with_bcb);
106
107 let mut prev = expn_id;
109 let mut curr_expn_id = node.call_site_expn_id;
110 while let Some(expn_id) = curr_expn_id {
111 let entry = nodes.entry(expn_id);
112 let node_existed = matches!(entry, IndexEntry::Occupied(_));
113
114 let node = entry.or_insert_with_key(new_node);
115 node.child_expn_ids.insert(prev);
116
117 if node_existed {
118 break;
119 }
120
121 prev = expn_id;
122 curr_expn_id = node.call_site_expn_id;
123 }
124 }
125
126 ExpnTree { nodes }
127}