use std::cell::OnceCell; use rustc_data_structures::graph::WithNumNodes; use rustc_index::IndexVec; use rustc_middle::mir; use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol, DUMMY_SP}; use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB}; use crate::coverage::ExtractedHirInfo; mod from_mir; pub(super) struct CoverageSpans { /// Map from BCBs to their list of coverage spans. bcb_to_spans: IndexVec>, } impl CoverageSpans { /// Extracts coverage-relevant spans from MIR, and associates them with /// their corresponding BCBs. /// /// Returns `None` if no coverage-relevant spans could be extracted. pub(super) fn generate_coverage_spans( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &CoverageGraph, ) -> Option { let coverage_spans = CoverageSpansGenerator::generate_coverage_spans( mir_body, hir_info, basic_coverage_blocks, ); if coverage_spans.is_empty() { return None; } // Group the coverage spans by BCB, with the BCBs in sorted order. let mut bcb_to_spans = IndexVec::from_elem_n(Vec::new(), basic_coverage_blocks.num_nodes()); for CoverageSpan { bcb, span, .. } in coverage_spans { bcb_to_spans[bcb].push(span); } Some(Self { bcb_to_spans }) } pub(super) fn bcb_has_coverage_spans(&self, bcb: BasicCoverageBlock) -> bool { !self.bcb_to_spans[bcb].is_empty() } pub(super) fn spans_for_bcb(&self, bcb: BasicCoverageBlock) -> &[Span] { &self.bcb_to_spans[bcb] } } /// A BCB is deconstructed into one or more `Span`s. Each `Span` maps to a `CoverageSpan` that /// references the originating BCB and one or more MIR `Statement`s and/or `Terminator`s. /// Initially, the `Span`s come from the `Statement`s and `Terminator`s, but subsequent /// transforms can combine adjacent `Span`s and `CoverageSpan` from the same BCB, merging the /// `merged_spans` vectors, and the `Span`s to cover the extent of the combined `Span`s. /// /// Note: A span merged into another CoverageSpan may come from a `BasicBlock` that /// is not part of the `CoverageSpan` bcb if the statement was included because it's `Span` matches /// or is subsumed by the `Span` associated with this `CoverageSpan`, and it's `BasicBlock` /// `dominates()` the `BasicBlock`s in this `CoverageSpan`. #[derive(Debug, Clone)] struct CoverageSpan { pub span: Span, pub expn_span: Span, pub current_macro_or_none: OnceCell>, pub bcb: BasicCoverageBlock, /// List of all the original spans from MIR that have been merged into this /// span. Mainly used to precisely skip over gaps when truncating a span. pub merged_spans: Vec, pub is_closure: bool, } impl CoverageSpan { pub fn for_fn_sig(fn_sig_span: Span) -> Self { Self::new(fn_sig_span, fn_sig_span, START_BCB, false) } pub(super) fn new( span: Span, expn_span: Span, bcb: BasicCoverageBlock, is_closure: bool, ) -> Self { Self { span, expn_span, current_macro_or_none: Default::default(), bcb, merged_spans: vec![span], is_closure, } } pub fn merge_from(&mut self, other: &Self) { debug_assert!(self.is_mergeable(other)); self.span = self.span.to(other.span); self.merged_spans.extend_from_slice(&other.merged_spans); } pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { self.merged_spans.retain(|span| span.hi() <= cutoff_pos); if let Some(max_hi) = self.merged_spans.iter().map(|span| span.hi()).max() { self.span = self.span.with_hi(max_hi); } } #[inline] pub fn is_mergeable(&self, other: &Self) -> bool { self.is_in_same_bcb(other) && !(self.is_closure || other.is_closure) } #[inline] pub fn is_in_same_bcb(&self, other: &Self) -> bool { self.bcb == other.bcb } /// If the span is part of a macro, returns the macro name symbol. pub fn current_macro(&self) -> Option { self.current_macro_or_none .get_or_init(|| { if let ExpnKind::Macro(MacroKind::Bang, current_macro) = self.expn_span.ctxt().outer_expn_data().kind { return Some(current_macro); } None }) .map(|symbol| symbol) } /// If the span is part of a macro, and the macro is visible (expands directly to the given /// body_span), returns the macro name symbol. pub fn visible_macro(&self, body_span: Span) -> Option { let current_macro = self.current_macro()?; let parent_callsite = self.expn_span.parent_callsite()?; // In addition to matching the context of the body span, the parent callsite // must also be the source callsite, i.e. the parent must have no parent. let is_visible_macro = parent_callsite.parent_callsite().is_none() && parent_callsite.eq_ctxt(body_span); is_visible_macro.then_some(current_macro) } pub fn is_macro_expansion(&self) -> bool { self.current_macro().is_some() } } /// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a /// minimal set of `CoverageSpan`s, using the BCB CFG to determine where it is safe and useful to: /// /// * Remove duplicate source code coverage regions /// * Merge spans that represent continuous (both in source code and control flow), non-branching /// execution /// * Carve out (leave uncovered) any span that will be counted by another MIR (notably, closures) struct CoverageSpansGenerator<'a> { /// A `Span` covering the function body of the MIR (typically from left curly brace to right /// curly brace). body_span: Span, /// The BasicCoverageBlock Control Flow Graph (BCB CFG). basic_coverage_blocks: &'a CoverageGraph, /// The initial set of `CoverageSpan`s, sorted by `Span` (`lo` and `hi`) and by relative /// dominance between the `BasicCoverageBlock`s of equal `Span`s. sorted_spans_iter: std::vec::IntoIter, /// The current `CoverageSpan` to compare to its `prev`, to possibly merge, discard, force the /// discard of the `prev` (and or `pending_dups`), or keep both (with `prev` moved to /// `pending_dups`). If `curr` is not discarded or merged, it becomes `prev` for the next /// iteration. some_curr: Option, /// The original `span` for `curr`, in case `curr.span()` is modified. The `curr_original_span` /// **must not be mutated** (except when advancing to the next `curr`), even if `curr.span()` /// is mutated. curr_original_span: Span, /// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`. /// If that `curr` was discarded, `prev` retains its value from the previous iteration. some_prev: Option, /// Assigned from `curr_original_span` from the previous iteration. The `prev_original_span` /// **must not be mutated** (except when advancing to the next `prev`), even if `prev.span()` /// is mutated. prev_original_span: Span, /// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and /// no `BasicCoverageBlock` in this list dominates another `BasicCoverageBlock` in the list. /// If a new `curr` span also fits this criteria (compared to an existing list of /// `pending_dups`), that `curr` `CoverageSpan` moves to `prev` before possibly being added to /// the `pending_dups` list, on the next iteration. As a result, if `prev` and `pending_dups` /// have the same `Span`, the criteria for `pending_dups` holds for `prev` as well: a `prev` /// with a matching `Span` does not dominate any `pending_dup` and no `pending_dup` dominates a /// `prev` with a matching `Span`) pending_dups: Vec, /// The final `CoverageSpan`s to add to the coverage map. A `Counter` or `Expression` /// will also be injected into the MIR for each `CoverageSpan`. refined_spans: Vec, } impl<'a> CoverageSpansGenerator<'a> { /// Generate a minimal set of `CoverageSpan`s, each representing a contiguous code region to be /// counted. /// /// The basic steps are: /// /// 1. Extract an initial set of spans from the `Statement`s and `Terminator`s of each /// `BasicCoverageBlockData`. /// 2. Sort the spans by span.lo() (starting position). Spans that start at the same position /// are sorted with longer spans before shorter spans; and equal spans are sorted /// (deterministically) based on "dominator" relationship (if any). /// 3. Traverse the spans in sorted order to identify spans that can be dropped (for instance, /// if another span or spans are already counting the same code region), or should be merged /// into a broader combined span (because it represents a contiguous, non-branching, and /// uninterrupted region of source code). /// /// Closures are exposed in their enclosing functions as `Assign` `Rvalue`s, and since /// closures have their own MIR, their `Span` in their enclosing function should be left /// "uncovered". /// /// Note the resulting vector of `CoverageSpan`s may not be fully sorted (and does not need /// to be). pub(super) fn generate_coverage_spans( mir_body: &mir::Body<'_>, hir_info: &ExtractedHirInfo, basic_coverage_blocks: &'a CoverageGraph, ) -> Vec { let sorted_spans = from_mir::mir_to_initial_sorted_coverage_spans( mir_body, hir_info, basic_coverage_blocks, ); let coverage_spans = Self { body_span: hir_info.body_span, basic_coverage_blocks, sorted_spans_iter: sorted_spans.into_iter(), some_curr: None, curr_original_span: DUMMY_SP, some_prev: None, prev_original_span: DUMMY_SP, pending_dups: Vec::new(), refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), }; coverage_spans.to_refined_spans() } /// Iterate through the sorted `CoverageSpan`s, and return the refined list of merged and /// de-duplicated `CoverageSpan`s. fn to_refined_spans(mut self) -> Vec { while self.next_coverage_span() { // For the first span we don't have `prev` set, so most of the // span-processing steps don't make sense yet. if self.some_prev.is_none() { debug!(" initial span"); self.maybe_push_macro_name_span(); continue; } // The remaining cases assume that `prev` and `curr` are set. let prev = self.prev(); let curr = self.curr(); if curr.is_mergeable(prev) { debug!(" same bcb (and neither is a closure), merge with prev={prev:?}"); let prev = self.take_prev(); self.curr_mut().merge_from(&prev); self.maybe_push_macro_name_span(); // Note that curr.span may now differ from curr_original_span } else if prev.span.hi() <= curr.span.lo() { debug!( " different bcbs and disjoint spans, so keep curr for next iter, and add prev={prev:?}", ); let prev = self.take_prev(); self.refined_spans.push(prev); self.maybe_push_macro_name_span(); } else if prev.is_closure { // drop any equal or overlapping span (`curr`) and keep `prev` to test again in the // next iter debug!( " curr overlaps a closure (prev). Drop curr and keep prev for next iter. prev={prev:?}", ); self.take_curr(); // Discards curr. } else if curr.is_closure { self.carve_out_span_for_closure(); } else if self.prev_original_span == curr.span { // Note that this compares the new (`curr`) span to `prev_original_span`. // In this branch, the actual span byte range of `prev_original_span` is not // important. What is important is knowing whether the new `curr` span was // **originally** the same as the original span of `prev()`. The original spans // reflect their original sort order, and for equal spans, conveys a partial // ordering based on CFG dominator priority. if prev.is_macro_expansion() && curr.is_macro_expansion() { // Macros that expand to include branching (such as // `assert_eq!()`, `assert_ne!()`, `info!()`, `debug!()`, or // `trace!()`) typically generate callee spans with identical // ranges (typically the full span of the macro) for all // `BasicBlocks`. This makes it impossible to distinguish // the condition (`if val1 != val2`) from the optional // branched statements (such as the call to `panic!()` on // assert failure). In this case it is better (or less // worse) to drop the optional branch bcbs and keep the // non-conditional statements, to count when reached. debug!( " curr and prev are part of a macro expansion, and curr has the same span \ as prev, but is in a different bcb. Drop curr and keep prev for next iter. \ prev={prev:?}", ); self.take_curr(); // Discards curr. } else { self.update_pending_dups(); } } else { self.cutoff_prev_at_overlapping_curr(); self.maybe_push_macro_name_span(); } } // Drain any remaining dups into the output. for dup in self.pending_dups.drain(..) { debug!(" ...adding at least one pending dup={:?}", dup); self.refined_spans.push(dup); } // There is usually a final span remaining in `prev` after the loop ends, // so add it to the output as well. if let Some(prev) = self.some_prev.take() { debug!(" AT END, adding last prev={prev:?}"); self.refined_spans.push(prev); } // Do one last merge pass, to simplify the output. self.refined_spans.dedup_by(|b, a| { if a.is_mergeable(b) { debug!(?a, ?b, "merging list-adjacent refined spans"); a.merge_from(b); true } else { false } }); // Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage // regions for the current function leave room for the closure's own coverage regions // (injected separately, from the closure's own MIR). self.refined_spans.retain(|covspan| !covspan.is_closure); self.refined_spans } /// If `curr` is part of a new macro expansion, carve out and push a separate /// span that ends just after the macro name and its subsequent `!`. fn maybe_push_macro_name_span(&mut self) { let curr = self.curr(); let Some(visible_macro) = curr.visible_macro(self.body_span) else { return }; if let Some(prev) = &self.some_prev && prev.expn_span.eq_ctxt(curr.expn_span) { return; } // The split point is relative to `curr_original_span`, // because `curr.span` may have been merged with preceding spans. let split_point_after_macro_bang = self.curr_original_span.lo() + BytePos(visible_macro.as_str().len() as u32) + BytePos(1); // add 1 for the `!` debug_assert!(split_point_after_macro_bang <= curr.span.hi()); if split_point_after_macro_bang > curr.span.hi() { // Something is wrong with the macro name span; // return now to avoid emitting malformed mappings (e.g. #117788). return; } let mut macro_name_cov = curr.clone(); macro_name_cov.span = macro_name_cov.span.with_hi(split_point_after_macro_bang); self.curr_mut().span = curr.span.with_lo(split_point_after_macro_bang); debug!( " and curr starts a new macro expansion, so add a new span just for \ the macro `{visible_macro}!`, new span={macro_name_cov:?}", ); self.refined_spans.push(macro_name_cov); } #[track_caller] fn curr(&self) -> &CoverageSpan { self.some_curr.as_ref().unwrap_or_else(|| bug!("some_curr is None (curr)")) } #[track_caller] fn curr_mut(&mut self) -> &mut CoverageSpan { self.some_curr.as_mut().unwrap_or_else(|| bug!("some_curr is None (curr_mut)")) } /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the /// `curr` coverage span. #[track_caller] fn take_curr(&mut self) -> CoverageSpan { self.some_curr.take().unwrap_or_else(|| bug!("some_curr is None (take_curr)")) } #[track_caller] fn prev(&self) -> &CoverageSpan { self.some_prev.as_ref().unwrap_or_else(|| bug!("some_prev is None (prev)")) } #[track_caller] fn prev_mut(&mut self) -> &mut CoverageSpan { self.some_prev.as_mut().unwrap_or_else(|| bug!("some_prev is None (prev_mut)")) } #[track_caller] fn take_prev(&mut self) -> CoverageSpan { self.some_prev.take().unwrap_or_else(|| bug!("some_prev is None (take_prev)")) } /// If there are `pending_dups` but `prev` is not a matching dup (`prev.span` doesn't match the /// `pending_dups` spans), then one of the following two things happened during the previous /// iteration: /// * the previous `curr` span (which is now `prev`) was not a duplicate of the pending_dups /// (in which case there should be at least two spans in `pending_dups`); or /// * the `span` of `prev` was modified by `curr_mut().merge_from(prev)` (in which case /// `pending_dups` could have as few as one span) /// In either case, no more spans will match the span of `pending_dups`, so /// add the `pending_dups` if they don't overlap `curr`, and clear the list. fn maybe_flush_pending_dups(&mut self) { let Some(last_dup) = self.pending_dups.last() else { return }; if last_dup.span == self.prev().span { return; } debug!( " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ previous iteration, or prev started a new disjoint span" ); if last_dup.span.hi() <= self.curr().span.lo() { for dup in self.pending_dups.drain(..) { debug!(" ...adding at least one pending={:?}", dup); self.refined_spans.push(dup); } } else { self.pending_dups.clear(); } assert!(self.pending_dups.is_empty()); } /// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order. fn next_coverage_span(&mut self) -> bool { if let Some(curr) = self.some_curr.take() { self.some_prev = Some(curr); self.prev_original_span = self.curr_original_span; } while let Some(curr) = self.sorted_spans_iter.next() { debug!("FOR curr={:?}", curr); if let Some(prev) = &self.some_prev && prev.span.lo() > curr.span.lo() { // Skip curr because prev has already advanced beyond the end of curr. // This can only happen if a prior iteration updated `prev` to skip past // a region of code, such as skipping past a closure. debug!( " prev.span starts after curr.span, so curr will be dropped (skipping past \ closure?); prev={prev:?}", ); } else { // Save a copy of the original span for `curr` in case the `CoverageSpan` is changed // by `self.curr_mut().merge_from(prev)`. self.curr_original_span = curr.span; self.some_curr.replace(curr); self.maybe_flush_pending_dups(); return true; } } false } /// If `prev`s span extends left of the closure (`curr`), carve out the closure's span from /// `prev`'s span. (The closure's coverage counters will be injected when processing the /// closure's own MIR.) Add the portion of the span to the left of the closure; and if the span /// extends to the right of the closure, update `prev` to that portion of the span. For any /// `pending_dups`, repeat the same process. fn carve_out_span_for_closure(&mut self) { let prev = self.prev(); let curr = self.curr(); let left_cutoff = curr.span.lo(); let right_cutoff = curr.span.hi(); let has_pre_closure_span = prev.span.lo() < right_cutoff; let has_post_closure_span = prev.span.hi() > right_cutoff; if has_pre_closure_span { let mut pre_closure = self.prev().clone(); pre_closure.span = pre_closure.span.with_hi(left_cutoff); debug!(" prev overlaps a closure. Adding span for pre_closure={:?}", pre_closure); for mut dup in self.pending_dups.iter().cloned() { dup.span = dup.span.with_hi(left_cutoff); debug!(" ...and at least one pre_closure dup={:?}", dup); self.refined_spans.push(dup); } self.refined_spans.push(pre_closure); } if has_post_closure_span { // Mutate `prev.span()` to start after the closure (and discard curr). // (**NEVER** update `prev_original_span` because it affects the assumptions // about how the `CoverageSpan`s are ordered.) self.prev_mut().span = self.prev().span.with_lo(right_cutoff); debug!(" Mutated prev.span to start after the closure. prev={:?}", self.prev()); for dup in &mut self.pending_dups { debug!(" ...and at least one overlapping dup={:?}", dup); dup.span = dup.span.with_lo(right_cutoff); } let closure_covspan = self.take_curr(); // Prevent this curr from becoming prev. self.refined_spans.push(closure_covspan); // since self.prev() was already updated } else { self.pending_dups.clear(); } } /// Called if `curr.span` equals `prev_original_span` (and potentially equal to all /// `pending_dups` spans, if any). Keep in mind, `prev.span()` may have been changed. /// If prev.span() was merged into other spans (with matching BCB, for instance), /// `prev.span.hi()` will be greater than (further right of) `prev_original_span.hi()`. /// If prev.span() was split off to the right of a closure, prev.span().lo() will be /// greater than prev_original_span.lo(). The actual span of `prev_original_span` is /// not as important as knowing that `prev()` **used to have the same span** as `curr()`, /// which means their sort order is still meaningful for determining the dominator /// relationship. /// /// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if /// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held, /// until their disposition is determined. In this latter case, the `prev` dup is moved into /// `pending_dups` so the new `curr` dup can be moved to `prev` for the next iteration. fn update_pending_dups(&mut self) { let prev_bcb = self.prev().bcb; let curr_bcb = self.curr().bcb; // Equal coverage spans are ordered by dominators before dominated (if any), so it should be // impossible for `curr` to dominate any previous `CoverageSpan`. debug_assert!(!self.basic_coverage_blocks.dominates(curr_bcb, prev_bcb)); let initial_pending_count = self.pending_dups.len(); if initial_pending_count > 0 { self.pending_dups .retain(|dup| !self.basic_coverage_blocks.dominates(dup.bcb, curr_bcb)); let n_discarded = initial_pending_count - self.pending_dups.len(); if n_discarded > 0 { debug!( " discarded {n_discarded} of {initial_pending_count} pending_dups that dominated curr", ); } } if self.basic_coverage_blocks.dominates(prev_bcb, curr_bcb) { debug!( " different bcbs but SAME spans, and prev dominates curr. Discard prev={:?}", self.prev() ); self.cutoff_prev_at_overlapping_curr(); // If one span dominates the other, associate the span with the code from the dominated // block only (`curr`), and discard the overlapping portion of the `prev` span. (Note // that if `prev.span` is wider than `prev_original_span`, a `CoverageSpan` will still // be created for `prev`s block, for the non-overlapping portion, left of `curr.span`.) // // For example: // match somenum { // x if x < 1 => { ... } // }... // // The span for the first `x` is referenced by both the pattern block (every time it is // evaluated) and the arm code (only when matched). The counter will be applied only to // the dominated block. This allows coverage to track and highlight things like the // assignment of `x` above, if the branch is matched, making `x` available to the arm // code; and to track and highlight the question mark `?` "try" operator at the end of // a function call returning a `Result`, so the `?` is covered when the function returns // an `Err`, and not counted as covered if the function always returns `Ok`. } else { // Save `prev` in `pending_dups`. (`curr` will become `prev` in the next iteration.) // If the `curr` CoverageSpan is later discarded, `pending_dups` can be discarded as // well; but if `curr` is added to refined_spans, the `pending_dups` will also be added. debug!( " different bcbs but SAME spans, and neither dominates, so keep curr for \ next iter, and, pending upcoming spans (unless overlapping) add prev={:?}", self.prev() ); let prev = self.take_prev(); self.pending_dups.push(prev); } } /// `curr` overlaps `prev`. If `prev`s span extends left of `curr`s span, keep _only_ /// statements that end before `curr.lo()` (if any), and add the portion of the /// combined span for those statements. Any other statements have overlapping spans /// that can be ignored because `curr` and/or other upcoming statements/spans inside /// the overlap area will produce their own counters. This disambiguation process /// avoids injecting multiple counters for overlapping spans, and the potential for /// double-counting. fn cutoff_prev_at_overlapping_curr(&mut self) { debug!( " different bcbs, overlapping spans, so ignore/drop pending and only add prev \ if it has statements that end before curr; prev={:?}", self.prev() ); if self.pending_dups.is_empty() { let curr_span = self.curr().span; self.prev_mut().cutoff_statements_at(curr_span.lo()); if self.prev().merged_spans.is_empty() { debug!(" ... no non-overlapping statements to add"); } else { debug!(" ... adding modified prev={:?}", self.prev()); let prev = self.take_prev(); self.refined_spans.push(prev); } } else { // with `pending_dups`, `prev` cannot have any statements that don't overlap self.pending_dups.clear(); } } }