Skip to content

Commit 8e36076

Browse files
committed
coverage: Handle hole spans without dividing spans into buckets
Because we no longer merge non-adjacent spans, there is no need to use buckets to prevent merging across hole spans.
1 parent 15a7ec1 commit 8e36076

File tree

1 file changed

+33
-59
lines changed
  • compiler/rustc_mir_transform/src/coverage

1 file changed

+33
-59
lines changed

compiler/rustc_mir_transform/src/coverage/spans.rs

+33-59
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
1-
use std::collections::VecDeque;
2-
use std::iter;
3-
41
use rustc_data_structures::fx::FxHashSet;
52
use rustc_middle::mir;
63
use rustc_middle::ty::TyCtxt;
74
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
8-
use tracing::{debug, debug_span, instrument};
5+
use tracing::instrument;
96

107
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
118
use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir};
@@ -78,24 +75,17 @@ pub(super) fn extract_refined_covspans<'tcx>(
7875
holes.sort_by(|a, b| compare_spans(a.span, b.span));
7976
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
8077

81-
// Split the covspans into separate buckets that don't overlap any holes.
82-
let buckets = divide_spans_into_buckets(covspans, &holes);
83-
84-
for covspans in buckets {
85-
let _span = debug_span!("processing bucket", ?covspans).entered();
78+
// Discard any span that overlaps with a hole.
79+
discard_spans_overlapping_holes(&mut covspans, &holes);
8680

87-
let mut covspans = remove_unwanted_overlapping_spans(covspans);
88-
debug!(?covspans, "after removing overlaps");
81+
// Perform more refinement steps after holes have been dealt with.
82+
let mut covspans = remove_unwanted_overlapping_spans(covspans);
83+
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
8984

90-
// Do one last merge pass, to simplify the output.
91-
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
92-
debug!(?covspans, "after merge");
93-
94-
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
95-
// Each span produced by the refiner represents an ordinary code region.
96-
mappings::CodeMapping { span, bcb }
97-
}));
98-
}
85+
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
86+
// Each span produced by the refiner represents an ordinary code region.
87+
mappings::CodeMapping { span, bcb }
88+
}));
9989
}
10090

10191
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
@@ -137,52 +127,36 @@ fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec<SpanFromMir>)
137127
}
138128
}
139129

140-
/// Uses the holes to divide the given covspans into buckets, such that:
141-
/// - No span in any hole overlaps a bucket (discarding spans if necessary).
142-
/// - The spans in each bucket are strictly after all spans in previous buckets,
143-
/// and strictly before all spans in subsequent buckets.
130+
/// Discard all covspans that overlap a hole.
144131
///
145-
/// The lists of covspans and holes must be sorted.
146-
/// The resulting buckets are sorted relative to each other, and each bucket's
147-
/// contents are sorted.
148-
#[instrument(level = "debug")]
149-
fn divide_spans_into_buckets(input_covspans: Vec<Covspan>, holes: &[Hole]) -> Vec<Vec<Covspan>> {
150-
debug_assert!(input_covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
132+
/// The lists of covspans and holes must be sorted, and any holes that overlap
133+
/// with each other must have already been merged.
134+
fn discard_spans_overlapping_holes(covspans: &mut Vec<Covspan>, holes: &[Hole]) {
135+
debug_assert!(covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
151136
debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
137+
debug_assert!(holes.array_windows().all(|[a, b]| !a.span.overlaps_or_adjacent(b.span)));
138+
139+
let mut curr_hole = 0usize;
140+
let mut overlaps_hole = |covspan: &Covspan| -> bool {
141+
while let Some(hole) = holes.get(curr_hole) {
142+
// Both lists are sorted, so we can permanently skip any holes that
143+
// end before the start of the current span.
144+
if hole.span.hi() <= covspan.span.lo() {
145+
curr_hole += 1;
146+
continue;
147+
}
152148

153-
// Now we're ready to start grouping spans into buckets separated by holes.
154-
155-
let mut input_covspans = VecDeque::from(input_covspans);
156-
157-
// For each hole:
158-
// - Identify the spans that are entirely or partly before the hole.
159-
// - Discard any that overlap with the hole.
160-
// - Add the remaining identified spans to the corresponding bucket.
161-
let mut buckets = (0..holes.len()).map(|_| vec![]).collect::<Vec<_>>();
162-
for (hole, bucket) in holes.iter().zip(&mut buckets) {
163-
bucket.extend(
164-
drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi())
165-
.filter(|c| !c.span.overlaps(hole.span)),
166-
);
167-
}
168-
169-
// Any remaining spans form their own final bucket, after the final hole.
170-
// (If there were no holes, this will just be all of the initial spans.)
171-
buckets.push(Vec::from(input_covspans));
149+
return hole.span.overlaps(covspan.span);
150+
}
172151

173-
buckets
174-
}
152+
// No holes left, so this covspan doesn't overlap with any holes.
153+
false
154+
};
175155

176-
/// Similar to `.drain(..)`, but stops just before it would remove an item not
177-
/// satisfying the predicate.
178-
fn drain_front_while<'a, T>(
179-
queue: &'a mut VecDeque<T>,
180-
mut pred_fn: impl FnMut(&T) -> bool,
181-
) -> impl Iterator<Item = T> {
182-
iter::from_fn(move || queue.pop_front_if(|x| pred_fn(x)))
156+
covspans.retain(|covspan| !overlaps_hole(covspan));
183157
}
184158

185-
/// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines"
159+
/// Takes a list of sorted spans extracted from MIR, and "refines"
186160
/// those spans by removing spans that overlap in unwanted ways.
187161
#[instrument(level = "debug")]
188162
fn remove_unwanted_overlapping_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> {

0 commit comments

Comments
 (0)