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.
This commit is contained in:
@@ -1,11 +1,8 @@
|
|||||||
use std::collections::VecDeque;
|
|
||||||
use std::iter;
|
|
||||||
|
|
||||||
use rustc_data_structures::fx::FxHashSet;
|
use rustc_data_structures::fx::FxHashSet;
|
||||||
use rustc_middle::mir;
|
use rustc_middle::mir;
|
||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
|
use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span};
|
||||||
use tracing::{debug, debug_span, instrument};
|
use tracing::instrument;
|
||||||
|
|
||||||
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
|
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
|
||||||
use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir};
|
use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir};
|
||||||
@@ -83,25 +80,18 @@ pub(super) fn extract_refined_covspans<'tcx>(
|
|||||||
holes.sort_by(|a, b| compare_spans(a.span, b.span));
|
holes.sort_by(|a, b| compare_spans(a.span, b.span));
|
||||||
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
|
holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
|
||||||
|
|
||||||
// Split the covspans into separate buckets that don't overlap any holes.
|
// Discard any span that overlaps with a hole.
|
||||||
let buckets = divide_spans_into_buckets(covspans, &holes);
|
discard_spans_overlapping_holes(&mut covspans, &holes);
|
||||||
|
|
||||||
for covspans in buckets {
|
|
||||||
let _span = debug_span!("processing bucket", ?covspans).entered();
|
|
||||||
|
|
||||||
|
// Perform more refinement steps after holes have been dealt with.
|
||||||
let mut covspans = remove_unwanted_overlapping_spans(covspans);
|
let mut covspans = remove_unwanted_overlapping_spans(covspans);
|
||||||
debug!(?covspans, "after removing overlaps");
|
|
||||||
|
|
||||||
// Do one last merge pass, to simplify the output.
|
|
||||||
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
|
covspans.dedup_by(|b, a| a.merge_if_eligible(b));
|
||||||
debug!(?covspans, "after merge");
|
|
||||||
|
|
||||||
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
|
code_mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
|
||||||
// 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 }
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
|
/// Macros that expand into branches (e.g. `assert!`, `trace!`) tend to generate
|
||||||
/// multiple condition/consequent blocks that have the span of the whole macro
|
/// multiple condition/consequent blocks that have the span of the whole macro
|
||||||
@@ -142,52 +132,36 @@ fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec<SpanFromMir>)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Uses the holes to divide the given covspans into buckets, such that:
|
/// Discard all covspans that overlap a hole.
|
||||||
/// - No span in any hole overlaps a bucket (discarding spans if necessary).
|
|
||||||
/// - The spans in each bucket are strictly after all spans in previous buckets,
|
|
||||||
/// and strictly before all spans in subsequent buckets.
|
|
||||||
///
|
///
|
||||||
/// The lists of covspans and holes must be sorted.
|
/// The lists of covspans and holes must be sorted, and any holes that overlap
|
||||||
/// The resulting buckets are sorted relative to each other, and each bucket's
|
/// with each other must have already been merged.
|
||||||
/// contents are sorted.
|
fn discard_spans_overlapping_holes(covspans: &mut Vec<Covspan>, holes: &[Hole]) {
|
||||||
#[instrument(level = "debug")]
|
debug_assert!(covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
|
||||||
fn divide_spans_into_buckets(input_covspans: Vec<Covspan>, holes: &[Hole]) -> Vec<Vec<Covspan>> {
|
|
||||||
debug_assert!(input_covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
|
|
||||||
debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
|
debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
|
||||||
|
debug_assert!(holes.array_windows().all(|[a, b]| !a.span.overlaps_or_adjacent(b.span)));
|
||||||
|
|
||||||
// Now we're ready to start grouping spans into buckets separated by holes.
|
let mut curr_hole = 0usize;
|
||||||
|
let mut overlaps_hole = |covspan: &Covspan| -> bool {
|
||||||
let mut input_covspans = VecDeque::from(input_covspans);
|
while let Some(hole) = holes.get(curr_hole) {
|
||||||
|
// Both lists are sorted, so we can permanently skip any holes that
|
||||||
// For each hole:
|
// end before the start of the current span.
|
||||||
// - Identify the spans that are entirely or partly before the hole.
|
if hole.span.hi() <= covspan.span.lo() {
|
||||||
// - Discard any that overlap with the hole.
|
curr_hole += 1;
|
||||||
// - Add the remaining identified spans to the corresponding bucket.
|
continue;
|
||||||
let mut buckets = (0..holes.len()).map(|_| vec![]).collect::<Vec<_>>();
|
|
||||||
for (hole, bucket) in holes.iter().zip(&mut buckets) {
|
|
||||||
bucket.extend(
|
|
||||||
drain_front_while(&mut input_covspans, |c| c.span.lo() < hole.span.hi())
|
|
||||||
.filter(|c| !c.span.overlaps(hole.span)),
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Any remaining spans form their own final bucket, after the final hole.
|
return hole.span.overlaps(covspan.span);
|
||||||
// (If there were no holes, this will just be all of the initial spans.)
|
|
||||||
buckets.push(Vec::from(input_covspans));
|
|
||||||
|
|
||||||
buckets
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Similar to `.drain(..)`, but stops just before it would remove an item not
|
// No holes left, so this covspan doesn't overlap with any holes.
|
||||||
/// satisfying the predicate.
|
false
|
||||||
fn drain_front_while<'a, T>(
|
};
|
||||||
queue: &'a mut VecDeque<T>,
|
|
||||||
mut pred_fn: impl FnMut(&T) -> bool,
|
covspans.retain(|covspan| !overlaps_hole(covspan));
|
||||||
) -> impl Iterator<Item = T> {
|
|
||||||
iter::from_fn(move || queue.pop_front_if(|x| pred_fn(x)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines"
|
/// Takes a list of sorted spans extracted from MIR, and "refines"
|
||||||
/// those spans by removing spans that overlap in unwanted ways.
|
/// those spans by removing spans that overlap in unwanted ways.
|
||||||
#[instrument(level = "debug")]
|
#[instrument(level = "debug")]
|
||||||
fn remove_unwanted_overlapping_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> {
|
fn remove_unwanted_overlapping_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> {
|
||||||
|
|||||||
Reference in New Issue
Block a user