Combination of commits
Fixes multiple issue with counters, with simplification Includes a change to the implicit else span in ast_lowering, so coverage of the implicit else no longer spans the `then` block. Adds coverage for unused closures and async function bodies. Fixes: #78542 Adding unreachable regions for known MIR missing from coverage map Cleaned up PR commits, and removed link-dead-code requirement and tests Coverage no longer depends on Issue #76038 (`-C link-dead-code` is no longer needed or enforced, so MSVC can use the same tests as Linux and MacOS now) Restrict adding unreachable regions to covered files Improved the code that adds coverage for uncalled functions (with MIR but not-codegenned) to avoid generating coverage in files not already included in the files with covered functions. Resolved last known issue requiring --emit llvm-ir workaround Fixed bugs in how unreachable code spans were added.
This commit is contained in:
parent
f6c9c1a576
commit
def932ca86
354 changed files with 12634 additions and 20486 deletions
|
@ -1,10 +1,9 @@
|
|||
use super::debug::term_type;
|
||||
use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
|
||||
use super::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph, START_BCB};
|
||||
|
||||
use crate::util::spanview::source_range_no_file;
|
||||
|
||||
use rustc_data_structures::graph::WithNumNodes;
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_middle::mir::{
|
||||
self, AggregateKind, BasicBlock, FakeReadCause, Rvalue, Statement, StatementKind, Terminator,
|
||||
TerminatorKind,
|
||||
|
@ -74,6 +73,10 @@ pub(super) struct CoverageSpan {
|
|||
}
|
||||
|
||||
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,
|
||||
|
@ -82,10 +85,10 @@ impl CoverageSpan {
|
|||
stmt_index: usize,
|
||||
) -> Self {
|
||||
let is_closure = match statement.kind {
|
||||
StatementKind::Assign(box (
|
||||
_,
|
||||
Rvalue::Aggregate(box AggregateKind::Closure(_, _), _),
|
||||
)) => true,
|
||||
StatementKind::Assign(box (_, Rvalue::Aggregate(box ref kind, _))) => match kind {
|
||||
AggregateKind::Closure(_, _) | AggregateKind::Generator(_, _, _) => true,
|
||||
_ => false,
|
||||
},
|
||||
_ => false,
|
||||
};
|
||||
|
||||
|
@ -109,9 +112,6 @@ impl CoverageSpan {
|
|||
pub fn merge_from(&mut self, mut other: CoverageSpan) {
|
||||
debug_assert!(self.is_mergeable(&other));
|
||||
self.span = self.span.to(other.span);
|
||||
if other.is_closure {
|
||||
self.is_closure = true;
|
||||
}
|
||||
self.coverage_statements.append(&mut other.coverage_statements);
|
||||
}
|
||||
|
||||
|
@ -171,6 +171,9 @@ 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,
|
||||
|
@ -216,11 +219,13 @@ pub struct CoverageSpans<'a, 'tcx> {
|
|||
impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
||||
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<CoverageSpan> {
|
||||
let mut coverage_spans = CoverageSpans {
|
||||
mir_body,
|
||||
fn_sig_span,
|
||||
body_span,
|
||||
basic_coverage_blocks,
|
||||
sorted_spans_iter: None,
|
||||
|
@ -277,6 +282,8 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||
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() {
|
||||
|
@ -331,7 +338,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||
prev={:?}",
|
||||
self.prev()
|
||||
);
|
||||
self.discard_curr();
|
||||
self.take_curr();
|
||||
} else if self.curr().is_closure {
|
||||
self.carve_out_span_for_closure();
|
||||
} else if self.prev_original_span == self.curr().span {
|
||||
|
@ -345,28 +352,28 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||
|
||||
debug!(" AT END, adding last prev={:?}", self.prev());
|
||||
let prev = self.take_prev();
|
||||
let CoverageSpans {
|
||||
mir_body, basic_coverage_blocks, pending_dups, mut refined_spans, ..
|
||||
} = self;
|
||||
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);
|
||||
}
|
||||
refined_spans.push(prev);
|
||||
|
||||
// Remove `CoverageSpan`s with empty spans ONLY if the empty `CoverageSpan`s BCB also has at
|
||||
// least one other non-empty `CoverageSpan`.
|
||||
let mut has_coverage = BitSet::new_empty(basic_coverage_blocks.num_nodes());
|
||||
for covspan in &refined_spans {
|
||||
if !covspan.span.is_empty() {
|
||||
has_coverage.insert(covspan.bcb);
|
||||
}
|
||||
// Async functions wrap a closure that implements the body to be executed. The enclosing
|
||||
// function is initially called, posts the closure to the executor, and returns. 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);
|
||||
}
|
||||
refined_spans.retain(|covspan| {
|
||||
!(covspan.span.is_empty()
|
||||
&& is_goto(&basic_coverage_blocks[covspan.bcb].terminator(mir_body).kind)
|
||||
&& has_coverage.contains(covspan.bcb))
|
||||
});
|
||||
|
||||
// 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
|
||||
|
@ -491,8 +498,8 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||
|
||||
/// If called, then the next call to `next_coverage_span()` will *not* update `prev` with the
|
||||
/// `curr` coverage span.
|
||||
fn discard_curr(&mut self) {
|
||||
self.some_curr = None;
|
||||
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
|
||||
|
@ -508,11 +515,11 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||
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.
|
||||
/// 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();
|
||||
|
@ -541,7 +548,8 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||
dup.span = dup.span.with_lo(right_cutoff);
|
||||
}
|
||||
self.pending_dups.append(&mut pending_dups);
|
||||
self.discard_curr(); // since self.prev() was already updated
|
||||
let closure_covspan = self.take_curr();
|
||||
self.refined_spans.push(closure_covspan); // since self.prev() was already updated
|
||||
} else {
|
||||
pending_dups.clear();
|
||||
}
|
||||
|
@ -705,30 +713,8 @@ pub(super) fn filtered_terminator_span(
|
|||
| TerminatorKind::DropAndReplace { .. }
|
||||
| TerminatorKind::SwitchInt { .. }
|
||||
// For `FalseEdge`, only the `real` branch is taken, so it is similar to a `Goto`.
|
||||
// FIXME(richkadel): Note that `Goto` was moved to it's own match arm, for the reasons
|
||||
// described below. Add tests to confirm whether or not similar cases also apply to
|
||||
// `FalseEdge`.
|
||||
| TerminatorKind::FalseEdge { .. } => None,
|
||||
|
||||
// FIXME(#78542): Can spans for `TerminatorKind::Goto` be improved to avoid special cases?
|
||||
//
|
||||
// `Goto`s are often the targets of `SwitchInt` branches, and certain important
|
||||
// optimizations to replace some `Counter`s with `Expression`s require a separate
|
||||
// `BasicCoverageBlock` for each branch, to support the `Counter`, when needed.
|
||||
//
|
||||
// Also, some test cases showed that `Goto` terminators, and to some degree their `Span`s,
|
||||
// provided useful context for coverage, such as to count and show when `if` blocks
|
||||
// _without_ `else` blocks execute the `false` case (counting when the body of the `if`
|
||||
// was _not_ taken). In these cases, the `Goto` span is ultimately given a `CoverageSpan`
|
||||
// of 1 character, at the end of it's original `Span`.
|
||||
//
|
||||
// However, in other cases, a visible `CoverageSpan` is not wanted, but the `Goto`
|
||||
// block must still be counted (for example, to contribute its count to an `Expression`
|
||||
// that reports the execution count for some other block). In these cases, the code region
|
||||
// is set to `None`. (See `Instrumentor::is_code_region_redundant()`.)
|
||||
TerminatorKind::Goto { .. } => {
|
||||
Some(function_source_span(terminator.source_info.span.shrink_to_hi(), body_span))
|
||||
}
|
||||
| TerminatorKind::FalseEdge { .. }
|
||||
| TerminatorKind::Goto { .. } => None,
|
||||
|
||||
// Retain spans from all other terminators
|
||||
TerminatorKind::Resume
|
||||
|
@ -749,11 +735,3 @@ 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 }
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn is_goto(term_kind: &TerminatorKind<'tcx>) -> bool {
|
||||
match term_kind {
|
||||
TerminatorKind::Goto { .. } => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue