Skip to main content

rustc_mir_transform/coverage/
from_mir.rs

1use rustc_middle::mir::coverage::{CoverageKind, PointKind};
2use rustc_middle::mir::{self, Statement, StatementKind};
3use rustc_span::Span;
4
5use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
6use crate::coverage::hir_info::ExtractedHirInfo;
7
8#[derive(Debug)]
9pub(crate) struct RawSpanFromMir {
10    /// A span that has been extracted from a MIR marker statement, but
11    /// hasn't been "unexpanded", so it might not lie within the function body
12    /// span and might be part of an expansion with a different context.
13    pub(crate) raw_span: Span,
14    pub(crate) bcb: BasicCoverageBlock,
15}
16
17/// Generates an initial set of coverage spans from marker statements in the function's
18/// MIR body, each associated with its corresponding node in the coverage graph.
19///
20/// FIXME(Zalathar): This extraction is currently in a transitional state, since we're
21/// no longer trying to heuristically recover meaningful spans from MIR soup, but we
22/// haven't yet fully embraced the possibilities of HIR-aware analysis.
23pub(crate) fn extract_raw_spans_from_mir<'tcx>(
24    mir_body: &mir::Body<'tcx>,
25    hir_info: &ExtractedHirInfo,
26    graph: &CoverageGraph,
27) -> Vec<RawSpanFromMir> {
28    let mut raw_spans = vec![];
29
30    // We only care about blocks that are part of the coverage graph.
31    for (bcb, bcb_data) in graph.iter_enumerated() {
32        // A coverage graph node can consist of multiple basic blocks.
33        for &bb in &bcb_data.basic_blocks {
34            let statements = mir_body[bb].statements.iter();
35            raw_spans.extend(
36                statements
37                    .filter_map(|stmt| filtered_statement_span(hir_info, stmt))
38                    .map(|raw_span: Span| RawSpanFromMir { raw_span, bcb }),
39            );
40        }
41    }
42
43    raw_spans
44}
45
46/// If the MIR `Statement` has a span contributive to computing coverage spans,
47/// return it; otherwise return `None`.
48fn filtered_statement_span<'tcx>(
49    hir_info: &ExtractedHirInfo,
50    statement: &Statement<'tcx>,
51) -> Option<Span> {
52    let StatementKind::Coverage(CoverageKind::Point { point_kind, hir_id }) = statement.kind else {
53        return None;
54    };
55    match point_kind {
56        // These PointKind variants contribute to normal code spans.
57        // (Other variants added in the future might want to return None here.)
58        PointKind::Expr | PointKind::ImplicitElse | PointKind::FunctionEnd => {}
59    }
60    if hir_info.nodes_to_ignore.contains(&hir_id) {
61        return None;
62    }
63
64    Some(statement.source_info.span)
65}