coverage: Remove hole-carving code from the main span refiner

Now that hole spans are handled by a separate earlier pass, this code never
sees hole spans, and therefore doesn't need to deal with them.
This commit is contained in:
Zalathar 2024-06-02 22:35:13 +10:00
parent 6d1557f268
commit c57a1d1baa

View file

@ -24,8 +24,7 @@ pub(super) fn extract_refined_covspans(
from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks); from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks);
for bucket in sorted_span_buckets { for bucket in sorted_span_buckets {
let refined_spans = SpansRefiner::refine_sorted_spans(bucket); let refined_spans = SpansRefiner::refine_sorted_spans(bucket);
code_mappings.extend(refined_spans.into_iter().map(|covspan| { code_mappings.extend(refined_spans.into_iter().map(|RefinedCovspan { span, bcb }| {
let RefinedCovspan { span, bcb, is_hole: _ } = covspan;
// Each span produced by the refiner represents an ordinary code region. // Each span produced by the refiner represents an ordinary code region.
mappings::CodeMapping { span, bcb } mappings::CodeMapping { span, bcb }
})); }));
@ -36,24 +35,16 @@ pub(super) fn extract_refined_covspans(
struct CurrCovspan { struct CurrCovspan {
span: Span, span: Span,
bcb: BasicCoverageBlock, bcb: BasicCoverageBlock,
is_hole: bool,
} }
impl CurrCovspan { impl CurrCovspan {
fn new(span: Span, bcb: BasicCoverageBlock, is_hole: bool) -> Self { fn new(span: Span, bcb: BasicCoverageBlock) -> Self {
Self { span, bcb, is_hole } Self { span, bcb }
} }
fn into_prev(self) -> PrevCovspan { fn into_prev(self) -> PrevCovspan {
let Self { span, bcb, is_hole } = self; let Self { span, bcb } = self;
PrevCovspan { span, bcb, merged_spans: vec![span], is_hole } PrevCovspan { span, bcb, merged_spans: vec![span] }
}
fn into_refined(self) -> RefinedCovspan {
// This is only called in cases where `curr` is a hole span that has
// been carved out of `prev`.
debug_assert!(self.is_hole);
self.into_prev().into_refined()
} }
} }
@ -64,12 +55,11 @@ struct PrevCovspan {
/// List of all the original spans from MIR that have been merged into this /// List of all the original spans from MIR that have been merged into this
/// span. Mainly used to precisely skip over gaps when truncating a span. /// span. Mainly used to precisely skip over gaps when truncating a span.
merged_spans: Vec<Span>, merged_spans: Vec<Span>,
is_hole: bool,
} }
impl PrevCovspan { impl PrevCovspan {
fn is_mergeable(&self, other: &CurrCovspan) -> bool { fn is_mergeable(&self, other: &CurrCovspan) -> bool {
self.bcb == other.bcb && !self.is_hole && !other.is_hole self.bcb == other.bcb
} }
fn merge_from(&mut self, other: &CurrCovspan) { fn merge_from(&mut self, other: &CurrCovspan) {
@ -87,14 +77,9 @@ impl PrevCovspan {
if self.merged_spans.is_empty() { None } else { Some(self.into_refined()) } if self.merged_spans.is_empty() { None } else { Some(self.into_refined()) }
} }
fn refined_copy(&self) -> RefinedCovspan {
let &Self { span, bcb, merged_spans: _, is_hole } = self;
RefinedCovspan { span, bcb, is_hole }
}
fn into_refined(self) -> RefinedCovspan { fn into_refined(self) -> RefinedCovspan {
// Even though we consume self, we can just reuse the copying impl. let Self { span, bcb, merged_spans: _ } = self;
self.refined_copy() RefinedCovspan { span, bcb }
} }
} }
@ -102,12 +87,11 @@ impl PrevCovspan {
struct RefinedCovspan { struct RefinedCovspan {
span: Span, span: Span,
bcb: BasicCoverageBlock, bcb: BasicCoverageBlock,
is_hole: bool,
} }
impl RefinedCovspan { impl RefinedCovspan {
fn is_mergeable(&self, other: &Self) -> bool { fn is_mergeable(&self, other: &Self) -> bool {
self.bcb == other.bcb && !self.is_hole && !other.is_hole self.bcb == other.bcb
} }
fn merge_from(&mut self, other: &Self) { fn merge_from(&mut self, other: &Self) {
@ -122,8 +106,6 @@ impl RefinedCovspan {
/// * Remove duplicate source code coverage regions /// * Remove duplicate source code coverage regions
/// * Merge spans that represent continuous (both in source code and control flow), non-branching /// * Merge spans that represent continuous (both in source code and control flow), non-branching
/// execution /// execution
/// * Carve out (leave uncovered) any "hole" spans that need to be left blank
/// (e.g. closures that will be counted by their own MIR body)
struct SpansRefiner { struct SpansRefiner {
/// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative /// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative
/// dominance between the `BasicCoverageBlock`s of equal `Span`s. /// dominance between the `BasicCoverageBlock`s of equal `Span`s.
@ -184,13 +166,6 @@ impl SpansRefiner {
); );
let prev = self.take_prev().into_refined(); let prev = self.take_prev().into_refined();
self.refined_spans.push(prev); self.refined_spans.push(prev);
} else if prev.is_hole {
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
// next iter
debug!(?prev, "prev (a hole) overlaps curr, so discarding curr");
self.take_curr(); // Discards curr.
} else if curr.is_hole {
self.carve_out_span_for_hole();
} else { } else {
self.cutoff_prev_at_overlapping_curr(); self.cutoff_prev_at_overlapping_curr();
} }
@ -214,9 +189,6 @@ impl SpansRefiner {
} }
}); });
// Discard hole spans, since their purpose was to carve out chunks from
// other spans, but we don't want the holes themselves in the final mappings.
self.refined_spans.retain(|covspan| !covspan.is_hole);
self.refined_spans self.refined_spans
} }
@ -252,50 +224,17 @@ impl SpansRefiner {
if let Some(curr) = self.some_curr.take() { if let Some(curr) = self.some_curr.take() {
self.some_prev = Some(curr.into_prev()); self.some_prev = Some(curr.into_prev());
} }
while let Some(curr) = self.sorted_spans_iter.next() { if let Some(SpanFromMir { span, bcb, .. }) = self.sorted_spans_iter.next() {
debug!("FOR curr={:?}", curr); // This code only sees sorted spans after hole-carving, so there should
if let Some(prev) = &self.some_prev // be no way for `curr` to start before `prev`.
&& prev.span.lo() > curr.span.lo() if let Some(prev) = &self.some_prev {
{ debug_assert!(prev.span.lo() <= span.lo());
// Skip curr because prev has already advanced beyond the end of curr.
// This can only happen if a prior iteration updated `prev` to skip past
// a region of code, such as skipping past a hole.
debug!(?prev, "prev.span starts after curr.span, so curr will be dropped");
} else {
self.some_curr = Some(CurrCovspan::new(curr.span, curr.bcb, false));
return true;
} }
} self.some_curr = Some(CurrCovspan::new(span, bcb));
false debug!(?self.some_prev, ?self.some_curr, "next_coverage_span");
} true
} else {
/// If `prev`s span extends left of the hole (`curr`), carve out the hole's span from false
/// `prev`'s span. Add the portion of the span to the left of the hole; and if the span
/// extends to the right of the hole, update `prev` to that portion of the span.
fn carve_out_span_for_hole(&mut self) {
let prev = self.prev();
let curr = self.curr();
let left_cutoff = curr.span.lo();
let right_cutoff = curr.span.hi();
let has_pre_hole_span = prev.span.lo() < right_cutoff;
let has_post_hole_span = prev.span.hi() > right_cutoff;
if has_pre_hole_span {
let mut pre_hole = prev.refined_copy();
pre_hole.span = pre_hole.span.with_hi(left_cutoff);
debug!(?pre_hole, "prev overlaps a hole; adding pre-hole span");
self.refined_spans.push(pre_hole);
}
if has_post_hole_span {
// Mutate `prev.span` to start after the hole (and discard curr).
self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
debug!(prev=?self.prev(), "mutated prev to start after the hole");
// Prevent this curr from becoming prev.
let hole_covspan = self.take_curr().into_refined();
self.refined_spans.push(hole_covspan); // since self.prev() was already updated
} }
} }