rustc_mir_transform/coverage/
spans.rs1use rustc_data_structures::fx::FxHashSet;
2use rustc_middle::mir;
3use rustc_middle::mir::coverage::{Mapping, MappingKind, START_BCB};
4use rustc_middle::ty::TyCtxt;
5use rustc_span::source_map::SourceMap;
6use rustc_span::{BytePos, DesugaringKind, ExpnKind, MacroKind, Span};
7use tracing::instrument;
8
9use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
10use crate::coverage::hir_info::ExtractedHirInfo;
11use crate::coverage::spans::from_mir::{Hole, RawSpanFromMir, SpanFromMir};
12use crate::coverage::unexpand;
13
14mod from_mir;
15
16pub(super) fn extract_refined_covspans<'tcx>(
17 tcx: TyCtxt<'tcx>,
18 mir_body: &mir::Body<'tcx>,
19 hir_info: &ExtractedHirInfo,
20 graph: &CoverageGraph,
21 mappings: &mut Vec<Mapping>,
22) {
23 if hir_info.is_async_fn {
24 if let Some(span) = hir_info.fn_sig_span {
29 mappings.push(Mapping { span, kind: MappingKind::Code { bcb: START_BCB } })
30 }
31 return;
32 }
33
34 let &ExtractedHirInfo { body_span, .. } = hir_info;
35
36 let raw_spans = from_mir::extract_raw_spans_from_mir(mir_body, graph);
37 let mut covspans = raw_spans
38 .into_iter()
39 .filter_map(|RawSpanFromMir { raw_span, bcb }| try {
40 let (span, expn_kind) =
41 unexpand::unexpand_into_body_span_with_expn_kind(raw_span, body_span)?;
42 if span.source_equal(body_span) {
45 return None;
46 };
47 SpanFromMir { span, expn_kind, bcb }
48 })
49 .collect::<Vec<_>>();
50
51 if covspans.is_empty() {
53 return;
54 }
55
56 covspans.push(SpanFromMir::for_fn_sig(
61 hir_info.fn_sig_span.unwrap_or_else(|| body_span.shrink_to_lo()),
62 ));
63
64 covspans.sort_by(|a, b| graph.cmp_in_dominator_order(a.bcb, b.bcb));
66 remove_unwanted_expansion_spans(&mut covspans);
67 shrink_visible_macro_spans(tcx, &mut covspans);
68
69 let mut covspans = covspans.into_iter().map(SpanFromMir::into_covspan).collect::<Vec<_>>();
71
72 let compare_covspans = |a: &Covspan, b: &Covspan| {
73 compare_spans(a.span, b.span)
74 .then_with(|| graph.cmp_in_dominator_order(a.bcb, b.bcb).reverse())
76 };
77 covspans.sort_by(compare_covspans);
78
79 covspans.dedup_by(|b, a| a.span.source_equal(b.span));
84
85 let mut holes = hir_info
87 .hole_spans
88 .iter()
89 .copied()
90 .filter(|&hole_span| body_span.contains(hole_span) && body_span.eq_ctxt(hole_span))
92 .map(|span| Hole { span })
93 .collect::<Vec<_>>();
94 holes.sort_by(|a, b| compare_spans(a.span, b.span));
95 holes.dedup_by(|b, a| a.merge_if_overlapping_or_adjacent(b));
96
97 discard_spans_overlapping_holes(&mut covspans, &holes);
99
100 let mut covspans = remove_unwanted_overlapping_spans(covspans);
102
103 let source_map = tcx.sess.source_map();
105 covspans.retain_mut(|covspan| {
106 let Some(span) = ensure_non_empty_span(source_map, covspan.span) else { return false };
107 covspan.span = span;
108 true
109 });
110
111 covspans.dedup_by(|b, a| a.merge_if_eligible(b));
113
114 mappings.extend(covspans.into_iter().map(|Covspan { span, bcb }| {
115 Mapping { span, kind: MappingKind::Code { bcb } }
117 }));
118}
119
120fn remove_unwanted_expansion_spans(covspans: &mut Vec<SpanFromMir>) {
131 let mut deduplicated_spans = FxHashSet::default();
132
133 covspans.retain(|covspan| {
134 match covspan.expn_kind {
135 Some(ExpnKind::Desugaring(DesugaringKind::Await)) => {
137 deduplicated_spans.insert(covspan.span)
138 }
139 Some(ExpnKind::Macro(MacroKind::Bang, _)) => deduplicated_spans.insert(covspan.span),
140 _ => true,
142 }
143 });
144}
145
146fn shrink_visible_macro_spans(tcx: TyCtxt<'_>, covspans: &mut Vec<SpanFromMir>) {
150 let source_map = tcx.sess.source_map();
151
152 for covspan in covspans {
153 if matches!(covspan.expn_kind, Some(ExpnKind::Macro(MacroKind::Bang, _))) {
154 covspan.span = source_map.span_through_char(covspan.span, '!');
155 }
156 }
157}
158
159fn discard_spans_overlapping_holes(covspans: &mut Vec<Covspan>, holes: &[Hole]) {
164 debug_assert!(covspans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
165 debug_assert!(holes.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
166 debug_assert!(holes.array_windows().all(|[a, b]| !a.span.overlaps_or_adjacent(b.span)));
167
168 let mut curr_hole = 0usize;
169 let mut overlaps_hole = |covspan: &Covspan| -> bool {
170 while let Some(hole) = holes.get(curr_hole) {
171 if hole.span.hi() <= covspan.span.lo() {
174 curr_hole += 1;
175 continue;
176 }
177
178 return hole.span.overlaps(covspan.span);
179 }
180
181 false
183 };
184
185 covspans.retain(|covspan| !overlaps_hole(covspan));
186}
187
188#[instrument(level = "debug")]
191fn remove_unwanted_overlapping_spans(sorted_spans: Vec<Covspan>) -> Vec<Covspan> {
192 debug_assert!(sorted_spans.is_sorted_by(|a, b| compare_spans(a.span, b.span).is_le()));
193
194 let mut pending = vec![];
197 let mut refined = vec![];
198
199 for curr in sorted_spans {
200 pending.retain(|prev: &Covspan| {
201 if prev.span.hi() <= curr.span.lo() {
202 refined.push(prev.clone());
205 false
206 } else {
207 prev.bcb == curr.bcb
211 }
212 });
213 pending.push(curr);
214 }
215
216 refined.extend(pending);
218 refined
219}
220
221#[derive(Clone, Debug)]
222struct Covspan {
223 span: Span,
224 bcb: BasicCoverageBlock,
225}
226
227impl Covspan {
228 fn merge_if_eligible(&mut self, other: &Self) -> bool {
234 let eligible_for_merge =
235 |a: &Self, b: &Self| (a.bcb == b.bcb) && a.span.overlaps_or_adjacent(b.span);
236
237 if eligible_for_merge(self, other) {
238 self.span = self.span.to(other.span);
239 true
240 } else {
241 false
242 }
243 }
244}
245
246fn compare_spans(a: Span, b: Span) -> std::cmp::Ordering {
248 Ord::cmp(&a.lo(), &b.lo())
250 .then_with(|| Ord::cmp(&a.hi(), &b.hi()).reverse())
256}
257
258fn ensure_non_empty_span(source_map: &SourceMap, span: Span) -> Option<Span> {
259 if !span.is_empty() {
260 return Some(span);
261 }
262
263 source_map
265 .span_to_source(span, |src, start, end| try {
266 if src.as_bytes().get(end).copied() == Some(b'{') {
271 Some(span.with_hi(span.hi() + BytePos(1)))
272 } else if start > 0 && src.as_bytes()[start - 1] == b'}' {
273 Some(span.with_lo(span.lo() - BytePos(1)))
274 } else {
275 None
276 }
277 })
278 .ok()?
279}