frontend 0.4.0

rustc's frontend with no LLVM and no std: parsing through MIR, as a library
// `#![no_std]`: these arrive with the standard prelude and name no path, so a `std::`
// search cannot see them - and a `#[derive]` can use them without the name appearing
// in this file at all, which is why they are not trimmed by inspection.
use alloc::borrow::ToOwned;
use alloc::boxed::Box;
use alloc::format;
use alloc::string::{String, ToString};
use alloc::vec;
use alloc::vec::Vec;

use crate::rustc_index::IndexVec;
use crate::rustc_middle::mir::coverage::{
    BlockMarkerId, BranchSpan, CoverageEarlyInfo, CoverageKind, Mapping, MappingKind,
};
use crate::rustc_middle::mir::{self, BasicBlock, StatementKind};
use crate::rustc_middle::ty::TyCtxt;
use crate::rustc_span::ExpnKind;

use crate::rustc_mir_transform::coverage::expansion::{self, ExpnTree};
use crate::rustc_mir_transform::coverage::graph::CoverageGraph;
use crate::rustc_mir_transform::coverage::hir_info::ExtractedHirInfo;
use crate::rustc_mir_transform::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(
    early_info: &CoverageEarlyInfo,
    mir_body: &mir::Body<'_>,
) -> IndexVec<BlockMarkerId, Option<BasicBlock>> {
    let mut block_markers = IndexVec::<BlockMarkerId, Option<BasicBlock>>::from_elem_n(
        None,
        early_info.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(early_info) = mir_body.coverage_early_info.as_deref() else { return };
    let block_markers = resolve_block_markers(early_info, 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()) else { return };
    if node.expn_kind != ExpnKind::Root {
        return;
    }

    mappings.extend(node.branch_spans.iter().filter_map(
        // The closure returns `Option` directly in place of an unstable `try {}` block.
        |&BranchSpan { span, true_marker, false_marker }| {
            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)?;

            Some(Mapping { span, kind: MappingKind::Branch { true_bcb, false_bcb } })
        },
    ));
}