Skip to content

Commit f88b3a1

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 e608c04 commit f88b3a1

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};
@@ -83,24 +80,17 @@ pub(super) fn extract_refined_covspans<'tcx>(
8380
holes.sort_by(|a, b| compare_spans(a.span, b.span));
8481
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
8582

86-
// Split the covspans into separate buckets that don't overlap any holes.
87-
let buckets = divide_spans_into_buckets(covspans, &holes);
88-
89-
for covspans in buckets {
90-
let _span = debug_span!("processing bucket", ?covspans).entered();
83+
// Discard any span that overlaps with a hole.
84+
discard_spans_overlapping_holes(&mut covspans, &holes);
9185

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

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

10696
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
@@ -142,52 +132,36 @@ fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec<SpanFromMir>)
142132
}
143133
}
144134

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

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

178-
buckets
179-
}
157+
// No holes left, so this covspan doesn't overlap with any holes.
158+
false
159+
};
180160

181-
/// Similar to `.drain(..)`, but stops just before it would remove an item not
182-
/// satisfying the predicate.
183-
fn drain_front_while<'a, T>(
184-
queue: &'a mut VecDeque<T>,
185-
mut pred_fn: impl FnMut(&T) -> bool,
186-
) -> impl Iterator<Item = T> {
187-
iter::from_fn(move || queue.pop_front_if(|x| pred_fn(x)))
161+
covspans.retain(|covspan| !overlaps_hole(covspan));
188162
}
189163

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

0 commit comments

Comments
 (0)