use super::debug::term_type; use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB}; use crate::util::spanview::source_range_no_file; use rustc_data_structures::graph::WithNumNodes; use rustc_middle::mir::{ self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator, TerminatorKind, }; use rustc_middle::ty::TyCtxt; use rustc_span::source_map::original_sp; use rustc_span::{BytePos, Span, SyntaxContext}; use std::cmp::Ordering; #[derive(Debug, Copy, Clone)] pub(super) enum CoverageStatement { Statement(BasicBlock, Span, usize), Terminator(BasicBlock, Span), } impl CoverageStatement { pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { match *self { Self::Statement(bb, span, stmt_index) => { let stmt = &mir_body[bb].statements[stmt_index]; format!( "{}: @{}[{}]: {:?}", source_range_no_file(tcx, &span), bb.index(), stmt_index, stmt ) } Self::Terminator(bb, span) => { let term = mir_body[bb].terminator(); format!( "{}: @{}.{}: {:?}", source_range_no_file(tcx, &span), bb.index(), term_type(&term.kind), term.kind ) } } } pub fn span(&self) -> &Span { match self { Self::Statement(_, span, _) | Self::Terminator(_, span) => span, } } } /// 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 /// `CoverageStatement` vectors, and the `Span`s to cover the extent of the combined `Span`s. /// /// Note: A `CoverageStatement` 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` /// `is_dominated_by()` the `BasicBlock`s in this `CoverageSpan`. #[derive(Debug, Clone)] pub(super) struct CoverageSpan { pub span: Span, pub bcb: BasicCoverageBlock, pub coverage_statements: Vec, pub is_closure: bool, } impl CoverageSpan { pub fn for_fn_sig(fn_sig_span: Span) -> Self { Self { span: fn_sig_span, bcb: START_BCB, coverage_statements: vec![], is_closure: false } } pub fn for_statement( statement: &Statement<'tcx>, span: Span, bcb: BasicCoverageBlock, bb: BasicBlock, stmt_index: usize, ) -> Self { let is_closure = match statement.kind { StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => match kind { AggregateKind::Closure(_, _) | AggregateKind::Generator(_, _, _) => true, _ => false, }, _ => false, }; Self { span, bcb, coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)], is_closure, } } pub fn for_terminator(span: Span, bcb: BasicCoverageBlock, bb: BasicBlock) -> Self { Self { span, bcb, coverage_statements: vec![CoverageStatement::Terminator(bb, 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.coverage_statements.append(&mut other.coverage_statements); } pub fn cutoff_statements_at(&mut self, cutoff_pos: BytePos) { self.coverage_statements.retain(|covstmt| covstmt.span().hi() <= cutoff_pos); if let Some(highest_covstmt) = self.coverage_statements.iter().max_by_key(|covstmt| covstmt.span().hi()) { self.span = self.span.with_hi(highest_covstmt.span().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 } pub fn format(&self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>) -> String { format!( "{}\n {}", source_range_no_file(tcx, &self.span), self.format_coverage_statements(tcx, mir_body).replace("\n", "\n "), ) } pub fn format_coverage_statements( &self, tcx: TyCtxt<'tcx>, mir_body: &'a mir::Body<'tcx>, ) -> String { let mut sorted_coverage_statements = self.coverage_statements.clone(); sorted_coverage_statements.sort_unstable_by_key(|covstmt| match *covstmt { CoverageStatement::Statement(bb, _, index) => (bb, index), CoverageStatement::Terminator(bb, _) => (bb, usize::MAX), }); sorted_coverage_statements .iter() .map(|covstmt| covstmt.format(tcx, mir_body)) .collect::>() .join("\n") } } /// 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) pub struct CoverageSpans<'a, 'tcx> { /// The MIR, used to look up `BasicBlockData`. mir_body: &'a mir::Body<'tcx>, /// A `Span` covering the signature of function for the MIR. fn_sig_span: Span, /// 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: Option>, /// 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 the `curr` span is modified. 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. 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, 'tcx> CoverageSpans<'a, 'tcx> { /// 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: &'a mir::Body<'tcx>, fn_sig_span: Span, body_span: Span, basic_coverage_blocks: &'a CoverageGraph, ) -> Vec { let mut coverage_spans = CoverageSpans { mir_body, fn_sig_span, body_span, basic_coverage_blocks, sorted_spans_iter: None, refined_spans: Vec::with_capacity(basic_coverage_blocks.num_nodes() * 2), some_curr: None, curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), some_prev: None, prev_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)), pending_dups: Vec::new(), }; let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans(); coverage_spans.sorted_spans_iter = Some(sorted_spans.into_iter()); coverage_spans.some_prev = coverage_spans.sorted_spans_iter.as_mut().unwrap().next(); coverage_spans.prev_original_span = coverage_spans.some_prev.as_ref().expect("at least one span").span; coverage_spans.to_refined_spans() } fn mir_to_initial_sorted_coverage_spans(&self) -> Vec { let mut initial_spans = Vec::::with_capacity(self.mir_body.num_nodes() * 2); for (bcb, bcb_data) in self.basic_coverage_blocks.iter_enumerated() { for coverage_span in self.bcb_to_initial_coverage_spans(bcb, bcb_data) { initial_spans.push(coverage_span); } } if initial_spans.is_empty() { // This can happen if, for example, the function is unreachable (contains only a // `BasicBlock`(s) with an `Unreachable` terminator). return initial_spans; } initial_spans.push(CoverageSpan::for_fn_sig(self.fn_sig_span)); initial_spans.sort_unstable_by(|a, b| { if a.span.lo() == b.span.lo() { if a.span.hi() == b.span.hi() { if a.is_in_same_bcb(b) { Some(Ordering::Equal) } else { // Sort equal spans by dominator relationship, in reverse order (so // dominators always come after the dominated equal spans). When later // comparing two spans in order, the first will either dominate the second, // or they will have no dominator relationship. self.basic_coverage_blocks.dominators().rank_partial_cmp(b.bcb, a.bcb) } } else { // Sort hi() in reverse order so shorter spans are attempted after longer spans. // This guarantees that, if a `prev` span overlaps, and is not equal to, a // `curr` span, the prev span either extends further left of the curr span, or // they start at the same position and the prev span extends further right of // the end of the curr span. b.span.hi().partial_cmp(&a.span.hi()) } } else { a.span.lo().partial_cmp(&b.span.lo()) } .unwrap() }); initial_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() { if self.curr().is_mergeable(self.prev()) { debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev()); let prev = self.take_prev(); self.curr_mut().merge_from(prev); // Note that curr.span may now differ from curr_original_span } else if self.prev_ends_before_curr() { debug!( " different bcbs and disjoint spans, so keep curr for next iter, and add \ prev={:?}", self.prev() ); let prev = self.take_prev(); self.refined_spans.push(prev); } else if self.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={:?}", self.prev() ); self.take_curr(); } else if self.curr().is_closure { self.carve_out_span_for_closure(); } else if self.prev_original_span == self.curr().span { // Note that this compares the new span to `prev_original_span`, which may not // be the full `prev.span` (if merged during the previous iteration). self.hold_pending_dups_unless_dominated(); } else { self.cutoff_prev_at_overlapping_curr(); } } debug!(" AT END, adding last prev={:?}", self.prev()); let prev = self.take_prev(); let CoverageSpans { pending_dups, mut refined_spans, .. } = self; for dup in pending_dups { debug!(" ...adding at least one pending dup={:?}", dup); refined_spans.push(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) = refined_spans.last() { last_covspan.is_closure && last_covspan.span.hi() == self.body_span.hi() } else { false }; if !body_ends_with_closure { refined_spans.push(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). refined_spans.retain(|covspan| !covspan.is_closure); refined_spans } // Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of // the `BasicBlock`(s) in the given `BasicCoverageBlockData`. One `CoverageSpan` is generated // for each `Statement` and `Terminator`. (Note that subsequent stages of coverage analysis will // merge some `CoverageSpan`s, at which point a `CoverageSpan` may represent multiple // `Statement`s and/or `Terminator`s.) fn bcb_to_initial_coverage_spans( &self, bcb: BasicCoverageBlock, bcb_data: &'a BasicCoverageBlockData, ) -> Vec { bcb_data .basic_blocks .iter() .flat_map(|&bb| { let data = &self.mir_body[bb]; data.statements .iter() .enumerate() .filter_map(move |(index, statement)| { filtered_statement_span(statement, self.body_span).map(|span| { CoverageSpan::for_statement(statement, span, bcb, bb, index) }) }) .chain( filtered_terminator_span(data.terminator(), self.body_span) .map(|span| CoverageSpan::for_terminator(span, bcb, bb)), ) }) .collect() } 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")) } 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 check_pending_dups(&mut self) { if let Some(dup) = self.pending_dups.last() { if dup.span != self.prev().span { debug!( " SAME spans, but pending_dups are NOT THE SAME, so BCBs matched on \ previous iteration, or prev started a new disjoint span" ); if dup.span.hi() <= self.curr().span.lo() { let pending_dups = self.pending_dups.split_off(0); for dup in pending_dups.into_iter() { debug!(" ...adding at least one pending={:?}", dup); self.refined_spans.push(dup); } } 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.as_mut().unwrap().next() { debug!("FOR curr={:?}", curr); if self.prev_starts_after_next(&curr) { debug!( " prev.span starts after curr.span, so curr will be dropped (skipping past \ closure?); prev={:?}", self.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.check_pending_dups(); return true; } } false } /// 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")) } /// Returns true if the curr span should be skipped 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. fn prev_starts_after_next(&self, next_curr: &CoverageSpan) -> bool { self.prev().span.lo() > next_curr.span.lo() } /// Returns true if the curr span starts past the end of the prev span, which means they don't /// overlap, so we now know the prev can be added to the refined coverage spans. fn prev_ends_before_curr(&self) -> bool { self.prev().span.hi() <= self.curr().span.lo() } /// 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 curr_span = self.curr().span; let left_cutoff = curr_span.lo(); let right_cutoff = curr_span.hi(); let has_pre_closure_span = self.prev().span.lo() < right_cutoff; let has_post_closure_span = self.prev().span.hi() > right_cutoff; let mut pending_dups = self.pending_dups.split_off(0); 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.refined_spans.push(dup); } } self.refined_spans.push(pre_closure); } if has_post_closure_span { // Update prev.span to start after the closure (and discard curr) self.prev_mut().span = self.prev().span.with_lo(right_cutoff); self.prev_original_span = self.prev().span; for dup in pending_dups.iter_mut() { dup.span = dup.span.with_lo(right_cutoff); } self.pending_dups.append(&mut pending_dups); let closure_covspan = self.take_curr(); self.refined_spans.push(closure_covspan); // since self.prev() was already updated } else { pending_dups.clear(); } } /// Called if `curr.span` equals `prev_original_span` (and potentially equal to all /// `pending_dups` spans, if any); but keep in mind, `prev.span` may start at a `Span.lo()` that /// is less than (further left of) `prev_original_span.lo()`. /// /// 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 hold_pending_dups_unless_dominated(&mut self) { // 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.span_bcb_is_dominated_by(self.prev(), self.curr())); let initial_pending_count = self.pending_dups.len(); if initial_pending_count > 0 { let mut pending_dups = self.pending_dups.split_off(0); pending_dups.retain(|dup| !self.span_bcb_is_dominated_by(self.curr(), dup)); self.pending_dups.append(&mut pending_dups); if self.pending_dups.len() < initial_pending_count { debug!( " discarded {} of {} pending_dups that dominated curr", initial_pending_count - self.pending_dups.len(), initial_pending_count ); } } if self.span_bcb_is_dominated_by(self.curr(), self.prev()) { 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, assocate 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().coverage_statements.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(); } } fn span_bcb_is_dominated_by(&self, covspan: &CoverageSpan, dom_covspan: &CoverageSpan) -> bool { self.basic_coverage_blocks.is_dominated_by(covspan.bcb, dom_covspan.bcb) } } pub(super) fn filtered_statement_span( statement: &'a Statement<'tcx>, body_span: Span, ) -> 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(_) // Coverage should not be encountered, but don't inject coverage coverage | StatementKind::Coverage(_) // Ignore `Nop`s | 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 (cause, _)) if cause == FakeReadCause::ForGuardBinding => None, // Retain spans from all other statements StatementKind::FakeRead(box (_, _)) // Not including `ForGuardBinding` | StatementKind::CopyNonOverlapping(..) | StatementKind::Assign(_) | StatementKind::SetDiscriminant { .. } | StatementKind::LlvmInlineAsm(_) | StatementKind::Retag(_, _) | StatementKind::AscribeUserType(_, _) => { Some(function_source_span(statement.source_info.span, body_span)) } } } pub(super) fn filtered_terminator_span( terminator: &'a Terminator<'tcx>, body_span: Span, ) -> 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 // Unreachable blocks are not connected to the MIR CFG | TerminatorKind::Assert { .. } | TerminatorKind::Drop { .. } | TerminatorKind::DropAndReplace { .. } | TerminatorKind::SwitchInt { .. } // For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`. | TerminatorKind::FalseEdge { .. } | TerminatorKind::Goto { .. } => None, // Retain spans from all other terminators TerminatorKind::Resume | TerminatorKind::Abort | TerminatorKind::Return | TerminatorKind::Call { .. } | TerminatorKind::Yield { .. } | TerminatorKind::GeneratorDrop | TerminatorKind::FalseUnwind { .. } | TerminatorKind::InlineAsm { .. } => { Some(function_source_span(terminator.source_info.span, body_span)) } } } #[inline] fn function_source_span(span: Span, body_span: Span) -> Span { let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root()); if body_span.contains(span) { span } else { body_span } }