@@ -6,6 +6,7 @@ use rustc_span::{ExpnId, ExpnKind, Span};
66use crate :: coverage:: from_mir;
77use crate :: coverage:: graph:: CoverageGraph ;
88use crate :: coverage:: hir_info:: ExtractedHirInfo ;
9+ use crate :: coverage:: mappings:: MappingsError ;
910
1011#[ derive( Clone , Copy , Debug ) ]
1112pub ( crate ) struct SpanWithBcb {
@@ -62,6 +63,8 @@ pub(crate) struct ExpnNode {
6263 /// but is helpful for debugging and might be useful later.
6364 #[ expect( dead_code) ]
6465 pub ( crate ) expn_id : ExpnId ,
66+ /// Index of this node in a depth-first traversal from the root.
67+ pub ( crate ) dfs_rank : usize ,
6568
6669 // Useful info extracted from `ExpnData`.
6770 pub ( crate ) expn_kind : ExpnKind ,
@@ -100,6 +103,7 @@ impl ExpnNode {
100103
101104 Self {
102105 expn_id,
106+ dfs_rank : usize:: MAX ,
103107
104108 expn_kind : expn_data. kind ,
105109 call_site,
@@ -124,7 +128,7 @@ pub(crate) fn build_expn_tree(
124128 mir_body : & mir:: Body < ' _ > ,
125129 hir_info : & ExtractedHirInfo ,
126130 graph : & CoverageGraph ,
127- ) -> ExpnTree {
131+ ) -> Result < ExpnTree , MappingsError > {
128132 let raw_spans = from_mir:: extract_raw_spans_from_mir ( mir_body, graph) ;
129133
130134 let mut nodes = FxIndexMap :: default ( ) ;
@@ -157,6 +161,9 @@ pub(crate) fn build_expn_tree(
157161 }
158162 }
159163
164+ // Sort the tree nodes into depth-first order.
165+ sort_nodes_depth_first ( & mut nodes) ?;
166+
160167 // If we have a span for the function signature, associate it with the
161168 // corresponding expansion tree node.
162169 if let Some ( fn_sig_span) = hir_info. fn_sig_span
@@ -189,5 +196,32 @@ pub(crate) fn build_expn_tree(
189196 }
190197 }
191198
192- ExpnTree { nodes }
199+ Ok ( ExpnTree { nodes } )
200+ }
201+
202+ /// Sorts the tree nodes in the map into depth-first order.
203+ ///
204+ /// This allows subsequent operations to iterate over all nodes, while assuming
205+ /// that every node occurs before all of its descendants.
206+ fn sort_nodes_depth_first ( nodes : & mut FxIndexMap < ExpnId , ExpnNode > ) -> Result < ( ) , MappingsError > {
207+ let mut dfs_stack = vec ! [ ExpnId :: root( ) ] ;
208+ let mut next_dfs_rank = 0usize ;
209+ while let Some ( expn_id) = dfs_stack. pop ( ) {
210+ if let Some ( node) = nodes. get_mut ( & expn_id) {
211+ node. dfs_rank = next_dfs_rank;
212+ next_dfs_rank += 1 ;
213+ dfs_stack. extend ( node. child_expn_ids . iter ( ) . rev ( ) . copied ( ) ) ;
214+ }
215+ }
216+ nodes. sort_by_key ( |_expn_id, node| node. dfs_rank ) ;
217+
218+ // Verify that the depth-first search visited each node exactly once.
219+ for ( i, & ExpnNode { dfs_rank, .. } ) in nodes. values ( ) . enumerate ( ) {
220+ if dfs_rank != i {
221+ tracing:: debug!( dfs_rank, i, "expansion tree node's rank does not match its index" ) ;
222+ return Err ( MappingsError :: TreeSortFailure ) ;
223+ }
224+ }
225+
226+ Ok ( ( ) )
193227}
0 commit comments