|  | use rustc_data_structures::fx::{FxIndexMap, FxIndexSet, IndexEntry}; | 
|  | use rustc_middle::mir::coverage::BasicCoverageBlock; | 
|  | use rustc_span::{ExpnId, ExpnKind, Span}; | 
|  |  | 
|  | #[derive(Clone, Copy, Debug)] | 
|  | pub(crate) struct SpanWithBcb { | 
|  | pub(crate) span: Span, | 
|  | pub(crate) bcb: BasicCoverageBlock, | 
|  | } | 
|  |  | 
|  | #[derive(Debug)] | 
|  | pub(crate) struct ExpnTree { | 
|  | nodes: FxIndexMap<ExpnId, ExpnNode>, | 
|  | } | 
|  |  | 
|  | impl ExpnTree { | 
|  | pub(crate) fn get(&self, expn_id: ExpnId) -> Option<&ExpnNode> { | 
|  | self.nodes.get(&expn_id) | 
|  | } | 
|  |  | 
|  | /// Yields the tree node for the given expansion ID (if present), followed | 
|  | /// by the nodes of all of its descendants in depth-first order. | 
|  | pub(crate) fn iter_node_and_descendants( | 
|  | &self, | 
|  | root_expn_id: ExpnId, | 
|  | ) -> impl Iterator<Item = &ExpnNode> { | 
|  | gen move { | 
|  | let Some(root_node) = self.get(root_expn_id) else { return }; | 
|  | yield root_node; | 
|  |  | 
|  | // Stack of child-node-ID iterators that drives the depth-first traversal. | 
|  | let mut iter_stack = vec![root_node.child_expn_ids.iter()]; | 
|  |  | 
|  | while let Some(curr_iter) = iter_stack.last_mut() { | 
|  | // Pull the next ID from the top of the stack. | 
|  | let Some(&curr_id) = curr_iter.next() else { | 
|  | iter_stack.pop(); | 
|  | continue; | 
|  | }; | 
|  |  | 
|  | // Yield this node. | 
|  | let Some(node) = self.get(curr_id) else { continue }; | 
|  | yield node; | 
|  |  | 
|  | // Push the node's children, to be traversed next. | 
|  | if !node.child_expn_ids.is_empty() { | 
|  | iter_stack.push(node.child_expn_ids.iter()); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | #[derive(Debug)] | 
|  | pub(crate) struct ExpnNode { | 
|  | /// Storing the expansion ID in its own node is not strictly necessary, | 
|  | /// but is helpful for debugging and might be useful later. | 
|  | #[expect(dead_code)] | 
|  | pub(crate) expn_id: ExpnId, | 
|  |  | 
|  | // Useful info extracted from `ExpnData`. | 
|  | pub(crate) expn_kind: ExpnKind, | 
|  | /// Non-dummy `ExpnData::call_site` span. | 
|  | pub(crate) call_site: Option<Span>, | 
|  | /// Expansion ID of `call_site`, if present. | 
|  | /// This links an expansion node to its parent in the tree. | 
|  | pub(crate) call_site_expn_id: Option<ExpnId>, | 
|  |  | 
|  | /// Spans (and their associated BCBs) belonging to this expansion. | 
|  | pub(crate) spans: Vec<SpanWithBcb>, | 
|  | /// Expansions whose call-site is in this expansion. | 
|  | pub(crate) child_expn_ids: FxIndexSet<ExpnId>, | 
|  | } | 
|  |  | 
|  | impl ExpnNode { | 
|  | fn new(expn_id: ExpnId) -> Self { | 
|  | let expn_data = expn_id.expn_data(); | 
|  |  | 
|  | let call_site = Some(expn_data.call_site).filter(|sp| !sp.is_dummy()); | 
|  | let call_site_expn_id = try { call_site?.ctxt().outer_expn() }; | 
|  |  | 
|  | Self { | 
|  | expn_id, | 
|  |  | 
|  | expn_kind: expn_data.kind, | 
|  | call_site, | 
|  | call_site_expn_id, | 
|  |  | 
|  | spans: vec![], | 
|  | child_expn_ids: FxIndexSet::default(), | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | /// Given a collection of span/BCB pairs from potentially-different syntax contexts, | 
|  | /// arranges them into an "expansion tree" based on their expansion call-sites. | 
|  | pub(crate) fn build_expn_tree(spans: impl IntoIterator<Item = SpanWithBcb>) -> ExpnTree { | 
|  | let mut nodes = FxIndexMap::default(); | 
|  | let new_node = |&expn_id: &ExpnId| ExpnNode::new(expn_id); | 
|  |  | 
|  | for span_with_bcb in spans { | 
|  | // Create a node for this span's enclosing expansion, and add the span to it. | 
|  | let expn_id = span_with_bcb.span.ctxt().outer_expn(); | 
|  | let node = nodes.entry(expn_id).or_insert_with_key(new_node); | 
|  | node.spans.push(span_with_bcb); | 
|  |  | 
|  | // Now walk up the expansion call-site chain, creating nodes and registering children. | 
|  | let mut prev = expn_id; | 
|  | let mut curr_expn_id = node.call_site_expn_id; | 
|  | while let Some(expn_id) = curr_expn_id { | 
|  | let entry = nodes.entry(expn_id); | 
|  | let node_existed = matches!(entry, IndexEntry::Occupied(_)); | 
|  |  | 
|  | let node = entry.or_insert_with_key(new_node); | 
|  | node.child_expn_ids.insert(prev); | 
|  |  | 
|  | if node_existed { | 
|  | break; | 
|  | } | 
|  |  | 
|  | prev = expn_id; | 
|  | curr_expn_id = node.call_site_expn_id; | 
|  | } | 
|  | } | 
|  |  | 
|  | ExpnTree { nodes } | 
|  | } |