|
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}; |
@@ -78,24 +75,17 @@ pub(super) fn extract_refined_covspans<'tcx>( |
78 | 75 | holes.sort_by(|a, b| compare_spans(a.span, b.span)); |
79 | 76 | holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b)); |
80 | 77 |
|
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); |
86 | 80 |
|
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)); |
89 | 84 |
|
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 | + })); |
99 | 89 | } |
100 | 90 |
|
101 | 91 | /// 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>) |
137 | 127 | } |
138 | 128 | } |
139 | 129 |
|
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. |
144 | 131 | /// |
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())); |
151 | 136 | 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 | + } |
152 | 148 |
|
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 | + } |
172 | 151 |
|
173 | | - buckets |
174 | | -} |
| 152 | + // No holes left, so this covspan doesn't overlap with any holes. |
| 153 | + false |
| 154 | + }; |
175 | 155 |
|
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)); |
183 | 157 | } |
184 | 158 |
|
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" |
186 | 160 | /// those spans by removing spans that overlap in unwanted ways. |
187 | 161 | #[instrument(level = "debug")] |
188 | 162 | fn remove_unwanted_overlapping_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> { |
|
0 commit comments