| use rustc_index::IndexVec; | |
| use rustc_middle::mir::coverage::{ | |
| BlockMarkerId, BranchSpan, CoverageInfoHi, CoverageKind, Mapping, MappingKind, | |
| }; | |
| use rustc_middle::mir::{self, BasicBlock, StatementKind}; | |
| use rustc_middle::ty::TyCtxt; | |
| use rustc_span::ExpnKind; | |
| use crate::coverage::expansion::{self, ExpnTree}; | |
| use crate::coverage::graph::CoverageGraph; | |
| use crate::coverage::hir_info::ExtractedHirInfo; | |
| use crate::coverage::spans::extract_refined_covspans; | |
| /// Indicates why mapping extraction failed, for debug-logging purposes. | |
| #[derive(Debug)] | |
| pub(crate) enum MappingsError { | |
| NoMappings, | |
| TreeSortFailure, | |
| } | |
| #[derive(Default)] | |
| pub(crate) struct ExtractedMappings { | |
| pub(crate) mappings: Vec<Mapping>, | |
| } | |
| /// Extracts coverage-relevant spans from MIR, and uses them to create | |
| /// coverage mapping data for inclusion in MIR. | |
| pub(crate) fn extract_mappings_from_mir<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| mir_body: &mir::Body<'tcx>, | |
| hir_info: &ExtractedHirInfo, | |
| graph: &CoverageGraph, | |
| ) -> Result<ExtractedMappings, MappingsError> { | |
| let expn_tree = expansion::build_expn_tree(mir_body, hir_info, graph)?; | |
| let mut mappings = vec![]; | |
| // Extract ordinary code mappings from MIR statement/terminator spans. | |
| extract_refined_covspans(tcx, hir_info, graph, &expn_tree, &mut mappings); | |
| extract_branch_mappings(mir_body, hir_info, graph, &expn_tree, &mut mappings); | |
| if mappings.is_empty() { | |
| tracing::debug!("no mappings were extracted"); | |
| return Err(MappingsError::NoMappings); | |
| } | |
| Ok(ExtractedMappings { mappings }) | |
| } | |
| fn resolve_block_markers( | |
| coverage_info_hi: &CoverageInfoHi, | |
| mir_body: &mir::Body<'_>, | |
| ) -> IndexVec<BlockMarkerId, Option<BasicBlock>> { | |
| let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n( | |
| None, | |
| coverage_info_hi.num_block_markers, | |
| ); | |
| // Fill out the mapping from block marker IDs to their enclosing blocks. | |
| for (bb, data) in mir_body.basic_blocks.iter_enumerated() { | |
| for statement in &data.statements { | |
| if let StatementKind::Coverage(CoverageKind::BlockMarker { id }) = statement.kind { | |
| block_markers[id] = Some(bb); | |
| } | |
| } | |
| } | |
| block_markers | |
| } | |
| fn extract_branch_mappings( | |
| mir_body: &mir::Body<'_>, | |
| hir_info: &ExtractedHirInfo, | |
| graph: &CoverageGraph, | |
| expn_tree: &ExpnTree, | |
| mappings: &mut Vec<Mapping>, | |
| ) { | |
| let Some(coverage_info_hi) = mir_body.coverage_info_hi.as_deref() else { return }; | |
| let block_markers = resolve_block_markers(coverage_info_hi, mir_body); | |
| // For now, ignore any branch span that was introduced by | |
| // expansion. This makes things like assert macros less noisy. | |
| let Some(node) = expn_tree.get(hir_info.body_span.ctxt().outer_expn()) else { return }; | |
| if node.expn_kind != ExpnKind::Root { | |
| return; | |
| } | |
| mappings.extend(node.branch_spans.iter().filter_map( | |
| |&BranchSpan { span, true_marker, false_marker }| try { | |
| let bcb_from_marker = |marker: BlockMarkerId| graph.bcb_from_bb(block_markers[marker]?); | |
| let true_bcb = bcb_from_marker(true_marker)?; | |
| let false_bcb = bcb_from_marker(false_marker)?; | |
| Mapping { span, kind: MappingKind::Branch { true_bcb, false_bcb } } | |
| }, | |
| )); | |
| } |