mirror of
				https://github.com/rust-lang/rust.git
				synced 2025-10-30 20:44:34 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			676 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
			
		
		
	
	
			676 lines
		
	
	
		
			30 KiB
		
	
	
	
		
			Rust
		
	
	
	
	
	
| use std::cell::OnceCell;
 | |
| 
 | |
| use rustc_data_structures::graph::WithNumNodes;
 | |
| use rustc_index::IndexVec;
 | |
| use rustc_middle::mir::{self, AggregateKind, Rvalue, Statement, StatementKind};
 | |
| use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol, DUMMY_SP};
 | |
| 
 | |
| use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
 | |
| 
 | |
| mod from_mir;
 | |
| 
 | |
| pub(super) struct CoverageSpans {
 | |
|     /// Map from BCBs to their list of coverage spans.
 | |
|     bcb_to_spans: IndexVec<BasicCoverageBlock, Vec<Span>>,
 | |
| }
 | |
| 
 | |
| impl CoverageSpans {
 | |
|     pub(super) fn generate_coverage_spans(
 | |
|         mir_body: &mir::Body<'_>,
 | |
|         fn_sig_span: Span,
 | |
|         body_span: Span,
 | |
|         basic_coverage_blocks: &CoverageGraph,
 | |
|     ) -> Self {
 | |
|         let coverage_spans = CoverageSpansGenerator::generate_coverage_spans(
 | |
|             mir_body,
 | |
|             fn_sig_span,
 | |
|             body_span,
 | |
|             basic_coverage_blocks,
 | |
|         );
 | |
| 
 | |
|         // 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);
 | |
|         }
 | |
| 
 | |
|         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 bcbs_with_coverage_spans(
 | |
|         &self,
 | |
|     ) -> impl Iterator<Item = (BasicCoverageBlock, &[Span])> {
 | |
|         self.bcb_to_spans.iter_enumerated().filter_map(|(bcb, spans)| {
 | |
|             // Only yield BCBs that have at least one coverage span.
 | |
|             (!spans.is_empty()).then_some((bcb, spans.as_slice()))
 | |
|         })
 | |
|     }
 | |
| }
 | |
| 
 | |
| /// 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<Option<Symbol>>,
 | |
|     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<Span>,
 | |
|     pub is_closure: bool,
 | |
| }
 | |
| 
 | |
| impl CoverageSpan {
 | |
|     pub fn for_fn_sig(fn_sig_span: Span) -> Self {
 | |
|         Self {
 | |
|             span: fn_sig_span,
 | |
|             expn_span: fn_sig_span,
 | |
|             current_macro_or_none: Default::default(),
 | |
|             bcb: START_BCB,
 | |
|             merged_spans: vec![fn_sig_span],
 | |
|             is_closure: false,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn for_statement(
 | |
|         statement: &Statement<'_>,
 | |
|         span: Span,
 | |
|         expn_span: Span,
 | |
|         bcb: BasicCoverageBlock,
 | |
|     ) -> Self {
 | |
|         let is_closure = match statement.kind {
 | |
|             StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => {
 | |
|                 matches!(kind, AggregateKind::Closure(_, _) | AggregateKind::Coroutine(_, _, _))
 | |
|             }
 | |
|             _ => false,
 | |
|         };
 | |
| 
 | |
|         Self {
 | |
|             span,
 | |
|             expn_span,
 | |
|             current_macro_or_none: Default::default(),
 | |
|             bcb,
 | |
|             merged_spans: vec![span],
 | |
|             is_closure,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn for_terminator(span: Span, expn_span: Span, bcb: BasicCoverageBlock) -> Self {
 | |
|         Self {
 | |
|             span,
 | |
|             expn_span,
 | |
|             current_macro_or_none: Default::default(),
 | |
|             bcb,
 | |
|             merged_spans: vec![span],
 | |
|             is_closure: false,
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     pub fn merge_from(&mut self, mut other: CoverageSpan) {
 | |
|         debug_assert!(self.is_mergeable(&other));
 | |
|         self.span = self.span.to(other.span);
 | |
|         self.merged_spans.append(&mut 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<Symbol> {
 | |
|         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<Symbol> {
 | |
|         if let Some(current_macro) = self.current_macro()
 | |
|             && self
 | |
|                 .expn_span
 | |
|                 .parent_callsite()
 | |
|                 .unwrap_or_else(|| bug!("macro must have a parent"))
 | |
|                 .eq_ctxt(body_span)
 | |
|         {
 | |
|             return Some(current_macro);
 | |
|         }
 | |
|         None
 | |
|     }
 | |
| 
 | |
|     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<CoverageSpan>,
 | |
| 
 | |
|     /// 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<CoverageSpan>,
 | |
| 
 | |
|     /// 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<CoverageSpan>,
 | |
| 
 | |
|     /// 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<CoverageSpan>,
 | |
| 
 | |
|     /// 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<CoverageSpan>,
 | |
| }
 | |
| 
 | |
| 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<'_>,
 | |
|         fn_sig_span: Span, // Ensured to be same SourceFile and SyntaxContext as `body_span`
 | |
|         body_span: Span,
 | |
|         basic_coverage_blocks: &'a CoverageGraph,
 | |
|     ) -> Vec<CoverageSpan> {
 | |
|         let sorted_spans = from_mir::mir_to_initial_sorted_coverage_spans(
 | |
|             mir_body,
 | |
|             fn_sig_span,
 | |
|             body_span,
 | |
|             basic_coverage_blocks,
 | |
|         );
 | |
| 
 | |
|         let coverage_spans = Self {
 | |
|             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<CoverageSpan> {
 | |
|         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.push_refined_span(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();
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         let prev = self.take_prev();
 | |
|         debug!("    AT END, adding last prev={prev:?}");
 | |
| 
 | |
|         // Take `pending_dups` so that we can drain it while calling self methods.
 | |
|         // It is never used as a field after this point.
 | |
|         for dup in std::mem::take(&mut self.pending_dups) {
 | |
|             debug!("    ...adding at least one pending dup={:?}", dup);
 | |
|             self.push_refined_span(dup);
 | |
|         }
 | |
| 
 | |
|         // Async functions wrap a closure that implements the body to be executed. The enclosing
 | |
|         // function is called and returns an `impl Future` without initially executing any of the
 | |
|         // body. To avoid showing the return from the enclosing function as a "covered" return from
 | |
|         // the closure, the enclosing function's `TerminatorKind::Return`s `CoverageSpan` is
 | |
|         // excluded. The closure's `Return` is the only one that will be counted. This provides
 | |
|         // adequate coverage, and more intuitive counts. (Avoids double-counting the closing brace
 | |
|         // of the function body.)
 | |
|         let body_ends_with_closure = if let Some(last_covspan) = self.refined_spans.last() {
 | |
|             last_covspan.is_closure && last_covspan.span.hi() == self.body_span.hi()
 | |
|         } else {
 | |
|             false
 | |
|         };
 | |
| 
 | |
|         if !body_ends_with_closure {
 | |
|             self.push_refined_span(prev);
 | |
|         }
 | |
| 
 | |
|         // 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
 | |
|     }
 | |
| 
 | |
|     fn push_refined_span(&mut self, covspan: CoverageSpan) {
 | |
|         if let Some(last) = self.refined_spans.last_mut()
 | |
|             && last.is_mergeable(&covspan)
 | |
|         {
 | |
|             // Instead of pushing the new span, merge it with the last refined span.
 | |
|             debug!(?last, ?covspan, "merging new refined span with last refined span");
 | |
|             last.merge_from(covspan);
 | |
|         } else {
 | |
|             self.refined_spans.push(covspan);
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// 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;
 | |
|         }
 | |
| 
 | |
|         let merged_prefix_len = self.curr_original_span.lo() - curr.span.lo();
 | |
|         let after_macro_bang = merged_prefix_len + BytePos(visible_macro.as_str().len() as u32 + 1);
 | |
|         let mut macro_name_cov = curr.clone();
 | |
|         self.curr_mut().span = curr.span.with_lo(curr.span.lo() + after_macro_bang);
 | |
|         macro_name_cov.span =
 | |
|             macro_name_cov.span.with_hi(macro_name_cov.span.lo() + 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.push_refined_span(macro_name_cov);
 | |
|     }
 | |
| 
 | |
|     fn curr(&self) -> &CoverageSpan {
 | |
|         self.some_curr
 | |
|             .as_ref()
 | |
|             .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr"))
 | |
|     }
 | |
| 
 | |
|     fn curr_mut(&mut self) -> &mut CoverageSpan {
 | |
|         self.some_curr
 | |
|             .as_mut()
 | |
|             .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr"))
 | |
|     }
 | |
| 
 | |
|     /// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the
 | |
|     /// `curr` coverage span.
 | |
|     fn take_curr(&mut self) -> CoverageSpan {
 | |
|         self.some_curr.take().unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_curr"))
 | |
|     }
 | |
| 
 | |
|     fn prev(&self) -> &CoverageSpan {
 | |
|         self.some_prev
 | |
|             .as_ref()
 | |
|             .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev"))
 | |
|     }
 | |
| 
 | |
|     fn prev_mut(&mut self) -> &mut CoverageSpan {
 | |
|         self.some_prev
 | |
|             .as_mut()
 | |
|             .unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_prev"))
 | |
|     }
 | |
| 
 | |
|     fn take_prev(&mut self) -> CoverageSpan {
 | |
|         self.some_prev.take().unwrap_or_else(|| bug!("invalid attempt to unwrap a None some_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() {
 | |
|             // Temporarily steal `pending_dups` into a local, so that we can
 | |
|             // drain it while calling other self methods.
 | |
|             let mut pending_dups = std::mem::take(&mut self.pending_dups);
 | |
|             for dup in pending_dups.drain(..) {
 | |
|                 debug!("    ...adding at least one pending={:?}", dup);
 | |
|                 self.push_refined_span(dup);
 | |
|             }
 | |
|             // The list of dups is now empty, but we can recycle its capacity.
 | |
|             assert!(pending_dups.is_empty() && self.pending_dups.is_empty());
 | |
|             self.pending_dups = pending_dups;
 | |
|         } else {
 | |
|             self.pending_dups.clear();
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     /// 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;
 | |
| 
 | |
|         // Temporarily steal `pending_dups` into a local, so that we can
 | |
|         // mutate and/or drain it while calling other self methods.
 | |
|         let mut pending_dups = std::mem::take(&mut self.pending_dups);
 | |
| 
 | |
|         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);
 | |
|             if !pending_dups.is_empty() {
 | |
|                 for mut dup in pending_dups.iter().cloned() {
 | |
|                     dup.span = dup.span.with_hi(left_cutoff);
 | |
|                     debug!("    ...and at least one pre_closure dup={:?}", dup);
 | |
|                     self.push_refined_span(dup);
 | |
|                 }
 | |
|             }
 | |
|             self.push_refined_span(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 pending_dups.iter_mut() {
 | |
|                 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.push_refined_span(closure_covspan); // since self.prev() was already updated
 | |
|         } else {
 | |
|             pending_dups.clear();
 | |
|         }
 | |
| 
 | |
|         // Restore the modified post-closure spans, or the empty vector's capacity.
 | |
|         assert!(self.pending_dups.is_empty());
 | |
|         self.pending_dups = pending_dups;
 | |
|     }
 | |
| 
 | |
|     /// 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.push_refined_span(prev);
 | |
|             }
 | |
|         } else {
 | |
|             // with `pending_dups`, `prev` cannot have any statements that don't overlap
 | |
|             self.pending_dups.clear();
 | |
|         }
 | |
|     }
 | |
| }
 | 
