|  | 
| 1 |  | -use std::collections::VecDeque; | 
| 2 |  | -use std::iter; | 
| 3 |  | - | 
| 4 | 1 | use rustc_data_structures::fx::FxHashSet; | 
| 5 | 2 | use rustc_middle::mir; | 
| 6 | 3 | use rustc_middle::ty::TyCtxt; | 
| 7 | 4 | use rustc_span::{DesugaringKind, ExpnKind, MacroKind, Span}; | 
| 8 |  | -use tracing::{debug, debug_span, instrument}; | 
|  | 5 | +use tracing::instrument; | 
| 9 | 6 | 
 | 
| 10 | 7 | use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; | 
| 11 | 8 | use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir}; | 
| @@ -83,24 +80,17 @@ pub(super) fn extract_refined_covspans<'tcx>( | 
| 83 | 80 |     holes.sort_by(|a, b| compare_spans(a.span, b.span)); | 
| 84 | 81 |     holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); | 
| 85 | 82 | 
 | 
| 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); | 
| 91 | 85 | 
 | 
| 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)); | 
| 94 | 89 | 
 | 
| 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 | +    })); | 
| 104 | 94 | } | 
| 105 | 95 | 
 | 
| 106 | 96 | /// 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>) | 
| 142 | 132 |     } | 
| 143 | 133 | } | 
| 144 | 134 | 
 | 
| 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. | 
| 149 | 136 | /// | 
| 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())); | 
| 156 | 141 |     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 | +            } | 
| 157 | 153 | 
 | 
| 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 | +        } | 
| 177 | 156 | 
 | 
| 178 |  | -    buckets | 
| 179 |  | -} | 
|  | 157 | +        // No holes left, so this covspan doesn't overlap with any holes. | 
|  | 158 | +        false | 
|  | 159 | +    }; | 
| 180 | 160 | 
 | 
| 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)); | 
| 188 | 162 | } | 
| 189 | 163 | 
 | 
| 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" | 
| 191 | 165 | /// those spans by removing spans that overlap in unwanted ways. | 
| 192 | 166 | #[instrument(level = "debug")] | 
| 193 | 167 | fn remove_unwanted_overlapping_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> { | 
|  | 
0 commit comments