rustc_mir_transform/coverage/
mappings.rs

1use rustc_index::IndexVec;
2use rustc_middle::mir::coverage::{BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind};
3use rustc_middle::mir::{self, BasicBlock, StatementKind};
4use rustc_middle::ty::TyCtxt;
5use rustc_span::Span;
6
7use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph};
8use crate::coverage::hir_info::ExtractedHirInfo;
9use crate::coverage::spans::extract_refined_covspans;
10use crate::coverage::unexpand::unexpand_into_body_span;
11
12/// Associates an ordinary executable code span with its corresponding BCB.
13#[derive(Debug)]
14pub(super) struct CodeMapping {
15    pub(super) span: Span,
16    pub(super) bcb: BasicCoverageBlock,
17}
18
19#[derive(Debug)]
20pub(super) struct BranchPair {
21    pub(super) span: Span,
22    pub(super) true_bcb: BasicCoverageBlock,
23    pub(super) false_bcb: BasicCoverageBlock,
24}
25
26#[derive(Default)]
27pub(super) struct ExtractedMappings {
28    pub(super) code_mappings: Vec<CodeMapping>,
29    pub(super) branch_pairs: Vec<BranchPair>,
30}
31
32/// Extracts coverage-relevant spans from MIR, and associates them with
33/// their corresponding BCBs.
34pub(super) fn extract_all_mapping_info_from_mir<'tcx>(
35    tcx: TyCtxt<'tcx>,
36    mir_body: &mir::Body<'tcx>,
37    hir_info: &ExtractedHirInfo,
38    graph: &CoverageGraph,
39) -> ExtractedMappings {
40    let mut code_mappings = vec![];
41    let mut branch_pairs = vec![];
42
43    // Extract ordinary code mappings from MIR statement/terminator spans.
44    extract_refined_covspans(tcx, mir_body, hir_info, graph, &mut code_mappings);
45
46    branch_pairs.extend(extract_branch_pairs(mir_body, hir_info, graph));
47
48    ExtractedMappings { code_mappings, branch_pairs }
49}
50
51fn resolve_block_markers(
52    coverage_info_hi: &CoverageInfoHi,
53    mir_body: &mir::Body<'_>,
54) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
55    let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
56        None,
57        coverage_info_hi.num_block_markers,
58    );
59
60    // Fill out the mapping from block marker IDs to their enclosing blocks.
61    for (bb, data) in mir_body.basic_blocks.iter_enumerated() {
62        for statement in &data.statements {
63            if let StatementKind::Coverage(CoverageKind::BlockMarker { id }) = statement.kind {
64                block_markers[id] = Some(bb);
65            }
66        }
67    }
68
69    block_markers
70}
71
72pub(super) fn extract_branch_pairs(
73    mir_body: &mir::Body<'_>,
74    hir_info: &ExtractedHirInfo,
75    graph: &CoverageGraph,
76) -> Vec<BranchPair> {
77    let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return vec![] };
78
79    let block_markers = resolve_block_markers(coverage_info_hi, mir_body);
80
81    coverage_info_hi
82        .branch_spans
83        .iter()
84        .filter_map(|&BranchSpan { span: raw_span, true_marker, false_marker }| {
85            // For now, ignore any branch span that was introduced by
86            // expansion. This makes things like assert macros less noisy.
87            if !raw_span.ctxt().outer_expn_data().is_root() {
88                return None;
89            }
90            let span = unexpand_into_body_span(raw_span, hir_info.body_span)?;
91
92            let bcb_from_marker = |marker: BlockMarkerId| graph.bcb_from_bb(block_markers[marker]?);
93
94            let true_bcb = bcb_from_marker(true_marker)?;
95            let false_bcb = bcb_from_marker(false_marker)?;
96
97            Some(BranchPair { span, true_bcb, false_bcb })
98        })
99        .collect::<Vec<_>>()
100}