1
- use rustc_middle::bug;
2
1
use rustc_middle::mir;
3
- use rustc_span::{BytePos, Span} ;
2
+ use rustc_span::Span;
4
3
5
4
use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
6
5
use crate::coverage::mappings;
@@ -23,66 +22,14 @@ pub(super) fn extract_refined_covspans(
23
22
let sorted_span_buckets =
24
23
from_mir::mir_to_initial_sorted_coverage_spans(mir_body, hir_info, basic_coverage_blocks);
25
24
for bucket in sorted_span_buckets {
26
- let refined_spans = SpansRefiner:: refine_sorted_spans(bucket);
25
+ let refined_spans = refine_sorted_spans(bucket);
27
26
code_mappings.extend(refined_spans.into_iter().map(|RefinedCovspan { span, bcb }| {
28
27
// Each span produced by the refiner represents an ordinary code region.
29
28
mappings::CodeMapping { span, bcb }
30
29
}));
31
30
}
32
31
}
33
32
34
- #[derive(Debug)]
35
- struct CurrCovspan {
36
- span: Span,
37
- bcb: BasicCoverageBlock,
38
- }
39
-
40
- impl CurrCovspan {
41
- fn new(span: Span, bcb: BasicCoverageBlock) -> Self {
42
- Self { span, bcb }
43
- }
44
-
45
- fn into_prev(self) -> PrevCovspan {
46
- let Self { span, bcb } = self;
47
- PrevCovspan { span, bcb, merged_spans: vec![span] }
48
- }
49
- }
50
-
51
- #[derive(Debug)]
52
- struct PrevCovspan {
53
- span: Span,
54
- bcb: BasicCoverageBlock,
55
- /// List of all the original spans from MIR that have been merged into this
56
- /// span. Mainly used to precisely skip over gaps when truncating a span.
57
- merged_spans: Vec<Span>,
58
- }
59
-
60
- impl PrevCovspan {
61
- fn is_mergeable(&self, other: &CurrCovspan) -> bool {
62
- self.bcb == other.bcb
63
- }
64
-
65
- fn merge_from(&mut self, other: &CurrCovspan) {
66
- debug_assert!(self.is_mergeable(other));
67
- self.span = self.span.to(other.span);
68
- self.merged_spans.push(other.span);
69
- }
70
-
71
- fn cutoff_statements_at(mut self, cutoff_pos: BytePos) -> Option<RefinedCovspan> {
72
- self.merged_spans.retain(|span| span.hi() <= cutoff_pos);
73
- if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() {
74
- self.span = self.span.with_hi(max_hi);
75
- }
76
-
77
- if self.merged_spans.is_empty() { None } else { Some(self.into_refined()) }
78
- }
79
-
80
- fn into_refined(self) -> RefinedCovspan {
81
- let Self { span, bcb, merged_spans: _ } = self;
82
- RefinedCovspan { span, bcb }
83
- }
84
- }
85
-
86
33
#[derive(Debug)]
87
34
struct RefinedCovspan {
88
35
span: Span,
@@ -100,164 +47,50 @@ impl RefinedCovspan {
100
47
}
101
48
}
102
49
103
- /// Converts the initial set of coverage spans (one per MIR `Statement` or `Terminator`) into a
104
- /// minimal set of coverage spans, using the BCB CFG to determine where it is safe and useful to:
105
- ///
106
- /// * Remove duplicate source code coverage regions
107
- /// * Merge spans that represent continuous (both in source code and control flow), non-branching
108
- /// execution
109
- struct SpansRefiner {
110
- /// The initial set of coverage spans, sorted by `Span` (`lo` and `hi`) and by relative
111
- /// dominance between the `BasicCoverageBlock`s of equal `Span`s.
112
- sorted_spans_iter: std::vec::IntoIter<SpanFromMir>,
113
-
114
- /// The current coverage span to compare to its `prev`, to possibly merge, discard,
115
- /// or cause `prev` to be modified or discarded.
116
- /// If `curr` is not discarded or merged, it becomes `prev` for the next iteration.
117
- some_curr: Option<CurrCovspan>,
118
-
119
- /// The coverage span from a prior iteration; typically assigned from that iteration's `curr`.
120
- /// If that `curr` was discarded, `prev` retains its value from the previous iteration.
121
- some_prev: Option<PrevCovspan>,
122
-
123
- /// The final coverage spans to add to the coverage map. A `Counter` or `Expression`
124
- /// will also be injected into the MIR for each BCB that has associated spans.
125
- refined_spans: Vec<RefinedCovspan>,
126
- }
127
-
128
- impl SpansRefiner {
129
- /// Takes the initial list of (sorted) spans extracted from MIR, and "refines"
130
- /// them by merging compatible adjacent spans, removing redundant spans,
131
- /// and carving holes in spans when they overlap in unwanted ways.
132
- fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
133
- let sorted_spans_len = sorted_spans.len();
134
- let this = Self {
135
- sorted_spans_iter: sorted_spans.into_iter(),
136
- some_curr: None,
137
- some_prev: None,
138
- refined_spans: Vec::with_capacity(sorted_spans_len),
139
- };
140
-
141
- this.to_refined_spans()
142
- }
143
-
144
- /// Iterate through the sorted coverage spans, and return the refined list of merged and
145
- /// de-duplicated spans.
146
- fn to_refined_spans(mut self) -> Vec<RefinedCovspan> {
147
- while self.next_coverage_span() {
148
- // For the first span we don't have `prev` set, so most of the
149
- // span-processing steps don't make sense yet.
150
- if self.some_prev.is_none() {
151
- debug!(" initial span");
152
- continue;
153
- }
154
-
155
- // The remaining cases assume that `prev` and `curr` are set.
156
- let prev = self.prev();
157
- let curr = self.curr();
158
-
159
- if prev.is_mergeable(curr) {
160
- debug!(?prev, "curr will be merged into prev");
161
- let curr = self.take_curr();
162
- self.prev_mut().merge_from(&curr);
163
- } else if prev.span.hi() <= curr.span.lo() {
164
- debug!(
165
- " different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}",
166
- );
167
- let prev = self.take_prev().into_refined();
168
- self.refined_spans.push(prev);
169
- } else {
170
- self.cutoff_prev_at_overlapping_curr();
171
- }
172
- }
173
-
174
- // There is usually a final span remaining in `prev` after the loop ends,
175
- // so add it to the output as well.
176
- if let Some(prev) = self.some_prev.take() {
177
- debug!(" AT END, adding last prev={prev:?}");
178
- self.refined_spans.push(prev.into_refined());
179
- }
180
-
181
- // Do one last merge pass, to simplify the output.
182
- self.refined_spans.dedup_by(|b, a| {
183
- if a.is_mergeable(b) {
184
- debug!(?a, ?b, "merging list-adjacent refined spans");
185
- a.merge_from(b);
186
- true
187
- } else {
50
+ /// Takes one of the buckets of (sorted) spans extracted from MIR, and "refines"
51
+ /// those spans by removing spans that overlap in unwanted ways, and by merging
52
+ /// compatible adjacent spans.
53
+ #[instrument(level = "debug")]
54
+ fn refine_sorted_spans(sorted_spans: Vec<SpanFromMir>) -> Vec<RefinedCovspan> {
55
+ // Holds spans that have been read from the input vector, but haven't yet
56
+ // been committed to the output vector.
57
+ let mut pending = vec![];
58
+ let mut refined = vec![];
59
+
60
+ for covspan in sorted_spans {
61
+ pending.retain(|prev: &SpanFromMir| {
62
+ if prev.span.hi() <= covspan.span.lo() {
63
+ // There's no overlap between the previous/current covspans,
64
+ // so move the previous one into the refined list.
65
+ refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb });
188
66
false
67
+ } else {
68
+ // Otherwise, retain the previous covspan only if it has the
69
+ // same BCB. This tends to discard long outer spans that enclose
70
+ // smaller inner spans with different control flow.
71
+ prev.bcb == covspan.bcb
189
72
}
190
73
});
191
-
192
- self.refined_spans
193
- }
194
-
195
- #[track_caller]
196
- fn curr(&self) -> &CurrCovspan {
197
- self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)"))
198
- }
199
-
200
- /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the
201
- /// `curr` coverage span.
202
- #[track_caller]
203
- fn take_curr(&mut self) -> CurrCovspan {
204
- self.some_curr.take().unwrap_or_else(|| bug!("some_curr is None (take_curr)"))
205
- }
206
-
207
- #[track_caller]
208
- fn prev(&self) -> &PrevCovspan {
209
- self.some_prev.as_ref().unwrap_or_else(|| bug!("some_prev is None (prev)"))
210
- }
211
-
212
- #[track_caller]
213
- fn prev_mut(&mut self) -> &mut PrevCovspan {
214
- self.some_prev.as_mut().unwrap_or_else(|| bug!("some_prev is None (prev_mut)"))
74
+ pending.push(covspan);
215
75
}
216
76
217
- #[track_caller]
218
- fn take_prev(&mut self) -> PrevCovspan {
219
- self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)"))
77
+ // Drain the rest of the pending list into the refined list.
78
+ for prev in pending {
79
+ refined.push(RefinedCovspan { span: prev.span, bcb: prev.bcb });
220
80
}
221
81
222
- /// Advance `prev` to `curr` (if any), and `curr` to the next coverage span in sorted order.
223
- fn next_coverage_span(&mut self) -> bool {
224
- if let Some(curr) = self.some_curr.take() {
225
- self.some_prev = Some(curr.into_prev());
226
- }
227
- if let Some(SpanFromMir { span, bcb, .. }) = self.sorted_spans_iter.next() {
228
- // This code only sees sorted spans after hole-carving, so there should
229
- // be no way for `curr` to start before `prev`.
230
- if let Some(prev) = &self.some_prev {
231
- debug_assert!(prev.span.lo() <= span.lo());
232
- }
233
- self.some_curr = Some(CurrCovspan::new(span, bcb));
234
- debug!(?self.some_prev, ?self.some_curr, "next_coverage_span");
82
+ // Do one last merge pass, to simplify the output.
83
+ debug!(?refined, "before merge");
84
+ refined.dedup_by(|b, a| {
85
+ if a.is_mergeable(b) {
86
+ debug!(?a, ?b, "merging list-adjacent refined spans");
87
+ a.merge_from(b);
235
88
true
236
89
} else {
237
90
false
238
91
}
239
- }
92
+ });
93
+ debug!(?refined, "after merge");
240
94
241
- /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_
242
- /// statements that end before `curr.lo()` (if any), and add the portion of the
243
- /// combined span for those statements. Any other statements have overlapping spans
244
- /// that can be ignored because `curr` and/or other upcoming statements/spans inside
245
- /// the overlap area will produce their own counters. This disambiguation process
246
- /// avoids injecting multiple counters for overlapping spans, and the potential for
247
- /// double-counting.
248
- fn cutoff_prev_at_overlapping_curr(&mut self) {
249
- debug!(
250
- " different bcbs, overlapping spans, so ignore/drop pending and only add prev \
251
- if it has statements that end before curr; prev={:?}",
252
- self.prev()
253
- );
254
-
255
- let curr_span = self.curr().span;
256
- if let Some(prev) = self.take_prev().cutoff_statements_at(curr_span.lo()) {
257
- debug!("after cutoff, adding {prev:?}");
258
- self.refined_spans.push(prev);
259
- } else {
260
- debug!("prev was eliminated by cutoff");
261
- }
262
- }
95
+ refined
263
96
}
0 commit comments