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 curr in sorted_spans {
61
+ pending. retain ( |prev : & SpanFromMir | {
62
+ if prev. span . hi ( ) <= curr. 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 == curr. 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 ( curr) ;
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