@@ -4,9 +4,11 @@ use rustc_middle::mir::{
4
4
self , AggregateKind , FakeReadCause , Rvalue , Statement , StatementKind , Terminator ,
5
5
TerminatorKind ,
6
6
} ;
7
- use rustc_span:: Span ;
7
+ use rustc_span:: { ExpnKind , MacroKind , Span , Symbol } ;
8
8
9
- use crate :: coverage:: graph:: { BasicCoverageBlock , BasicCoverageBlockData , CoverageGraph } ;
9
+ use crate :: coverage:: graph:: {
10
+ BasicCoverageBlock , BasicCoverageBlockData , CoverageGraph , START_BCB ,
11
+ } ;
10
12
use crate :: coverage:: spans:: CoverageSpan ;
11
13
use crate :: coverage:: ExtractedHirInfo ;
12
14
@@ -17,7 +19,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
17
19
) -> Vec < CoverageSpan > {
18
20
let & ExtractedHirInfo { is_async_fn, fn_sig_span, body_span, .. } = hir_info;
19
21
20
- let mut initial_spans = vec ! [ CoverageSpan :: for_fn_sig( fn_sig_span) ] ;
22
+ let mut initial_spans = vec ! [ SpanFromMir :: for_fn_sig( fn_sig_span) ] ;
21
23
22
24
if is_async_fn {
23
25
// An async function desugars into a function that returns a future,
@@ -57,7 +59,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
57
59
. then_with ( || Ord :: cmp ( & a. is_closure , & b. is_closure ) . reverse ( ) )
58
60
} ) ;
59
61
60
- initial_spans
62
+ initial_spans. into_iter ( ) . map ( SpanFromMir :: into_coverage_span ) . collect :: < Vec < _ > > ( )
61
63
}
62
64
63
65
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
@@ -67,7 +69,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
67
69
///
68
70
/// (The input spans should be sorted in BCB dominator order, so that the
69
71
/// retained "first" span is likely to dominate the others.)
70
- fn remove_unwanted_macro_spans ( initial_spans : & mut Vec < CoverageSpan > ) {
72
+ fn remove_unwanted_macro_spans ( initial_spans : & mut Vec < SpanFromMir > ) {
71
73
let mut seen_spans = FxHashSet :: default ( ) ;
72
74
initial_spans. retain ( |covspan| {
73
75
// Ignore (retain) closure spans and non-macro-expansion spans.
@@ -84,7 +86,7 @@ fn remove_unwanted_macro_spans(initial_spans: &mut Vec<CoverageSpan>) {
84
86
/// function body, split it into two parts. The first part covers just the
85
87
/// macro name plus `!`, and the second part covers the rest of the macro
86
88
/// invocation. This seems to give better results for code that uses macros.
87
- fn split_visible_macro_spans ( initial_spans : & mut Vec < CoverageSpan > , hir_info : & ExtractedHirInfo ) {
89
+ fn split_visible_macro_spans ( initial_spans : & mut Vec < SpanFromMir > , hir_info : & ExtractedHirInfo ) {
88
90
let mut extra_spans = vec ! [ ] ;
89
91
90
92
initial_spans. retain ( |covspan| {
@@ -105,8 +107,8 @@ fn split_visible_macro_spans(initial_spans: &mut Vec<CoverageSpan>, hir_info: &E
105
107
}
106
108
107
109
assert ! ( !covspan. is_closure) ;
108
- extra_spans. push ( CoverageSpan :: new ( before, covspan. expn_span , covspan. bcb , false ) ) ;
109
- extra_spans. push ( CoverageSpan :: new ( after, covspan. expn_span , covspan. bcb , false ) ) ;
110
+ extra_spans. push ( SpanFromMir :: new ( before, covspan. expn_span , covspan. bcb , false ) ) ;
111
+ extra_spans. push ( SpanFromMir :: new ( after, covspan. expn_span , covspan. bcb , false ) ) ;
110
112
false // Discard the original covspan that we just split.
111
113
} ) ;
112
114
@@ -125,22 +127,22 @@ fn bcb_to_initial_coverage_spans<'a, 'tcx>(
125
127
body_span : Span ,
126
128
bcb : BasicCoverageBlock ,
127
129
bcb_data : & ' a BasicCoverageBlockData ,
128
- ) -> impl Iterator < Item = CoverageSpan > + Captures < ' a > + Captures < ' tcx > {
130
+ ) -> impl Iterator < Item = SpanFromMir > + Captures < ' a > + Captures < ' tcx > {
129
131
bcb_data. basic_blocks . iter ( ) . flat_map ( move |& bb| {
130
132
let data = & mir_body[ bb] ;
131
133
132
134
let statement_spans = data. statements . iter ( ) . filter_map ( move |statement| {
133
135
let expn_span = filtered_statement_span ( statement) ?;
134
136
let span = unexpand_into_body_span ( expn_span, body_span) ?;
135
137
136
- Some ( CoverageSpan :: new ( span, expn_span, bcb, is_closure ( statement) ) )
138
+ Some ( SpanFromMir :: new ( span, expn_span, bcb, is_closure ( statement) ) )
137
139
} ) ;
138
140
139
141
let terminator_span = Some ( data. terminator ( ) ) . into_iter ( ) . filter_map ( move |terminator| {
140
142
let expn_span = filtered_terminator_span ( terminator) ?;
141
143
let span = unexpand_into_body_span ( expn_span, body_span) ?;
142
144
143
- Some ( CoverageSpan :: new ( span, expn_span, bcb, false ) )
145
+ Some ( SpanFromMir :: new ( span, expn_span, bcb, false ) )
144
146
} ) ;
145
147
146
148
statement_spans. chain ( terminator_span)
@@ -270,3 +272,66 @@ fn unexpand_into_body_span(span: Span, body_span: Span) -> Option<Span> {
270
272
let original_span = original_sp ( span, body_span) . with_ctxt ( body_span. ctxt ( ) ) ;
271
273
body_span. contains ( original_span) . then_some ( original_span)
272
274
}
275
+
276
+ #[ derive( Debug ) ]
277
+ struct SpanFromMir {
278
+ /// A copy of `expn_span` that has been "un-expanded" back to the current
279
+ /// function's `body_span`. After various intermediate processing steps,
280
+ /// this span is emitted as part of the final coverage mappings.
281
+ ///
282
+ /// With the exception of `fn_sig_span`, this should always be contained
283
+ /// within `body_span`.
284
+ span : Span ,
285
+ /// The actual span that was extracted from MIR, used to look up information
286
+ /// about macro expansions.
287
+ expn_span : Span ,
288
+ current_macro_or_none : std:: cell:: OnceCell < Option < Symbol > > ,
289
+ bcb : BasicCoverageBlock ,
290
+ is_closure : bool ,
291
+ }
292
+
293
+ impl SpanFromMir {
294
+ fn for_fn_sig ( fn_sig_span : Span ) -> Self {
295
+ Self :: new ( fn_sig_span, fn_sig_span, START_BCB , false )
296
+ }
297
+
298
+ fn new ( span : Span , expn_span : Span , bcb : BasicCoverageBlock , is_closure : bool ) -> Self {
299
+ Self { span, expn_span, current_macro_or_none : Default :: default ( ) , bcb, is_closure }
300
+ }
301
+
302
+ /// If the span is part of a macro, returns the macro name symbol.
303
+ fn current_macro ( & self ) -> Option < Symbol > {
304
+ self . current_macro_or_none
305
+ . get_or_init ( || {
306
+ if let ExpnKind :: Macro ( MacroKind :: Bang , current_macro) =
307
+ self . expn_span . ctxt ( ) . outer_expn_data ( ) . kind
308
+ {
309
+ return Some ( current_macro) ;
310
+ }
311
+ None
312
+ } )
313
+ . map ( |symbol| symbol)
314
+ }
315
+
316
+ /// If the span is part of a macro, and the macro is visible (expands directly to the given
317
+ /// body_span), returns the macro name symbol.
318
+ fn visible_macro ( & self , body_span : Span ) -> Option < Symbol > {
319
+ let current_macro = self . current_macro ( ) ?;
320
+ let parent_callsite = self . expn_span . parent_callsite ( ) ?;
321
+
322
+ // In addition to matching the context of the body span, the parent callsite
323
+ // must also be the source callsite, i.e. the parent must have no parent.
324
+ let is_visible_macro =
325
+ parent_callsite. parent_callsite ( ) . is_none ( ) && parent_callsite. eq_ctxt ( body_span) ;
326
+ is_visible_macro. then_some ( current_macro)
327
+ }
328
+
329
+ fn is_macro_expansion ( & self ) -> bool {
330
+ self . current_macro ( ) . is_some ( )
331
+ }
332
+
333
+ fn into_coverage_span ( self ) -> CoverageSpan {
334
+ let Self { span, expn_span : _, current_macro_or_none : _, bcb, is_closure } = self ;
335
+ CoverageSpan :: new ( span, bcb, is_closure)
336
+ }
337
+ }
0 commit comments