Skip to main content

rustc_mir_transform/coverage/
mappings.rs

1use rustc_index::IndexVec;
2use rustc_middle::mir::coverage::{
3    BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind, Mapping, MappingKind,
4};
5use rustc_middle::mir::{self, BasicBlock, StatementKind};
6use rustc_middle::ty::TyCtxt;
7use rustc_span::ExpnKind;
8
9use crate::coverage::expansion::{self, ExpnTree};
10use crate::coverage::graph::CoverageGraph;
11use crate::coverage::hir_info::ExtractedHirInfo;
12use crate::coverage::spans::extract_refined_covspans;
13
14/// Indicates why mapping extraction failed, for debug-logging purposes.
15#[derive(Debug)]
16pub(crate) enum MappingsError {
17    NoMappings,
18    TreeSortFailure,
19}
20
21#[derive(Default)]
22pub(crate) struct ExtractedMappings {
23    pub(crate) mappings: Vec<Mapping>,
24}
25
26/// Extracts coverage-relevant spans from MIR, and uses them to create
27/// coverage mapping data for inclusion in MIR.
28pub(crate) fn extract_mappings_from_mir<'tcx>(
29    tcx: TyCtxt<'tcx>,
30    mir_body: &mir::Body<'tcx>,
31    hir_info: &ExtractedHirInfo,
32    graph: &CoverageGraph,
33) -> Result<ExtractedMappings, MappingsError> {
34    let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph)?;
35
36    let mut mappings = vec![];
37
38    // Extract ordinary code mappings from MIR statement/terminator spans.
39    extract_refined_covspans(tcx, hir_info, graph, &expn_tree, &mut mappings);
40
41    extract_branch_mappings(mir_body, hir_info, graph, &expn_tree, &mut mappings);
42
43    if mappings.is_empty() {
44        tracing::debug!("no mappings were extracted");
45        return Err(MappingsError::NoMappings);
46    }
47    Ok(ExtractedMappings { mappings })
48}
49
50fn resolve_block_markers(
51    coverage_info_hi: &CoverageInfoHi,
52    mir_body: &mir::Body<'_>,
53) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
54    let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
55        None,
56        coverage_info_hi.num_block_markers,
57    );
58
59    // Fill out the mapping from block marker IDs to their enclosing blocks.
60    for (bb, data) in mir_body.basic_blocks.iter_enumerated() {
61        for statement in &data.statements {
62            if let StatementKind::Coverage(CoverageKind::BlockMarker { id }) = statement.kind {
63                block_markers[id] = Some(bb);
64            }
65        }
66    }
67
68    block_markers
69}
70
71fn extract_branch_mappings(
72    mir_body: &mir::Body<'_>,
73    hir_info: &ExtractedHirInfo,
74    graph: &CoverageGraph,
75    expn_tree: &ExpnTree,
76    mappings: &mut Vec<Mapping>,
77) {
78    let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return };
79    let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
80
81    // For now, ignore any branch span that was introduced by
82    // expansion. This makes things like assert macros less noisy.
83    let Some(node) = expn_tree.get(hir_info.body_span.ctxt().outer_expn()) else { return };
84    if node.expn_kind != ExpnKind::Root {
85        return;
86    }
87
88    mappings.extend(node.branch_spans.iter().filter_map(
89        |&BranchSpan { span, true_marker, false_marker }| try {
90            let bcb_from_marker = |marker: BlockMarkerId| graph.bcb_from_bb(block_markers[marker]?);
91
92            let true_bcb = bcb_from_marker(true_marker)?;
93            let false_bcb = bcb_from_marker(false_marker)?;
94
95            Mapping { span, kind: MappingKind::Branch { true_bcb, false_bcb } }
96        },
97    ));
98}