1- use rustc_middle:: bug;
21use rustc_middle:: mir;
3- use rustc_span:: { BytePos , Span } ;
2+ use rustc_span:: Span ;
43
54use crate :: coverage:: graph:: { BasicCoverageBlock , CoverageGraph } ;
65use crate :: coverage:: mappings;
@@ -23,66 +22,14 @@ pub(super) fn extract_refined_covspans(
2322 let sorted_span_buckets =
2423 from_mir:: mir_to_initial_sorted_coverage_spans ( mir_body, hir_info, basic_coverage_blocks) ;
2524 for bucket in sorted_span_buckets {
26- let refined_spans = SpansRefiner :: refine_sorted_spans ( bucket) ;
25+ let refined_spans = refine_sorted_spans ( bucket) ;
2726 code_mappings. extend ( refined_spans. into_iter ( ) . map ( |RefinedCovspan { span, bcb } | {
2827 // Each span produced by the refiner represents an ordinary code region.
2928 mappings:: CodeMapping { span, bcb }
3029 } ) ) ;
3130 }
3231}
3332
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-
8633#[ derive( Debug ) ]
8734struct RefinedCovspan {
8835 span : Span ,
@@ -100,164 +47,50 @@ impl RefinedCovspan {
10047 }
10148}
10249
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 } ) ;
18866 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
18972 }
19073 } ) ;
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) ;
21575 }
21676
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 } ) ;
22080 }
22181
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) ;
23588 true
23689 } else {
23790 false
23891 }
239- }
92+ } ) ;
93+ debug ! ( ?refined, "after merge" ) ;
24094
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
26396}
0 commit comments