1- use  rustc_data_structures:: fx:: FxHashSet ; 
21use  rustc_middle:: mir; 
32use  rustc_middle:: mir:: coverage:: { Mapping ,  MappingKind ,  START_BCB } ; 
43use  rustc_middle:: ty:: TyCtxt ; 
54use  rustc_span:: source_map:: SourceMap ; 
6- use  rustc_span:: { BytePos ,  DesugaringKind ,  ExpnKind ,  MacroKind ,  Span } ; 
5+ use  rustc_span:: { BytePos ,  DesugaringKind ,  ExpnId ,   ExpnKind ,  MacroKind ,  Span } ; 
76use  tracing:: instrument; 
87
8+ use  crate :: coverage:: expansion:: { self ,  ExpnTree ,  SpanWithBcb } ; 
99use  crate :: coverage:: graph:: { BasicCoverageBlock ,  CoverageGraph } ; 
1010use  crate :: coverage:: hir_info:: ExtractedHirInfo ; 
11- use  crate :: coverage:: spans:: from_mir:: { Hole ,  RawSpanFromMir ,  SpanFromMir } ; 
12- use  crate :: coverage:: unexpand; 
11+ use  crate :: coverage:: spans:: from_mir:: { Hole ,  RawSpanFromMir } ; 
1312
1413mod  from_mir; 
1514
@@ -34,19 +33,51 @@ pub(super) fn extract_refined_covspans<'tcx>(
3433    let  & ExtractedHirInfo  {  body_span,  .. }  = hir_info; 
3534
3635    let  raw_spans = from_mir:: extract_raw_spans_from_mir ( mir_body,  graph) ; 
37-     let  mut  covspans = raw_spans
38-         . into_iter ( ) 
39-         . filter_map ( |RawSpanFromMir  {  raw_span,  bcb } | try { 
40-             let  ( span,  expn_kind)  =
41-                 unexpand:: unexpand_into_body_span_with_expn_kind ( raw_span,  body_span) ?; 
42-             // Discard any spans that fill the entire body, because they tend 
43-             // to represent compiler-inserted code, e.g. implicitly returning `()`. 
44-             if  span. source_equal ( body_span)  { 
45-                 return  None ; 
46-             } ; 
47-             SpanFromMir  {  span,  expn_kind,  bcb } 
48-         } ) 
49-         . collect :: < Vec < _ > > ( ) ; 
36+     // Use the raw spans to build a tree of expansions for this function. 
37+     let  expn_tree = expansion:: build_expn_tree ( 
38+         raw_spans
39+             . into_iter ( ) 
40+             . map ( |RawSpanFromMir  {  raw_span,  bcb } | SpanWithBcb  {  span :  raw_span,  bcb } ) , 
41+     ) ; 
42+ 
43+     let  mut  covspans = vec ! [ ] ; 
44+     let  mut  push_covspan = |covspan :  Covspan | { 
45+         let  covspan_span = covspan. span ; 
46+         // Discard any spans not contained within the function body span. 
47+         // Also discard any spans that fill the entire body, because they tend 
48+         // to represent compiler-inserted code, e.g. implicitly returning `()`. 
49+         if  !body_span. contains ( covspan_span)  || body_span. source_equal ( covspan_span)  { 
50+             return ; 
51+         } 
52+ 
53+         // Each pushed covspan should have the same context as the body span. 
54+         // If it somehow doesn't, discard the covspan, or panic in debug builds. 
55+         if  !body_span. eq_ctxt ( covspan_span)  { 
56+             debug_assert ! ( 
57+                 false , 
58+                 "span context mismatch: body_span={body_span:?}, covspan.span={covspan_span:?}" 
59+             ) ; 
60+             return ; 
61+         } 
62+ 
63+         covspans. push ( covspan) ; 
64+     } ; 
65+ 
66+     if  let  Some ( node)  = expn_tree. get ( body_span. ctxt ( ) . outer_expn ( ) )  { 
67+         for  & SpanWithBcb  {  span,  bcb }  in  & node. spans  { 
68+             push_covspan ( Covspan  {  span,  bcb } ) ; 
69+         } 
70+ 
71+         // For each expansion with its call-site in the body span, try to 
72+         // distill a corresponding covspan. 
73+         for  & child_expn_id in  & node. child_expn_ids  { 
74+             if  let  Some ( covspan)  =
75+                 single_covspan_for_child_expn ( tcx,  graph,  & expn_tree,  child_expn_id) 
76+             { 
77+                 push_covspan ( covspan) ; 
78+             } 
79+         } 
80+     } 
5081
5182    // Only proceed if we found at least one usable span. 
5283    if  covspans. is_empty ( )  { 
@@ -57,17 +88,10 @@ pub(super) fn extract_refined_covspans<'tcx>(
5788    // Otherwise, add a fake span at the start of the body, to avoid an ugly 
5889    // gap between the start of the body and the first real span. 
5990    // FIXME: Find a more principled way to solve this problem. 
60-     covspans. push ( SpanFromMir :: for_fn_sig ( 
61-         hir_info. fn_sig_span . unwrap_or_else ( || body_span. shrink_to_lo ( ) ) , 
62-     ) ) ; 
63- 
64-     // First, perform the passes that need macro information. 
65-     covspans. sort_by ( |a,  b| graph. cmp_in_dominator_order ( a. bcb ,  b. bcb ) ) ; 
66-     remove_unwanted_expansion_spans ( & mut  covspans) ; 
67-     shrink_visible_macro_spans ( tcx,  & mut  covspans) ; 
68- 
69-     // We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`. 
70-     let  mut  covspans = covspans. into_iter ( ) . map ( SpanFromMir :: into_covspan) . collect :: < Vec < _ > > ( ) ; 
91+     covspans. push ( Covspan  { 
92+         span :  hir_info. fn_sig_span . unwrap_or_else ( || body_span. shrink_to_lo ( ) ) , 
93+         bcb :  START_BCB , 
94+     } ) ; 
7195
7296    let  compare_covspans = |a :  & Covspan ,  b :  & Covspan | { 
7397        compare_spans ( a. span ,  b. span ) 
@@ -117,43 +141,37 @@ pub(super) fn extract_refined_covspans<'tcx>(
117141    } ) ) ; 
118142} 
119143
120- /// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate 
121- /// multiple condition/consequent blocks that have the span of the whole macro 
122- /// invocation, which is unhelpful. Keeping only the first such span seems to 
123- /// give better mappings, so remove the others. 
124- /// 
125- /// Similarly, `await` expands to a branch on the discriminant of `Poll`, which 
126- /// leads to incorrect coverage if the `Future` is immediately ready (#98712). 
127- /// 
128- /// (The input spans should be sorted in BCB dominator order, so that the 
129- /// retained "first" span is likely to dominate the others.) 
130- fn  remove_unwanted_expansion_spans ( covspans :  & mut  Vec < SpanFromMir > )  { 
131-     let  mut  deduplicated_spans = FxHashSet :: default ( ) ; 
132- 
133-     covspans. retain ( |covspan| { 
134-         match  covspan. expn_kind  { 
135-             // Retain only the first await-related or macro-expanded covspan with this span. 
136-             Some ( ExpnKind :: Desugaring ( DesugaringKind :: Await ) )  => { 
137-                 deduplicated_spans. insert ( covspan. span ) 
138-             } 
139-             Some ( ExpnKind :: Macro ( MacroKind :: Bang ,  _) )  => deduplicated_spans. insert ( covspan. span ) , 
140-             // Ignore (retain) other spans. 
141-             _ => true , 
144+ /// For a single child expansion, try to distill it into a single span+BCB mapping. 
145+ fn  single_covspan_for_child_expn ( 
146+     tcx :  TyCtxt < ' _ > , 
147+     graph :  & CoverageGraph , 
148+     expn_tree :  & ExpnTree , 
149+     expn_id :  ExpnId , 
150+ )  -> Option < Covspan >  { 
151+     let  node = expn_tree. get ( expn_id) ?; 
152+ 
153+     let  bcbs =
154+         expn_tree. iter_node_and_descendants ( expn_id) . flat_map ( |n| n. spans . iter ( ) . map ( |s| s. bcb ) ) ; 
155+ 
156+     let  bcb = match  node. expn_kind  { 
157+         // For bang-macros (e.g. `assert!`, `trace!`) and for `await`, taking 
158+         // the "first" BCB in dominator order seems to give good results. 
159+         ExpnKind :: Macro ( MacroKind :: Bang ,  _)  | ExpnKind :: Desugaring ( DesugaringKind :: Await )  => { 
160+             bcbs. min_by ( |& a,  & b| graph. cmp_in_dominator_order ( a,  b) ) ?
142161        } 
143-     } ) ; 
144- } 
145- 
146- /// When a span corresponds to a macro invocation that is visible from the 
147- /// function body, truncate it to just the macro name plus `!`. 
148- /// This seems to give better results for code that uses macros. 
149- fn  shrink_visible_macro_spans ( tcx :  TyCtxt < ' _ > ,  covspans :  & mut  Vec < SpanFromMir > )  { 
150-     let  source_map = tcx. sess . source_map ( ) ; 
162+         // For other kinds of expansion, taking the "last" (most-dominated) BCB 
163+         // seems to give good results. 
164+         _ => bcbs. max_by ( |& a,  & b| graph. cmp_in_dominator_order ( a,  b) ) ?, 
165+     } ; 
151166
152-     for  covspan in  covspans { 
153-         if  matches ! ( covspan. expn_kind,  Some ( ExpnKind :: Macro ( MacroKind :: Bang ,  _) ) )  { 
154-             covspan. span  = source_map. span_through_char ( covspan. span ,  '!' ) ; 
155-         } 
167+     // For bang-macro expansions, limit the call-site span to just the macro 
168+     // name plus `!`, excluding the macro arguments. 
169+     let  mut  span = node. call_site ?; 
170+     if  matches ! ( node. expn_kind,  ExpnKind :: Macro ( MacroKind :: Bang ,  _) )  { 
171+         span = tcx. sess . source_map ( ) . span_through_char ( span,  '!' ) ; 
156172    } 
173+ 
174+     Some ( Covspan  {  span,  bcb } ) 
157175} 
158176
159177/// Discard all covspans that overlap a hole. 
0 commit comments