coverage: Hoist the splitting of visible macro invocations

This commit is contained in:
Zalathar 2023-12-22 11:21:52 +11:00
parent cd3a9760e4
commit d4d2f1428c
3 changed files with 54 additions and 58 deletions

View file

@ -220,7 +220,6 @@ impl<'a> CoverageSpansGenerator<'a> {
// span-processing steps don't make sense yet.
if self.some_prev.is_none() {
debug!(" initial span");
self.maybe_push_macro_name_span();
continue;
}
@ -232,7 +231,6 @@ impl<'a> CoverageSpansGenerator<'a> {
debug!(" same bcb (and neither is a closure), merge with prev={prev:?}");
let prev = self.take_prev();
self.curr_mut().merge_from(&prev);
self.maybe_push_macro_name_span();
// Note that curr.span may now differ from curr_original_span
} else if prev.span.hi() <= curr.span.lo() {
debug!(
@ -240,7 +238,6 @@ impl<'a> CoverageSpansGenerator<'a> {
);
let prev = self.take_prev();
self.refined_spans.push(prev);
self.maybe_push_macro_name_span();
} else if prev.is_closure {
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
// next iter
@ -256,7 +253,6 @@ impl<'a> CoverageSpansGenerator<'a> {
self.update_pending_dups();
} else {
self.cutoff_prev_at_overlapping_curr();
self.maybe_push_macro_name_span();
}
}
@ -291,36 +287,6 @@ impl<'a> CoverageSpansGenerator<'a> {
self.refined_spans
}
/// If `curr` is part of a new macro expansion, carve out and push a separate
/// span that ends just after the macro name and its subsequent `!`.
fn maybe_push_macro_name_span(&mut self) {
let curr = self.curr();
let Some(visible_macro) = curr.visible_macro else { return };
// The split point is relative to `curr_original_span`,
// because `curr.span` may have been merged with preceding spans.
let split_point_after_macro_bang = self.curr_original_span.lo()
+ BytePos(visible_macro.as_str().len() as u32)
+ BytePos(1); // add 1 for the `!`
debug_assert!(split_point_after_macro_bang <= curr.span.hi());
if split_point_after_macro_bang > curr.span.hi() {
// Something is wrong with the macro name span;
// return now to avoid emitting malformed mappings (e.g. #117788).
return;
}
let mut macro_name_cov = curr.clone();
macro_name_cov.span = macro_name_cov.span.with_hi(split_point_after_macro_bang);
self.curr_mut().span = curr.span.with_lo(split_point_after_macro_bang);
debug!(
" and curr starts a new macro expansion, so add a new span just for \
the macro `{visible_macro}!`, new span={macro_name_cov:?}",
);
self.refined_spans.push(macro_name_cov);
}
#[track_caller]
fn curr(&self) -> &CoverageSpan {
self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)"))

View file

@ -38,6 +38,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
initial_spans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
remove_unwanted_macro_spans(&mut initial_spans);
split_visible_macro_spans(&mut initial_spans);
initial_spans.sort_by(|a, b| {
// First sort by span start.
@ -79,6 +80,41 @@ fn remove_unwanted_macro_spans(initial_spans: &mut Vec<CoverageSpan>) {
});
}
/// When a span corresponds to a macro invocation that is visible from the
/// function body, split it into two parts. The first part covers just the
/// macro name plus `!`, and the second part covers the rest of the macro
/// invocation. This seems to give better results for code that uses macros.
fn split_visible_macro_spans(initial_spans: &mut Vec<CoverageSpan>) {
let mut extra_spans = vec![];
initial_spans.retain(|covspan| {
if covspan.is_closure {
return true;
}
let Some(visible_macro) = covspan.visible_macro else { return true };
let split_len = visible_macro.as_str().len() as u32 + 1;
let (before, after) = covspan.span.split_at(split_len);
if !covspan.span.contains(before) || !covspan.span.contains(after) {
// Something is unexpectedly wrong with the split point.
// The debug assertion in `split_at` will have already caught this,
// but in release builds it's safer to do nothing and maybe get a
// bug report for unexpected coverage, rather than risk an ICE.
return true;
}
assert!(!covspan.is_closure);
extra_spans.push(CoverageSpan::new(before, covspan.visible_macro, covspan.bcb, false));
extra_spans.push(CoverageSpan::new(after, covspan.visible_macro, covspan.bcb, false));
false // Discard the original covspan that we just split.
});
// The newly-split spans are added at the end, so any previous sorting
// is not preserved.
initial_spans.extend(extra_spans);
}
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated
// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will