rustc_mir_transform/coverage/spans/
from_mir.rs

1use std::iter;
2
3use rustc_middle::bug;
4use rustc_middle::mir::coverage::CoverageKind;
5use rustc_middle::mir::{
6    self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind,
7};
8use rustc_span::{ExpnKind, Span};
9
10use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
11use crate::coverage::spans::Covspan;
12
13#[derive(Debug)]
14pub(crate) struct RawSpanFromMir {
15    /// A span that has been extracted from a MIR statement/terminator, but
16    /// hasn't been "unexpanded", so it might not lie within the function body
17    /// span and might be part of an expansion with a different context.
18    pub(crate) raw_span: Span,
19    pub(crate) bcb: BasicCoverageBlock,
20}
21
22/// Generates an initial set of coverage spans from the statements and
23/// terminators in the function's MIR body, each associated with its
24/// corresponding node in the coverage graph.
25///
26/// This is necessarily an inexact process, because MIR isn't designed to
27/// capture source spans at the level of detail we would want for coverage,
28/// but it's good enough to be better than nothing.
29pub(crate) fn extract_raw_spans_from_mir<'tcx>(
30    mir_body: &mir::Body<'tcx>,
31    graph: &CoverageGraph,
32) -> Vec<RawSpanFromMir> {
33    let mut raw_spans = vec![];
34
35    // We only care about blocks that are part of the coverage graph.
36    for (bcb, bcb_data) in graph.iter_enumerated() {
37        let make_raw_span = |raw_span: Span| RawSpanFromMir { raw_span, bcb };
38
39        // A coverage graph node can consist of multiple basic blocks.
40        for &bb in &bcb_data.basic_blocks {
41            let bb_data = &mir_body[bb];
42
43            let statements = bb_data.statements.iter();
44            raw_spans.extend(statements.filter_map(filtered_statement_span).map(make_raw_span));
45
46            // There's only one terminator, but wrap it in an iterator to
47            // mirror the handling of statements.
48            let terminator = iter::once(bb_data.terminator());
49            raw_spans.extend(terminator.filter_map(filtered_terminator_span).map(make_raw_span));
50        }
51    }
52
53    raw_spans
54}
55
56/// If the MIR `Statement` has a span contributive to computing coverage spans,
57/// return it; otherwise return `None`.
58fn filtered_statement_span(statement: &Statement<'_>) -> Option<Span> {
59    match statement.kind {
60        // These statements have spans that are often outside the scope of the executed source code
61        // for their parent `BasicBlock`.
62        StatementKind::StorageLive(_)
63        | StatementKind::StorageDead(_)
64        | StatementKind::ConstEvalCounter
65        | StatementKind::BackwardIncompatibleDropHint { .. }
66        | StatementKind::Nop => None,
67
68        // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead`
69        // statements be more consistent?
70        //
71        // FakeReadCause::ForGuardBinding, in this example:
72        //     match somenum {
73        //         x if x < 1 => { ... }
74        //     }...
75        // The BasicBlock within the match arm code included one of these statements, but the span
76        // for it covered the `1` in this source. The actual statements have nothing to do with that
77        // source span:
78        //     FakeRead(ForGuardBinding, _4);
79        // where `_4` is:
80        //     _4 = &_1; (at the span for the first `x`)
81        // and `_1` is the `Place` for `somenum`.
82        //
83        // If and when the Issue is resolved, remove this special case match pattern:
84        StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None,
85
86        // Retain spans from most other statements.
87        StatementKind::FakeRead(_)
88        | StatementKind::Intrinsic(..)
89        | StatementKind::Coverage(
90            // The purpose of `SpanMarker` is to be matched and accepted here.
91            CoverageKind::SpanMarker,
92        )
93        | StatementKind::Assign(_)
94        | StatementKind::SetDiscriminant { .. }
95        | StatementKind::Deinit(..)
96        | StatementKind::Retag(_, _)
97        | StatementKind::PlaceMention(..)
98        | StatementKind::AscribeUserType(_, _) => Some(statement.source_info.span),
99
100        // Block markers are used for branch coverage, so ignore them here.
101        StatementKind::Coverage(CoverageKind::BlockMarker { .. }) => None,
102
103        // These coverage statements should not exist prior to coverage instrumentation.
104        StatementKind::Coverage(
105            CoverageKind::VirtualCounter { .. }
106            | CoverageKind::CondBitmapUpdate { .. }
107            | CoverageKind::TestVectorBitmapUpdate { .. },
108        ) => bug!(
109            "Unexpected coverage statement found during coverage instrumentation: {statement:?}"
110        ),
111    }
112}
113
114/// If the MIR `Terminator` has a span contributive to computing coverage spans,
115/// return it; otherwise return `None`.
116fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option<Span> {
117    match terminator.kind {
118        // These terminators have spans that don't positively contribute to computing a reasonable
119        // span of actually executed source code. (For example, SwitchInt terminators extracted from
120        // an `if condition { block }` has a span that includes the executed block, if true,
121        // but for coverage, the code region executed, up to *and* through the SwitchInt,
122        // actually stops before the if's block.)
123        TerminatorKind::Unreachable // Unreachable blocks are not connected to the MIR CFG
124        | TerminatorKind::Assert { .. }
125        | TerminatorKind::Drop { .. }
126        | TerminatorKind::SwitchInt { .. }
127        // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
128        | TerminatorKind::FalseEdge { .. }
129        | TerminatorKind::Goto { .. } => None,
130
131        // Call `func` operand can have a more specific span when part of a chain of calls
132        TerminatorKind::Call { ref func, .. }
133        | TerminatorKind::TailCall { ref func, .. } => {
134            let mut span = terminator.source_info.span;
135            if let mir::Operand::Constant(box constant) = func {
136                if constant.span.lo() > span.lo() {
137                    span = span.with_lo(constant.span.lo());
138                }
139            }
140            Some(span)
141        }
142
143        // Retain spans from all other terminators
144        TerminatorKind::UnwindResume
145        | TerminatorKind::UnwindTerminate(_)
146        | TerminatorKind::Return
147        | TerminatorKind::Yield { .. }
148        | TerminatorKind::CoroutineDrop
149        | TerminatorKind::FalseUnwind { .. }
150        | TerminatorKind::InlineAsm { .. } => {
151            Some(terminator.source_info.span)
152        }
153    }
154}
155
156#[derive(Debug)]
157pub(crate) struct Hole {
158    pub(crate) span: Span,
159}
160
161impl Hole {
162    pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool {
163        if !self.span.overlaps_or_adjacent(other.span) {
164            return false;
165        }
166
167        self.span = self.span.to(other.span);
168        true
169    }
170}
171
172#[derive(Debug)]
173pub(crate) struct SpanFromMir {
174    /// A span that has been extracted from MIR and then "un-expanded" back to
175    /// within the current function's `body_span`. After various intermediate
176    /// processing steps, this span is emitted as part of the final coverage
177    /// mappings.
178    ///
179    /// With the exception of `fn_sig_span`, this should always be contained
180    /// within `body_span`.
181    pub(crate) span: Span,
182    pub(crate) expn_kind: Option<ExpnKind>,
183    pub(crate) bcb: BasicCoverageBlock,
184}
185
186impl SpanFromMir {
187    pub(crate) fn for_fn_sig(fn_sig_span: Span) -> Self {
188        Self::new(fn_sig_span, None, START_BCB)
189    }
190
191    pub(crate) fn new(span: Span, expn_kind: Option<ExpnKind>, bcb: BasicCoverageBlock) -> Self {
192        Self { span, expn_kind, bcb }
193    }
194
195    pub(crate) fn into_covspan(self) -> Covspan {
196        let Self { span, expn_kind: _, bcb } = self;
197        Covspan { span, bcb }
198    }
199}