Skip to content

Commit f12795f

Browse files
committed
More improvements to macro coverage
1 parent c26afb7 commit f12795f

File tree

7 files changed

+366
-155
lines changed

7 files changed

+366
-155
lines changed

compiler/rustc_mir/src/transform/coverage/spans.rs

+110-49
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::mir::{
1111
use rustc_middle::ty::TyCtxt;
1212

1313
use rustc_span::source_map::original_sp;
14-
use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
14+
use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
1515

1616
use std::cmp::Ordering;
1717

@@ -67,20 +67,17 @@ impl CoverageStatement {
6767
#[derive(Debug, Clone)]
6868
pub(super) struct CoverageSpan {
6969
pub span: Span,
70-
pub is_macro_expansion: bool,
70+
pub expn_span: Span,
7171
pub bcb: BasicCoverageBlock,
7272
pub coverage_statements: Vec<CoverageStatement>,
7373
pub is_closure: bool,
7474
}
7575

7676
impl CoverageSpan {
7777
pub fn for_fn_sig(fn_sig_span: Span) -> Self {
78-
// Whether the function signature is from a macro or not, it should not be treated like
79-
// macro-expanded statements and terminators.
80-
let is_macro_expansion = false;
8178
Self {
8279
span: fn_sig_span,
83-
is_macro_expansion,
80+
expn_span: fn_sig_span,
8481
bcb: START_BCB,
8582
coverage_statements: vec![],
8683
is_closure: false,
@@ -90,7 +87,7 @@ impl CoverageSpan {
9087
pub fn for_statement(
9188
statement: &Statement<'tcx>,
9289
span: Span,
93-
is_macro_expansion: bool,
90+
expn_span: Span,
9491
bcb: BasicCoverageBlock,
9592
bb: BasicBlock,
9693
stmt_index: usize,
@@ -105,7 +102,7 @@ impl CoverageSpan {
105102

106103
Self {
107104
span,
108-
is_macro_expansion,
105+
expn_span,
109106
bcb,
110107
coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)],
111108
is_closure,
@@ -114,13 +111,13 @@ impl CoverageSpan {
114111

115112
pub fn for_terminator(
116113
span: Span,
117-
is_macro_expansion: bool,
114+
expn_span: Span,
118115
bcb: BasicCoverageBlock,
119116
bb: BasicBlock,
120117
) -> Self {
121118
Self {
122119
span,
123-
is_macro_expansion,
120+
expn_span,
124121
bcb,
125122
coverage_statements: vec![CoverageStatement::Terminator(bb, span)],
126123
is_closure: false,
@@ -176,6 +173,34 @@ impl CoverageSpan {
176173
.collect::<Vec<_>>()
177174
.join("\n")
178175
}
176+
177+
/// If the span is part of a macro, and the macro is visible (expands directly to the given
178+
/// body_span), returns the macro name symbol.
179+
pub fn current_macro(&self) -> Option<Symbol> {
180+
if let ExpnKind::Macro(MacroKind::Bang, current_macro) =
181+
self.expn_span.ctxt().outer_expn_data().kind
182+
{
183+
return Some(current_macro);
184+
}
185+
None
186+
}
187+
188+
/// If the span is part of a macro, and the macro is visible (expands directly to the given
189+
/// body_span), returns the macro name symbol.
190+
pub fn visible_macro(&self, body_span: Span) -> Option<Symbol> {
191+
if let Some(current_macro) = self.current_macro() {
192+
if self.expn_span.parent().unwrap_or_else(|| bug!("macro must have a parent")).ctxt()
193+
== body_span.ctxt()
194+
{
195+
return Some(current_macro);
196+
}
197+
}
198+
None
199+
}
200+
201+
pub fn is_macro_expansion(&self) -> bool {
202+
self.current_macro().is_some()
203+
}
179204
}
180205

181206
/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a
@@ -219,6 +244,9 @@ pub struct CoverageSpans<'a, 'tcx> {
219244
/// Assigned from `curr_original_span` from the previous iteration.
220245
prev_original_span: Span,
221246

247+
/// A copy of the expn_span from the prior iteration.
248+
prev_expn_span: Option<Span>,
249+
222250
/// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and
223251
/// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list.
224252
/// If a new `curr` span also fits this criteria (compared to an existing list of
@@ -273,15 +301,13 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
273301
curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
274302
some_prev: None,
275303
prev_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
304+
prev_expn_span: None,
276305
pending_dups: Vec::new(),
277306
};
278307

279308
let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans();
280309

281310
coverage_spans.sorted_spans_iter = Some(sorted_spans.into_iter());
282-
coverage_spans.some_prev = coverage_spans.sorted_spans_iter.as_mut().unwrap().next();
283-
coverage_spans.prev_original_span =
284-
coverage_spans.some_prev.as_ref().expect("at least one span").span;
285311

286312
coverage_spans.to_refined_spans()
287313
}
@@ -335,10 +361,14 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
335361
/// de-duplicated `CoverageSpan`s.
336362
fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
337363
while self.next_coverage_span() {
338-
if self.curr().is_mergeable(self.prev()) {
364+
if self.some_prev.is_none() {
365+
debug!(" initial span");
366+
self.check_invoked_macro_name_span();
367+
} else if self.curr().is_mergeable(self.prev()) {
339368
debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev());
340369
let prev = self.take_prev();
341370
self.curr_mut().merge_from(prev);
371+
self.check_invoked_macro_name_span();
342372
// Note that curr.span may now differ from curr_original_span
343373
} else if self.prev_ends_before_curr() {
344374
debug!(
@@ -347,7 +377,8 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
347377
self.prev()
348378
);
349379
let prev = self.take_prev();
350-
self.refined_spans.push(prev);
380+
self.push_refined_span(prev);
381+
self.check_invoked_macro_name_span();
351382
} else if self.prev().is_closure {
352383
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
353384
// next iter
@@ -362,7 +393,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
362393
} else if self.prev_original_span == self.curr().span {
363394
// Note that this compares the new span to `prev_original_span`, which may not
364395
// be the full `prev.span` (if merged during the previous iteration).
365-
if self.prev().is_macro_expansion && self.curr().is_macro_expansion {
396+
if self.prev().is_macro_expansion() && self.curr().is_macro_expansion() {
366397
// Macros that expand to include branching (such as
367398
// `assert_eq!()`, `assert_ne!()`, `info!()`, `debug!()`, or
368399
// `trace!()) typically generate callee spans with identical
@@ -385,15 +416,16 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
385416
}
386417
} else {
387418
self.cutoff_prev_at_overlapping_curr();
419+
self.check_invoked_macro_name_span();
388420
}
389421
}
390422

391423
debug!(" AT END, adding last prev={:?}", self.prev());
392424
let prev = self.take_prev();
393-
let CoverageSpans { pending_dups, mut refined_spans, .. } = self;
425+
let pending_dups = self.pending_dups.split_off(0);
394426
for dup in pending_dups {
395427
debug!(" ...adding at least one pending dup={:?}", dup);
396-
refined_spans.push(dup);
428+
self.push_refined_span(dup);
397429
}
398430

399431
// Async functions wrap a closure that implements the body to be executed. The enclosing
@@ -403,21 +435,60 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
403435
// excluded. The closure's `Return` is the only one that will be counted. This provides
404436
// adequate coverage, and more intuitive counts. (Avoids double-counting the closing brace
405437
// of the function body.)
406-
let body_ends_with_closure = if let Some(last_covspan) = refined_spans.last() {
438+
let body_ends_with_closure = if let Some(last_covspan) = self.refined_spans.last() {
407439
last_covspan.is_closure && last_covspan.span.hi() == self.body_span.hi()
408440
} else {
409441
false
410442
};
411443

412444
if !body_ends_with_closure {
413-
refined_spans.push(prev);
445+
self.push_refined_span(prev);
414446
}
415447

416448
// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage
417449
// regions for the current function leave room for the closure's own coverage regions
418450
// (injected separately, from the closure's own MIR).
419-
refined_spans.retain(|covspan| !covspan.is_closure);
420-
refined_spans
451+
self.refined_spans.retain(|covspan| !covspan.is_closure);
452+
self.refined_spans
453+
}
454+
455+
fn push_refined_span(&mut self, covspan: CoverageSpan) {
456+
let len = self.refined_spans.len();
457+
if len > 0 {
458+
let last = &mut self.refined_spans[len - 1];
459+
if last.is_mergeable(&covspan) {
460+
debug!(
461+
"merging new refined span with last refined span, last={:?}, covspan={:?}",
462+
last, covspan
463+
);
464+
last.merge_from(covspan);
465+
return;
466+
}
467+
}
468+
self.refined_spans.push(covspan)
469+
}
470+
471+
fn check_invoked_macro_name_span(&mut self) {
472+
if let Some(visible_macro) = self.curr().visible_macro(self.body_span) {
473+
if self.prev_expn_span.map_or(true, |prev_expn_span| {
474+
self.curr().expn_span.ctxt() != prev_expn_span.ctxt()
475+
}) {
476+
let merged_prefix_len = self.curr_original_span.lo() - self.curr().span.lo();
477+
let after_macro_bang = merged_prefix_len
478+
+ BytePos(visible_macro.to_string().bytes().count() as u32 + 1);
479+
let mut macro_name_cov = self.curr().clone();
480+
self.curr_mut().span =
481+
self.curr().span.with_lo(self.curr().span.lo() + after_macro_bang);
482+
macro_name_cov.span =
483+
macro_name_cov.span.with_hi(macro_name_cov.span.lo() + after_macro_bang);
484+
debug!(
485+
" and curr starts a new macro expansion, so add a new span just for \
486+
the macro `{}!`, new span={:?}",
487+
visible_macro, macro_name_cov
488+
);
489+
self.push_refined_span(macro_name_cov);
490+
}
491+
}
421492
}
422493

423494
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
@@ -440,22 +511,15 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
440511
.enumerate()
441512
.filter_map(move |(index, statement)| {
442513
filtered_statement_span(statement, self.body_span).map(
443-
|(span, is_macro_expansion)| {
514+
|(span, expn_span)| {
444515
CoverageSpan::for_statement(
445-
statement,
446-
span,
447-
is_macro_expansion,
448-
bcb,
449-
bb,
450-
index,
516+
statement, span, expn_span, bcb, bb, index,
451517
)
452518
},
453519
)
454520
})
455521
.chain(filtered_terminator_span(data.terminator(), self.body_span).map(
456-
|(span, is_macro_expansion)| {
457-
CoverageSpan::for_terminator(span, is_macro_expansion, bcb, bb)
458-
},
522+
|(span, expn_span)| CoverageSpan::for_terminator(span, expn_span, bcb, bb),
459523
))
460524
})
461525
.collect()
@@ -509,7 +573,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
509573
let pending_dups = self.pending_dups.split_off(0);
510574
for dup in pending_dups.into_iter() {
511575
debug!(" ...adding at least one pending={:?}", dup);
512-
self.refined_spans.push(dup);
576+
self.push_refined_span(dup);
513577
}
514578
} else {
515579
self.pending_dups.clear();
@@ -521,12 +585,13 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
521585
/// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order.
522586
fn next_coverage_span(&mut self) -> bool {
523587
if let Some(curr) = self.some_curr.take() {
588+
self.prev_expn_span = Some(curr.expn_span);
524589
self.some_prev = Some(curr);
525590
self.prev_original_span = self.curr_original_span;
526591
}
527592
while let Some(curr) = self.sorted_spans_iter.as_mut().unwrap().next() {
528593
debug!("FOR curr={:?}", curr);
529-
if self.prev_starts_after_next(&curr) {
594+
if self.some_prev.is_some() && self.prev_starts_after_next(&curr) {
530595
debug!(
531596
" prev.span starts after curr.span, so curr will be dropped (skipping past \
532597
closure?); prev={:?}",
@@ -583,10 +648,10 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
583648
for mut dup in pending_dups.iter().cloned() {
584649
dup.span = dup.span.with_hi(left_cutoff);
585650
debug!(" ...and at least one pre_closure dup={:?}", dup);
586-
self.refined_spans.push(dup);
651+
self.push_refined_span(dup);
587652
}
588653
}
589-
self.refined_spans.push(pre_closure);
654+
self.push_refined_span(pre_closure);
590655
}
591656
if has_post_closure_span {
592657
// Update prev.span to start after the closure (and discard curr)
@@ -597,7 +662,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
597662
}
598663
self.pending_dups.append(&mut pending_dups);
599664
let closure_covspan = self.take_curr();
600-
self.refined_spans.push(closure_covspan); // since self.prev() was already updated
665+
self.push_refined_span(closure_covspan); // since self.prev() was already updated
601666
} else {
602667
pending_dups.clear();
603668
}
@@ -688,7 +753,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
688753
} else {
689754
debug!(" ... adding modified prev={:?}", self.prev());
690755
let prev = self.take_prev();
691-
self.refined_spans.push(prev);
756+
self.push_refined_span(prev);
692757
}
693758
} else {
694759
// with `pending_dups`, `prev` cannot have any statements that don't overlap
@@ -704,7 +769,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
704769
pub(super) fn filtered_statement_span(
705770
statement: &'a Statement<'tcx>,
706771
body_span: Span,
707-
) -> Option<(Span, bool)> {
772+
) -> Option<(Span, Span)> {
708773
match statement.kind {
709774
// These statements have spans that are often outside the scope of the executed source code
710775
// for their parent `BasicBlock`.
@@ -749,7 +814,7 @@ pub(super) fn filtered_statement_span(
749814
pub(super) fn filtered_terminator_span(
750815
terminator: &'a Terminator<'tcx>,
751816
body_span: Span,
752-
) -> Option<(Span, bool)> {
817+
) -> Option<(Span, Span)> {
753818
match terminator.kind {
754819
// These terminators have spans that don't positively contribute to computing a reasonable
755820
// span of actually executed source code. (For example, SwitchInt terminators extracted from
@@ -789,14 +854,10 @@ pub(super) fn filtered_terminator_span(
789854
}
790855
}
791856

857+
/// Returns the span within the function source body, and the given span, which will be different
858+
/// if the given span is an expansion (macro, syntactic sugar, etc.).
792859
#[inline]
793-
fn function_source_span(span: Span, body_span: Span) -> (Span, bool) {
794-
let is_macro_expansion = span.ctxt() != body_span.ctxt()
795-
&& if let ExpnKind::Macro(MacroKind::Bang, _) = span.ctxt().outer_expn_data().kind {
796-
true
797-
} else {
798-
false
799-
};
800-
let span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
801-
(if body_span.contains(span) { span } else { body_span }, is_macro_expansion)
860+
fn function_source_span(span: Span, body_span: Span) -> (Span, Span) {
861+
let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
862+
(if body_span.contains(original_span) { original_span } else { body_span }, span)
802863
}

compiler/rustc_mir/src/transform/coverage/tests.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -683,12 +683,12 @@ fn test_make_bcb_counters() {
683683
let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
684684
let mut coverage_spans = Vec::new();
685685
for (bcb, data) in basic_coverage_blocks.iter_enumerated() {
686-
if let Some((span, is_macro_expansion)) =
686+
if let Some((span, expn_span)) =
687687
spans::filtered_terminator_span(data.terminator(&mir_body), body_span)
688688
{
689689
coverage_spans.push(spans::CoverageSpan::for_terminator(
690690
span,
691-
is_macro_expansion,
691+
expn_span,
692692
bcb,
693693
data.last_bb(),
694694
));

src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.inner_items.txt

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
1| |#![allow(unused_assignments, unused_variables, dead_code)]
22
2| |
33
3| 1|fn main() {
4-
4| | // Initialize test constants in a way that cannot be determined at compile time, to ensure
5-
5| | // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
6-
6| | // dependent conditions.
4+
4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure
5+
5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
6+
6| 1| // dependent conditions.
77
7| 1| let is_true = std::env::args().len() == 1;
88
8| 1|
99
9| 1| let mut countdown = 0;

0 commit comments

Comments
 (0)