Skip to content

Commit ce1c32f

Browse files
committed
coverage: Hoist the splitting of visible macro invocations
1 parent 3002284 commit ce1c32f

File tree

2 files changed

+36
-44
lines changed

2 files changed

+36
-44
lines changed

compiler/rustc_mir_transform/src/coverage/spans.rs

-44
Original file line numberDiff line numberDiff line change
@@ -159,10 +159,6 @@ impl CoverageSpan {
159159
/// execution
160160
/// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures)
161161
struct CoverageSpansGenerator<'a> {
162-
/// A `Span` covering the function body of the MIR (typically from left curly brace to right
163-
/// curly brace).
164-
body_span: Span,
165-
166162
/// The BasicCoverageBlock Control Flow Graph (BCB CFG).
167163
basic_coverage_blocks: &'a CoverageGraph,
168164

@@ -239,7 +235,6 @@ impl<'a> CoverageSpansGenerator<'a> {
239235
);
240236

241237
let coverage_spans = Self {
242-
body_span: hir_info.body_span,
243238
basic_coverage_blocks,
244239
sorted_spans_iter: sorted_spans.into_iter(),
245240
some_curr: None,
@@ -261,7 +256,6 @@ impl<'a> CoverageSpansGenerator<'a> {
261256
// span-processing steps don't make sense yet.
262257
if self.some_prev.is_none() {
263258
debug!(" initial span");
264-
self.maybe_push_macro_name_span();
265259
continue;
266260
}
267261

@@ -273,15 +267,13 @@ impl<'a> CoverageSpansGenerator<'a> {
273267
debug!(" same bcb (and neither is a closure), merge with prev={prev:?}");
274268
let prev = self.take_prev();
275269
self.curr_mut().merge_from(&prev);
276-
self.maybe_push_macro_name_span();
277270
// Note that curr.span may now differ from curr_original_span
278271
} else if prev.span.hi() <= curr.span.lo() {
279272
debug!(
280273
" different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}",
281274
);
282275
let prev = self.take_prev();
283276
self.refined_spans.push(prev);
284-
self.maybe_push_macro_name_span();
285277
} else if prev.is_closure {
286278
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
287279
// next iter
@@ -297,7 +289,6 @@ impl<'a> CoverageSpansGenerator<'a> {
297289
self.update_pending_dups();
298290
} else {
299291
self.cutoff_prev_at_overlapping_curr();
300-
self.maybe_push_macro_name_span();
301292
}
302293
}
303294

@@ -332,41 +323,6 @@ impl<'a> CoverageSpansGenerator<'a> {
332323
self.refined_spans
333324
}
334325

335-
/// If `curr` is part of a new macro expansion, carve out and push a separate
336-
/// span that ends just after the macro name and its subsequent `!`.
337-
fn maybe_push_macro_name_span(&mut self) {
338-
let curr = self.curr();
339-
340-
let Some(visible_macro) = curr.visible_macro(self.body_span) else { return };
341-
if let Some(prev) = &self.some_prev
342-
&& prev.expn_span.eq_ctxt(curr.expn_span)
343-
{
344-
return;
345-
}
346-
347-
// The split point is relative to `curr_original_span`,
348-
// because `curr.span` may have been merged with preceding spans.
349-
let split_point_after_macro_bang = self.curr_original_span.lo()
350-
+ BytePos(visible_macro.as_str().len() as u32)
351-
+ BytePos(1); // add 1 for the `!`
352-
debug_assert!(split_point_after_macro_bang <= curr.span.hi());
353-
if split_point_after_macro_bang > curr.span.hi() {
354-
// Something is wrong with the macro name span;
355-
// return now to avoid emitting malformed mappings (e.g. #117788).
356-
return;
357-
}
358-
359-
let mut macro_name_cov = curr.clone();
360-
macro_name_cov.span = macro_name_cov.span.with_hi(split_point_after_macro_bang);
361-
self.curr_mut().span = curr.span.with_lo(split_point_after_macro_bang);
362-
363-
debug!(
364-
" and curr starts a new macro expansion, so add a new span just for \
365-
the macro `{visible_macro}!`, new span={macro_name_cov:?}",
366-
);
367-
self.refined_spans.push(macro_name_cov);
368-
}
369-
370326
#[track_caller]
371327
fn curr(&self) -> &CoverageSpan {
372328
self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)"))

compiler/rustc_mir_transform/src/coverage/spans/from_mir.rs

+36
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
3939

4040
initial_spans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
4141
remove_unwanted_macro_spans(&mut initial_spans);
42+
split_visible_macro_spans(&mut initial_spans, hir_info);
4243

4344
initial_spans.sort_by(|a, b| {
4445
// First sort by span start.
@@ -80,6 +81,41 @@ fn remove_unwanted_macro_spans(initial_spans: &mut Vec<CoverageSpan>) {
8081
});
8182
}
8283

84+
/// When a span corresponds to a macro invocation that is visible from the
85+
/// function body, split it into two parts. The first part covers just the
86+
/// macro name plus `!`, and the second part covers the rest of the macro
87+
/// invocation. This seems to give better results for code that uses macros.
88+
fn split_visible_macro_spans(initial_spans: &mut Vec<CoverageSpan>, hir_info: &ExtractedHirInfo) {
89+
let mut extra_spans = vec![];
90+
91+
initial_spans.retain(|covspan| {
92+
if covspan.is_closure {
93+
return true;
94+
}
95+
96+
let Some(visible_macro) = covspan.visible_macro(hir_info.body_span) else { return true };
97+
98+
let split_len = visible_macro.as_str().len() as u32 + 1;
99+
let (before, after) = covspan.span.split_at(split_len);
100+
if !covspan.span.contains(before) || !covspan.span.contains(after) {
101+
// Something is unexpectedly wrong with the split point.
102+
// The debug assertion in `split_at` will have already caught this,
103+
// but in release builds it's safer to do nothing and maybe get a
104+
// bug report for unexpected coverage, rather than risk an ICE.
105+
return true;
106+
}
107+
108+
assert!(!covspan.is_closure);
109+
extra_spans.push(CoverageSpan::new(before, covspan.expn_span, covspan.bcb, false));
110+
extra_spans.push(CoverageSpan::new(after, covspan.expn_span, covspan.bcb, false));
111+
false // Discard the original covspan that we just split.
112+
});
113+
114+
// The newly-split spans are added at the end, so any previous sorting
115+
// is not preserved.
116+
initial_spans.extend(extra_spans);
117+
}
118+
83119
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
84120
// the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated
85121
// for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will

0 commit comments

Comments
 (0)