use std::iter; use rustc_middle::bug; use rustc_middle::mir::coverage::CoverageKind; use rustc_middle::mir::{ self, FakeReadCause, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_span::Span; use crate::coverage::graph::{BasicCoverageBlock, CoverageGraph}; #[derive(Debug)] pub(crate) struct RawSpanFromMir { /// A span that has been extracted from a MIR statement/terminator, but /// hasn't been "unexpanded", so it might not lie within the function body /// span and might be part of an expansion with a different context. pub(crate) raw_span: Span, pub(crate) bcb: BasicCoverageBlock, } /// Generates an initial set of coverage spans from the statements and /// terminators in the function's MIR body, each associated with its /// corresponding node in the coverage graph. /// /// This is necessarily an inexact process, because MIR isn't designed to /// capture source spans at the level of detail we would want for coverage, /// but it's good enough to be better than nothing. pub(crate) fn extract_raw_spans_from_mir<'tcx>( mir_body: &mir::Body<'tcx>, graph: &CoverageGraph, ) -> Vec { let mut raw_spans = vec![]; // We only care about blocks that are part of the coverage graph. for (bcb, bcb_data) in graph.iter_enumerated() { let make_raw_span = |raw_span: Span| RawSpanFromMir { raw_span, bcb }; // A coverage graph node can consist of multiple basic blocks. for &bb in &bcb_data.basic_blocks { let bb_data = &mir_body[bb]; let statements = bb_data.statements.iter(); raw_spans.extend(statements.filter_map(filtered_statement_span).map(make_raw_span)); // There's only one terminator, but wrap it in an iterator to // mirror the handling of statements. let terminator = iter::once(bb_data.terminator()); raw_spans.extend(terminator.filter_map(filtered_terminator_span).map(make_raw_span)); } } raw_spans } /// If the MIR `Statement` has a span contributive to computing coverage spans, /// return it; otherwise return `None`. fn filtered_statement_span(statement: &Statement<'_>) -> Option { match statement.kind { // These statements have spans that are often outside the scope of the executed source code // for their parent `BasicBlock`. StatementKind::StorageLive(_) | StatementKind::StorageDead(_) | StatementKind::ConstEvalCounter | StatementKind::BackwardIncompatibleDropHint { .. } | StatementKind::Nop => None, // FIXME(#78546): MIR InstrumentCoverage - Can the source_info.span for `FakeRead` // statements be more consistent? // // FakeReadCause::ForGuardBinding, in this example: // match somenum { // x if x < 1 => { ... } // }... // The BasicBlock within the match arm code included one of these statements, but the span // for it covered the `1` in this source. The actual statements have nothing to do with that // source span: // FakeRead(ForGuardBinding, _4); // where `_4` is: // _4 = &_1; (at the span for the first `x`) // and `_1` is the `Place` for `somenum`. // // If and when the Issue is resolved, remove this special case match pattern: StatementKind::FakeRead(box (FakeReadCause::ForGuardBinding, _)) => None, // Retain spans from most other statements. StatementKind::FakeRead(_) | StatementKind::Intrinsic(..) | StatementKind::Coverage( // The purpose of `SpanMarker` is to be matched and accepted here. CoverageKind::SpanMarker, ) | StatementKind::Assign(_) | StatementKind::SetDiscriminant { .. } | StatementKind::Deinit(..) | StatementKind::Retag(_, _) | StatementKind::PlaceMention(..) | StatementKind::AscribeUserType(_, _) => Some(statement.source_info.span), // Block markers are used for branch coverage, so ignore them here. StatementKind::Coverage(CoverageKind::BlockMarker { .. }) => None, // These coverage statements should not exist prior to coverage instrumentation. StatementKind::Coverage(CoverageKind::VirtualCounter { .. }) => bug!( "Unexpected coverage statement found during coverage instrumentation: {statement:?}" ), } } /// If the MIR `Terminator` has a span contributive to computing coverage spans, /// return it; otherwise return `None`. fn filtered_terminator_span(terminator: &Terminator<'_>) -> Option { match terminator.kind { // These terminators have spans that don't positively contribute to computing a reasonable // span of actually executed source code. (For example, SwitchInt terminators extracted from // an `if condition { block }` has a span that includes the executed block, if true, // but for coverage, the code region executed, up to *and* through the SwitchInt, // actually stops before the if's block.) TerminatorKind::Unreachable | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::SwitchInt { .. } | TerminatorKind::FalseEdge { .. } | TerminatorKind::Goto { .. } => None, // Call `func` operand can have a more specific span when part of a chain of calls TerminatorKind::Call { ref func, .. } | TerminatorKind::TailCall { ref func, .. } => { let mut span = terminator.source_info.span; if let mir::Operand::Constant(constant) = func && span.contains(constant.span) { span = constant.span; } Some(span) } // Retain spans from all other terminators TerminatorKind::UnwindResume | TerminatorKind::UnwindTerminate(_) | TerminatorKind::Return | TerminatorKind::Yield { .. } | TerminatorKind::CoroutineDrop | TerminatorKind::FalseUnwind { .. } | TerminatorKind::InlineAsm { .. } => Some(terminator.source_info.span), } } #[derive(Debug)] pub(crate) struct Hole { pub(crate) span: Span, } impl Hole { pub(crate) fn merge_if_overlapping_or_adjacent(&mut self, other: &mut Self) -> bool { if !self.span.overlaps_or_adjacent(other.span) { return false; } self.span = self.span.to(other.span); true } }