coverage: Eagerly convert coverage spans to a simpler form
This commit is contained in:
parent
bf74fb1d2f
commit
88ade9c740
2 changed files with 55 additions and 49 deletions
|
@ -28,11 +28,15 @@ pub(super) fn extract_refined_covspans(
|
||||||
let ExtractedCovspans { mut covspans, mut holes } =
|
let ExtractedCovspans { mut covspans, mut holes } =
|
||||||
extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks);
|
extract_covspans_and_holes_from_mir(mir_body, hir_info, basic_coverage_blocks);
|
||||||
|
|
||||||
|
// First, perform the passes that need macro information.
|
||||||
covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
|
covspans.sort_by(|a, b| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb));
|
||||||
remove_unwanted_macro_spans(&mut covspans);
|
remove_unwanted_macro_spans(&mut covspans);
|
||||||
split_visible_macro_spans(&mut covspans);
|
split_visible_macro_spans(&mut covspans);
|
||||||
|
|
||||||
let compare_covspans = |a: &SpanFromMir, b: &SpanFromMir| {
|
// We no longer need the extra information in `SpanFromMir`, so convert to `Covspan`.
|
||||||
|
let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::<Vec<_>>();
|
||||||
|
|
||||||
|
let compare_covspans = |a: &Covspan, b: &Covspan| {
|
||||||
compare_spans(a.span, b.span)
|
compare_spans(a.span, b.span)
|
||||||
// After deduplication, we want to keep only the most-dominated BCB.
|
// After deduplication, we want to keep only the most-dominated BCB.
|
||||||
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
|
.then_with(|| basic_coverage_blocks.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
|
||||||
|
@ -53,7 +57,7 @@ pub(super) fn extract_refined_covspans(
|
||||||
// and grouping them in buckets separated by the holes.
|
// and grouping them in buckets separated by the holes.
|
||||||
|
|
||||||
let mut input_covspans = VecDeque::from(covspans);
|
let mut input_covspans = VecDeque::from(covspans);
|
||||||
let mut fragments: Vec<SpanFromMir> = vec![];
|
let mut fragments = vec![];
|
||||||
|
|
||||||
// For each hole:
|
// For each hole:
|
||||||
// - Identify the spans that are entirely or partly before the hole.
|
// - Identify the spans that are entirely or partly before the hole.
|
||||||
|
@ -88,7 +92,7 @@ pub(super) fn extract_refined_covspans(
|
||||||
covspans.sort_by(compare_covspans);
|
covspans.sort_by(compare_covspans);
|
||||||
|
|
||||||
let covspans = refine_sorted_spans(covspans);
|
let covspans = refine_sorted_spans(covspans);
|
||||||
code_mappings.extend(covspans.into_iter().map(|RefinedCovspan { 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 }
|
||||||
}));
|
}));
|
||||||
|
@ -145,23 +149,6 @@ fn split_visible_macro_spans(covspans: &mut Vec<SpanFromMir>) {
|
||||||
covspans.extend(extra_spans);
|
covspans.extend(extra_spans);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
struct RefinedCovspan {
|
|
||||||
span: Span,
|
|
||||||
bcb: BasicCoverageBlock,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl RefinedCovspan {
|
|
||||||
fn is_mergeable(&self, other: &Self) -> bool {
|
|
||||||
self.bcb == other.bcb
|
|
||||||
}
|
|
||||||
|
|
||||||
fn merge_from(&mut self, other: &Self) {
|
|
||||||
debug_assert!(self.is_mergeable(other));
|
|
||||||
self.span = self.span.to(other.span);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Similar to `.drain(..)`, but stops just before it would remove an item not
|
/// Similar to `.drain(..)`, but stops just before it would remove an item not
|
||||||
/// satisfying the predicate.
|
/// satisfying the predicate.
|
||||||
fn drain_front_while<'a, T>(
|
fn drain_front_while<'a, T>(
|
||||||
|
@ -175,18 +162,18 @@ fn drain_front_while<'a, T>(
|
||||||
/// those spans by removing spans that overlap in unwanted ways, and by merging
|
/// those spans by removing spans that overlap in unwanted ways, and by merging
|
||||||
/// compatible adjacent spans.
|
/// compatible adjacent spans.
|
||||||
#[instrument(level = "debug")]
|
#[instrument(level = "debug")]
|
||||||
fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
|
fn refine_sorted_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> {
|
||||||
// Holds spans that have been read from the input vector, but haven't yet
|
// Holds spans that have been read from the input vector, but haven't yet
|
||||||
// been committed to the output vector.
|
// been committed to the output vector.
|
||||||
let mut pending = vec![];
|
let mut pending = vec![];
|
||||||
let mut refined = vec![];
|
let mut refined = vec![];
|
||||||
|
|
||||||
for curr in sorted_spans {
|
for curr in sorted_spans {
|
||||||
pending.retain(|prev: &SpanFromMir| {
|
pending.retain(|prev: &Covspan| {
|
||||||
if prev.span.hi() <= curr.span.lo() {
|
if prev.span.hi() <= curr.span.lo() {
|
||||||
// There's no overlap between the previous/current covspans,
|
// There's no overlap between the previous/current covspans,
|
||||||
// so move the previous one into the refined list.
|
// so move the previous one into the refined list.
|
||||||
refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb });
|
refined.push(prev.clone());
|
||||||
false
|
false
|
||||||
} else {
|
} else {
|
||||||
// Otherwise, retain the previous covspan only if it has the
|
// Otherwise, retain the previous covspan only if it has the
|
||||||
|
@ -199,26 +186,55 @@ fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Drain the rest of the pending list into the refined list.
|
// Drain the rest of the pending list into the refined list.
|
||||||
for prev in pending {
|
refined.extend(pending);
|
||||||
refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Do one last merge pass, to simplify the output.
|
// Do one last merge pass, to simplify the output.
|
||||||
debug!(?refined, "before merge");
|
debug!(?refined, "before merge");
|
||||||
refined.dedup_by(|b, a| {
|
refined.dedup_by(|b, a| a.merge_if_eligible(b));
|
||||||
if a.is_mergeable(b) {
|
|
||||||
debug!(?a, ?b, "merging list-adjacent refined spans");
|
|
||||||
a.merge_from(b);
|
|
||||||
true
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
});
|
|
||||||
debug!(?refined, "after merge");
|
debug!(?refined, "after merge");
|
||||||
|
|
||||||
refined
|
refined
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
struct Covspan {
|
||||||
|
span: Span,
|
||||||
|
bcb: BasicCoverageBlock,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Covspan {
|
||||||
|
/// Splits this covspan into 0-2 parts:
|
||||||
|
/// - The part that is strictly before the hole span, if any.
|
||||||
|
/// - The part that is strictly after the hole span, if any.
|
||||||
|
fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
|
||||||
|
let before = try {
|
||||||
|
let span = self.span.trim_end(hole_span)?;
|
||||||
|
Self { span, ..*self }
|
||||||
|
};
|
||||||
|
let after = try {
|
||||||
|
let span = self.span.trim_start(hole_span)?;
|
||||||
|
Self { span, ..*self }
|
||||||
|
};
|
||||||
|
|
||||||
|
(before, after)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// If `self` and `other` can be merged (i.e. they have the same BCB),
|
||||||
|
/// mutates `self.span` to also include `other.span` and returns true.
|
||||||
|
///
|
||||||
|
/// Note that compatible covspans can be merged even if their underlying
|
||||||
|
/// spans are not overlapping/adjacent; any space between them will also be
|
||||||
|
/// part of the merged covspan.
|
||||||
|
fn merge_if_eligible(&mut self, other: &Self) -> bool {
|
||||||
|
if self.bcb != other.bcb {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
self.span = self.span.to(other.span);
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Compares two spans in (lo ascending, hi descending) order.
|
/// Compares two spans in (lo ascending, hi descending) order.
|
||||||
fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
|
fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
|
||||||
// First sort by span start.
|
// First sort by span start.
|
||||||
|
|
|
@ -9,6 +9,7 @@ use rustc_span::{ExpnKind, MacroKind, Span, Symbol};
|
||||||
use crate::coverage::graph::{
|
use crate::coverage::graph::{
|
||||||
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
|
||||||
};
|
};
|
||||||
|
use crate::coverage::spans::Covspan;
|
||||||
use crate::coverage::ExtractedHirInfo;
|
use crate::coverage::ExtractedHirInfo;
|
||||||
|
|
||||||
pub(crate) struct ExtractedCovspans {
|
pub(crate) struct ExtractedCovspans {
|
||||||
|
@ -306,19 +307,8 @@ impl SpanFromMir {
|
||||||
Self { span, visible_macro, bcb }
|
Self { span, visible_macro, bcb }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Splits this span into 0-2 parts:
|
pub(crate) fn into_covspan(self) -> Covspan {
|
||||||
/// - The part that is strictly before the hole span, if any.
|
let Self { span, visible_macro: _, bcb } = self;
|
||||||
/// - The part that is strictly after the hole span, if any.
|
Covspan { span, bcb }
|
||||||
pub(crate) fn split_around_hole_span(&self, hole_span: Span) -> (Option<Self>, Option<Self>) {
|
|
||||||
let before = try {
|
|
||||||
let span = self.span.trim_end(hole_span)?;
|
|
||||||
Self { span, ..*self }
|
|
||||||
};
|
|
||||||
let after = try {
|
|
||||||
let span = self.span.trim_start(hole_span)?;
|
|
||||||
Self { span, ..*self }
|
|
||||||
};
|
|
||||||
|
|
||||||
(before, after)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue