rustc_mir_transform/coverage/spans/
from_mir.rs
1use rustc_middle::bug;
2use rustc_middle::mir::coverage::CoverageKind;
3use rustc_middle::mir::{
4 self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
5};
6use rustc_span::{ExpnKind, Span};
7
8use crate::coverage::ExtractedHirInfo;
9use crate::coverage::graph::{
10 BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB,
11};
12use crate::coverage::spans::Covspan;
13use crate::coverage::unexpand::unexpand_into_body_span_with_expn_kind;
14
15pub(crate) struct ExtractedCovspans {
16 pub(crate) covspans: Vec<SpanFromMir>,
17}
18
19pub(crate) fn extract_covspans_from_mir(
23 mir_body: &mir::Body<'_>,
24 hir_info: &ExtractedHirInfo,
25 graph: &CoverageGraph,
26) -> ExtractedCovspans {
27 let &ExtractedHirInfo { body_span, .. } = hir_info;
28
29 let mut covspans = vec![];
30
31 for (bcb, bcb_data) in graph.iter_enumerated() {
32 bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data, &mut covspans);
33 }
34
35 if !covspans.is_empty() {
37 let fn_sig_span = hir_info.fn_sig_span_extended.unwrap_or_else(|| body_span.shrink_to_lo());
41 covspans.push(SpanFromMir::for_fn_sig(fn_sig_span));
42 }
43
44 ExtractedCovspans { covspans }
45}
46
47fn bcb_to_initial_coverage_spans<'a, 'tcx>(
53 mir_body: &'a mir::Body<'tcx>,
54 body_span: Span,
55 bcb: BasicCoverageBlock,
56 bcb_data: &'a BasicCoverageBlockData,
57 initial_covspans: &mut Vec<SpanFromMir>,
58) {
59 for &bb in &bcb_data.basic_blocks {
60 let data = &mir_body[bb];
61
62 let unexpand = move |expn_span| {
63 unexpand_into_body_span_with_expn_kind(expn_span, body_span)
64 .filter(|(span, _)| !span.source_equal(body_span))
67 };
68
69 let mut extract_statement_span = |statement| {
70 let expn_span = filtered_statement_span(statement)?;
71 let (span, expn_kind) = unexpand(expn_span)?;
72
73 initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
74 Some(())
75 };
76 for statement in data.statements.iter() {
77 extract_statement_span(statement);
78 }
79
80 let mut extract_terminator_span = |terminator| {
81 let expn_span = filtered_terminator_span(terminator)?;
82 let (span, expn_kind) = unexpand(expn_span)?;
83
84 initial_covspans.push(SpanFromMir::new(span, expn_kind, bcb));
85 Some(())
86 };
87 extract_terminator_span(data.terminator());
88 }
89}
90
91fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
94 match statement.kind {
95 StatementKind::StorageLive(_)
98 | StatementKind::StorageDead(_)
99 | StatementKind::ConstEvalCounter
100 | StatementKind::BackwardIncompatibleDropHint { .. }
101 | StatementKind::Nop => None,
102
103 StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
120
121 StatementKind::FakeRead(_)
123 | StatementKind::Intrinsic(..)
124 | StatementKind::Coverage(
125 CoverageKind::SpanMarker,
127 )
128 | StatementKind::Assign(_)
129 | StatementKind::SetDiscriminant { .. }
130 | StatementKind::Deinit(..)
131 | StatementKind::Retag(_, _)
132 | StatementKind::PlaceMention(..)
133 | StatementKind::AscribeUserType(_, _) => Some(statement.source_info.span),
134
135 StatementKind::Coverage(CoverageKind::BlockMarker { .. }) => None,
137
138 StatementKind::Coverage(
140 CoverageKind::VirtualCounter { .. }
141 | CoverageKind::CondBitmapUpdate { .. }
142 | CoverageKind::TestVectorBitmapUpdate { .. },
143 ) => bug!(
144 "Unexpected coverage statement found during coverage instrumentation: {statement:?}"
145 ),
146 }
147}
148
149fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
152 match terminator.kind {
153 TerminatorKind::Unreachable | TerminatorKind::Assert { .. }
160 | TerminatorKind::Drop { .. }
161 | TerminatorKind::SwitchInt { .. }
162 | TerminatorKind::FalseEdge { .. }
164 | TerminatorKind::Goto { .. } => None,
165
166 TerminatorKind::Call { ref func, .. }
168 | TerminatorKind::TailCall { ref func, .. } => {
169 let mut span = terminator.source_info.span;
170 if let mir::Operand::Constant(box constant) = func {
171 if constant.span.lo() > span.lo() {
172 span = span.with_lo(constant.span.lo());
173 }
174 }
175 Some(span)
176 }
177
178 TerminatorKind::UnwindResume
180 | TerminatorKind::UnwindTerminate(_)
181 | TerminatorKind::Return
182 | TerminatorKind::Yield { .. }
183 | TerminatorKind::CoroutineDrop
184 | TerminatorKind::FalseUnwind { .. }
185 | TerminatorKind::InlineAsm { .. } => {
186 Some(terminator.source_info.span)
187 }
188 }
189}
190
191#[derive(Debug)]
192pub(crate) struct Hole {
193 pub(crate) span: Span,
194}
195
196impl Hole {
197 pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool {
198 if !self.span.overlaps_or_adjacent(other.span) {
199 return false;
200 }
201
202 self.span = self.span.to(other.span);
203 true
204 }
205}
206
207#[derive(Debug)]
208pub(crate) struct SpanFromMir {
209 pub(crate) span: Span,
217 pub(crate) expn_kind: Option<ExpnKind>,
218 pub(crate) bcb: BasicCoverageBlock,
219}
220
221impl SpanFromMir {
222 fn for_fn_sig(fn_sig_span: Span) -> Self {
223 Self::new(fn_sig_span, None, START_BCB)
224 }
225
226 pub(crate) fn new(span: Span, expn_kind: Option<ExpnKind>, bcb: BasicCoverageBlock) -> Self {
227 Self { span, expn_kind, bcb }
228 }
229
230 pub(crate) fn into_covspan(self) -> Covspan {
231 let Self { span, expn_kind: _, bcb } = self;
232 Covspan { span, bcb }
233 }
234}