mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-15 20:45:45 +03:00
7a3e5cd57e
This makes it possible for subsequent operations to iterate over all nodes, while assuming that every node occurs before all of its descendants.
99 lines
3.3 KiB
Rust
99 lines
3.3 KiB
Rust
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 } }
|
|
},
|
|
));
|
|
}
|