Auto merge of #84582 - richkadel:issue-84561, r=tmandry
Vastly improves coverage spans for macros Fixes: #84561 This resolves problems where macros like `trace!(...)` would show zero coverage if tracing was disabled, and `assert_eq!(...)` would show zero coverage if the assertion did not fail, because only one coverage span was generated, for the branch. This PR started with an idea that I could just drop branching blocks with same span as expanded macro. (See the fixed issue for more details.) That did help, but it didn't resolve everything. I also needed to add a span specifically for the macro name (plus `!`) to ensure the macro gets coverage even if it's internal expansion adds conditional branching blocks that are retained, and would otherwise drop the outer span. Now that outer span is _only_ the `(argument, list)`, which can safely be dropped now), because the macro name has its own span. While testing, I also noticed the spanview debug output can cause an ICE on a function with no body. The workaround for this is included in this PR (separate commit). r? `@tmandry` cc? `@wesleywiser`
This commit is contained in:
@@ -11,8 +11,9 @@ use rustc_middle::mir::{
|
|||||||
use rustc_middle::ty::TyCtxt;
|
use rustc_middle::ty::TyCtxt;
|
||||||
|
|
||||||
use rustc_span::source_map::original_sp;
|
use rustc_span::source_map::original_sp;
|
||||||
use rustc_span::{BytePos, Span};
|
use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
|
||||||
|
|
||||||
|
use std::cell::RefCell;
|
||||||
use std::cmp::Ordering;
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
#[derive(Debug, Copy, Clone)]
|
#[derive(Debug, Copy, Clone)]
|
||||||
@@ -67,6 +68,8 @@ impl CoverageStatement {
|
|||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub(super) struct CoverageSpan {
|
pub(super) struct CoverageSpan {
|
||||||
pub span: Span,
|
pub span: Span,
|
||||||
|
pub expn_span: Span,
|
||||||
|
pub current_macro_or_none: RefCell<Option<Option<Symbol>>>,
|
||||||
pub bcb: BasicCoverageBlock,
|
pub bcb: BasicCoverageBlock,
|
||||||
pub coverage_statements: Vec<CoverageStatement>,
|
pub coverage_statements: Vec<CoverageStatement>,
|
||||||
pub is_closure: bool,
|
pub is_closure: bool,
|
||||||
@@ -74,12 +77,20 @@ pub(super) struct CoverageSpan {
|
|||||||
|
|
||||||
impl CoverageSpan {
|
impl CoverageSpan {
|
||||||
pub fn for_fn_sig(fn_sig_span: Span) -> Self {
|
pub fn for_fn_sig(fn_sig_span: Span) -> Self {
|
||||||
Self { span: fn_sig_span, bcb: START_BCB, coverage_statements: vec![], is_closure: false }
|
Self {
|
||||||
|
span: fn_sig_span,
|
||||||
|
expn_span: fn_sig_span,
|
||||||
|
current_macro_or_none: Default::default(),
|
||||||
|
bcb: START_BCB,
|
||||||
|
coverage_statements: vec![],
|
||||||
|
is_closure: false,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_statement(
|
pub fn for_statement(
|
||||||
statement: &Statement<'tcx>,
|
statement: &Statement<'tcx>,
|
||||||
span: Span,
|
span: Span,
|
||||||
|
expn_span: Span,
|
||||||
bcb: BasicCoverageBlock,
|
bcb: BasicCoverageBlock,
|
||||||
bb: BasicBlock,
|
bb: BasicBlock,
|
||||||
stmt_index: usize,
|
stmt_index: usize,
|
||||||
@@ -94,15 +105,24 @@ impl CoverageSpan {
|
|||||||
|
|
||||||
Self {
|
Self {
|
||||||
span,
|
span,
|
||||||
|
expn_span,
|
||||||
|
current_macro_or_none: Default::default(),
|
||||||
bcb,
|
bcb,
|
||||||
coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)],
|
coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)],
|
||||||
is_closure,
|
is_closure,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn for_terminator(span: Span, bcb: BasicCoverageBlock, bb: BasicBlock) -> Self {
|
pub fn for_terminator(
|
||||||
|
span: Span,
|
||||||
|
expn_span: Span,
|
||||||
|
bcb: BasicCoverageBlock,
|
||||||
|
bb: BasicBlock,
|
||||||
|
) -> Self {
|
||||||
Self {
|
Self {
|
||||||
span,
|
span,
|
||||||
|
expn_span,
|
||||||
|
current_macro_or_none: Default::default(),
|
||||||
bcb,
|
bcb,
|
||||||
coverage_statements: vec![CoverageStatement::Terminator(bb, span)],
|
coverage_statements: vec![CoverageStatement::Terminator(bb, span)],
|
||||||
is_closure: false,
|
is_closure: false,
|
||||||
@@ -158,6 +178,38 @@ impl CoverageSpan {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join("\n")
|
.join("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// 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
|
||||||
|
.borrow_mut()
|
||||||
|
.get_or_insert_with(|| {
|
||||||
|
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() {
|
||||||
|
if self.expn_span.parent().unwrap_or_else(|| bug!("macro must have a parent")).ctxt()
|
||||||
|
== body_span.ctxt()
|
||||||
|
{
|
||||||
|
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
|
/// Converts the initial set of `CoverageSpan`s (one per MIR `Statement` or `Terminator`) into a
|
||||||
@@ -191,16 +243,23 @@ pub struct CoverageSpans<'a, 'tcx> {
|
|||||||
/// iteration.
|
/// iteration.
|
||||||
some_curr: Option<CoverageSpan>,
|
some_curr: Option<CoverageSpan>,
|
||||||
|
|
||||||
/// The original `span` for `curr`, in case the `curr` span is modified.
|
/// 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,
|
curr_original_span: Span,
|
||||||
|
|
||||||
/// The CoverageSpan from a prior iteration; typically assigned from that iteration's `curr`.
|
/// 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.
|
/// If that `curr` was discarded, `prev` retains its value from the previous iteration.
|
||||||
some_prev: Option<CoverageSpan>,
|
some_prev: Option<CoverageSpan>,
|
||||||
|
|
||||||
/// Assigned from `curr_original_span` from the previous iteration.
|
/// 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,
|
prev_original_span: Span,
|
||||||
|
|
||||||
|
/// A copy of the expn_span from the prior iteration.
|
||||||
|
prev_expn_span: Option<Span>,
|
||||||
|
|
||||||
/// One or more `CoverageSpan`s with the same `Span` but different `BasicCoverageBlock`s, and
|
/// 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.
|
/// 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
|
/// If a new `curr` span also fits this criteria (compared to an existing list of
|
||||||
@@ -255,15 +314,13 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
|
curr_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
|
||||||
some_prev: None,
|
some_prev: None,
|
||||||
prev_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
|
prev_original_span: Span::with_root_ctxt(BytePos(0), BytePos(0)),
|
||||||
|
prev_expn_span: None,
|
||||||
pending_dups: Vec::new(),
|
pending_dups: Vec::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans();
|
let sorted_spans = coverage_spans.mir_to_initial_sorted_coverage_spans();
|
||||||
|
|
||||||
coverage_spans.sorted_spans_iter = Some(sorted_spans.into_iter());
|
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()
|
coverage_spans.to_refined_spans()
|
||||||
}
|
}
|
||||||
@@ -317,10 +374,14 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
/// de-duplicated `CoverageSpan`s.
|
/// de-duplicated `CoverageSpan`s.
|
||||||
fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
|
fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
|
||||||
while self.next_coverage_span() {
|
while self.next_coverage_span() {
|
||||||
if self.curr().is_mergeable(self.prev()) {
|
if self.some_prev.is_none() {
|
||||||
|
debug!(" initial span");
|
||||||
|
self.check_invoked_macro_name_span();
|
||||||
|
} else if self.curr().is_mergeable(self.prev()) {
|
||||||
debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev());
|
debug!(" same bcb (and neither is a closure), merge with prev={:?}", self.prev());
|
||||||
let prev = self.take_prev();
|
let prev = self.take_prev();
|
||||||
self.curr_mut().merge_from(prev);
|
self.curr_mut().merge_from(prev);
|
||||||
|
self.check_invoked_macro_name_span();
|
||||||
// Note that curr.span may now differ from curr_original_span
|
// Note that curr.span may now differ from curr_original_span
|
||||||
} else if self.prev_ends_before_curr() {
|
} else if self.prev_ends_before_curr() {
|
||||||
debug!(
|
debug!(
|
||||||
@@ -329,7 +390,8 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
self.prev()
|
self.prev()
|
||||||
);
|
);
|
||||||
let prev = self.take_prev();
|
let prev = self.take_prev();
|
||||||
self.refined_spans.push(prev);
|
self.push_refined_span(prev);
|
||||||
|
self.check_invoked_macro_name_span();
|
||||||
} else if self.prev().is_closure {
|
} else if self.prev().is_closure {
|
||||||
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
|
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
|
||||||
// next iter
|
// next iter
|
||||||
@@ -342,20 +404,45 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
} else if self.curr().is_closure {
|
} else if self.curr().is_closure {
|
||||||
self.carve_out_span_for_closure();
|
self.carve_out_span_for_closure();
|
||||||
} else if self.prev_original_span == self.curr().span {
|
} else if self.prev_original_span == self.curr().span {
|
||||||
// Note that this compares the new span to `prev_original_span`, which may not
|
// Note that this compares the new (`curr`) span to `prev_original_span`.
|
||||||
// be the full `prev.span` (if merged during the previous iteration).
|
// In this branch, the actual span byte range of `prev_original_span` is not
|
||||||
self.hold_pending_dups_unless_dominated();
|
// 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 self.prev().is_macro_expansion() && self.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={:?}",
|
||||||
|
self.prev()
|
||||||
|
);
|
||||||
|
self.take_curr();
|
||||||
|
} else {
|
||||||
|
self.hold_pending_dups_unless_dominated();
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
self.cutoff_prev_at_overlapping_curr();
|
self.cutoff_prev_at_overlapping_curr();
|
||||||
|
self.check_invoked_macro_name_span();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
debug!(" AT END, adding last prev={:?}", self.prev());
|
debug!(" AT END, adding last prev={:?}", self.prev());
|
||||||
let prev = self.take_prev();
|
let prev = self.take_prev();
|
||||||
let CoverageSpans { pending_dups, mut refined_spans, .. } = self;
|
let pending_dups = self.pending_dups.split_off(0);
|
||||||
for dup in pending_dups {
|
for dup in pending_dups {
|
||||||
debug!(" ...adding at least one pending dup={:?}", dup);
|
debug!(" ...adding at least one pending dup={:?}", dup);
|
||||||
refined_spans.push(dup);
|
self.push_refined_span(dup);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Async functions wrap a closure that implements the body to be executed. The enclosing
|
// Async functions wrap a closure that implements the body to be executed. The enclosing
|
||||||
@@ -365,21 +452,60 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
// excluded. The closure's `Return` is the only one that will be counted. This provides
|
// 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
|
// adequate coverage, and more intuitive counts. (Avoids double-counting the closing brace
|
||||||
// of the function body.)
|
// of the function body.)
|
||||||
let body_ends_with_closure = if let Some(last_covspan) = refined_spans.last() {
|
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()
|
last_covspan.is_closure && last_covspan.span.hi() == self.body_span.hi()
|
||||||
} else {
|
} else {
|
||||||
false
|
false
|
||||||
};
|
};
|
||||||
|
|
||||||
if !body_ends_with_closure {
|
if !body_ends_with_closure {
|
||||||
refined_spans.push(prev);
|
self.push_refined_span(prev);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage
|
// 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
|
// regions for the current function leave room for the closure's own coverage regions
|
||||||
// (injected separately, from the closure's own MIR).
|
// (injected separately, from the closure's own MIR).
|
||||||
refined_spans.retain(|covspan| !covspan.is_closure);
|
self.refined_spans.retain(|covspan| !covspan.is_closure);
|
||||||
refined_spans
|
self.refined_spans
|
||||||
|
}
|
||||||
|
|
||||||
|
fn push_refined_span(&mut self, covspan: CoverageSpan) {
|
||||||
|
let len = self.refined_spans.len();
|
||||||
|
if len > 0 {
|
||||||
|
let last = &mut self.refined_spans[len - 1];
|
||||||
|
if last.is_mergeable(&covspan) {
|
||||||
|
debug!(
|
||||||
|
"merging new refined span with last refined span, last={:?}, covspan={:?}",
|
||||||
|
last, covspan
|
||||||
|
);
|
||||||
|
last.merge_from(covspan);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.refined_spans.push(covspan)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn check_invoked_macro_name_span(&mut self) {
|
||||||
|
if let Some(visible_macro) = self.curr().visible_macro(self.body_span) {
|
||||||
|
if self.prev_expn_span.map_or(true, |prev_expn_span| {
|
||||||
|
self.curr().expn_span.ctxt() != prev_expn_span.ctxt()
|
||||||
|
}) {
|
||||||
|
let merged_prefix_len = self.curr_original_span.lo() - self.curr().span.lo();
|
||||||
|
let after_macro_bang =
|
||||||
|
merged_prefix_len + BytePos(visible_macro.as_str().bytes().count() as u32 + 1);
|
||||||
|
let mut macro_name_cov = self.curr().clone();
|
||||||
|
self.curr_mut().span =
|
||||||
|
self.curr().span.with_lo(self.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 `{}!`, new span={:?}",
|
||||||
|
visible_macro, macro_name_cov
|
||||||
|
);
|
||||||
|
self.push_refined_span(macro_name_cov);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
|
// Generate a set of `CoverageSpan`s from the filtered set of `Statement`s and `Terminator`s of
|
||||||
@@ -401,14 +527,17 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.filter_map(move |(index, statement)| {
|
.filter_map(move |(index, statement)| {
|
||||||
filtered_statement_span(statement, self.body_span).map(|span| {
|
filtered_statement_span(statement, self.body_span).map(
|
||||||
CoverageSpan::for_statement(statement, span, bcb, bb, index)
|
|(span, expn_span)| {
|
||||||
})
|
CoverageSpan::for_statement(
|
||||||
|
statement, span, expn_span, bcb, bb, index,
|
||||||
|
)
|
||||||
|
},
|
||||||
|
)
|
||||||
})
|
})
|
||||||
.chain(
|
.chain(filtered_terminator_span(data.terminator(), self.body_span).map(
|
||||||
filtered_terminator_span(data.terminator(), self.body_span)
|
|(span, expn_span)| CoverageSpan::for_terminator(span, expn_span, bcb, bb),
|
||||||
.map(|span| CoverageSpan::for_terminator(span, bcb, bb)),
|
))
|
||||||
)
|
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
}
|
}
|
||||||
@@ -461,7 +590,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
let pending_dups = self.pending_dups.split_off(0);
|
let pending_dups = self.pending_dups.split_off(0);
|
||||||
for dup in pending_dups.into_iter() {
|
for dup in pending_dups.into_iter() {
|
||||||
debug!(" ...adding at least one pending={:?}", dup);
|
debug!(" ...adding at least one pending={:?}", dup);
|
||||||
self.refined_spans.push(dup);
|
self.push_refined_span(dup);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.pending_dups.clear();
|
self.pending_dups.clear();
|
||||||
@@ -473,12 +602,13 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
/// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order.
|
/// Advance `prev` to `curr` (if any), and `curr` to the next `CoverageSpan` in sorted order.
|
||||||
fn next_coverage_span(&mut self) -> bool {
|
fn next_coverage_span(&mut self) -> bool {
|
||||||
if let Some(curr) = self.some_curr.take() {
|
if let Some(curr) = self.some_curr.take() {
|
||||||
|
self.prev_expn_span = Some(curr.expn_span);
|
||||||
self.some_prev = Some(curr);
|
self.some_prev = Some(curr);
|
||||||
self.prev_original_span = self.curr_original_span;
|
self.prev_original_span = self.curr_original_span;
|
||||||
}
|
}
|
||||||
while let Some(curr) = self.sorted_spans_iter.as_mut().unwrap().next() {
|
while let Some(curr) = self.sorted_spans_iter.as_mut().unwrap().next() {
|
||||||
debug!("FOR curr={:?}", curr);
|
debug!("FOR curr={:?}", curr);
|
||||||
if self.prev_starts_after_next(&curr) {
|
if self.some_prev.is_some() && self.prev_starts_after_next(&curr) {
|
||||||
debug!(
|
debug!(
|
||||||
" prev.span starts after curr.span, so curr will be dropped (skipping past \
|
" prev.span starts after curr.span, so curr will be dropped (skipping past \
|
||||||
closure?); prev={:?}",
|
closure?); prev={:?}",
|
||||||
@@ -535,29 +665,38 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
for mut dup in pending_dups.iter().cloned() {
|
for mut dup in pending_dups.iter().cloned() {
|
||||||
dup.span = dup.span.with_hi(left_cutoff);
|
dup.span = dup.span.with_hi(left_cutoff);
|
||||||
debug!(" ...and at least one pre_closure dup={:?}", dup);
|
debug!(" ...and at least one pre_closure dup={:?}", dup);
|
||||||
self.refined_spans.push(dup);
|
self.push_refined_span(dup);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.refined_spans.push(pre_closure);
|
self.push_refined_span(pre_closure);
|
||||||
}
|
}
|
||||||
if has_post_closure_span {
|
if has_post_closure_span {
|
||||||
// Update prev.span to start after the closure (and discard curr)
|
// 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);
|
self.prev_mut().span = self.prev().span.with_lo(right_cutoff);
|
||||||
self.prev_original_span = self.prev().span;
|
debug!(" Mutated prev.span to start after the closure. prev={:?}", self.prev());
|
||||||
for dup in pending_dups.iter_mut() {
|
for dup in pending_dups.iter_mut() {
|
||||||
|
debug!(" ...and at least one overlapping dup={:?}", dup);
|
||||||
dup.span = dup.span.with_lo(right_cutoff);
|
dup.span = dup.span.with_lo(right_cutoff);
|
||||||
}
|
}
|
||||||
self.pending_dups.append(&mut pending_dups);
|
self.pending_dups.append(&mut pending_dups);
|
||||||
let closure_covspan = self.take_curr();
|
let closure_covspan = self.take_curr();
|
||||||
self.refined_spans.push(closure_covspan); // since self.prev() was already updated
|
self.push_refined_span(closure_covspan); // since self.prev() was already updated
|
||||||
} else {
|
} else {
|
||||||
pending_dups.clear();
|
pending_dups.clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Called if `curr.span` equals `prev_original_span` (and potentially equal to all
|
/// 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
|
/// `pending_dups` spans, if any). Keep in mind, `prev.span()` may have been changed.
|
||||||
/// is less than (further left of) `prev_original_span.lo()`.
|
/// 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 determinating the dominator
|
||||||
|
/// relationship.
|
||||||
///
|
///
|
||||||
/// When two `CoverageSpan`s have the same `Span`, dominated spans can be discarded; but if
|
/// 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,
|
/// neither `CoverageSpan` dominates the other, both (or possibly more than two) are held,
|
||||||
@@ -640,7 +779,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
} else {
|
} else {
|
||||||
debug!(" ... adding modified prev={:?}", self.prev());
|
debug!(" ... adding modified prev={:?}", self.prev());
|
||||||
let prev = self.take_prev();
|
let prev = self.take_prev();
|
||||||
self.refined_spans.push(prev);
|
self.push_refined_span(prev);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// with `pending_dups`, `prev` cannot have any statements that don't overlap
|
// with `pending_dups`, `prev` cannot have any statements that don't overlap
|
||||||
@@ -653,10 +792,13 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See `function_source_span()` for a description of the two returned spans.
|
||||||
|
/// If the MIR `Statement` is not contributive to computing coverage spans,
|
||||||
|
/// returns `None`.
|
||||||
pub(super) fn filtered_statement_span(
|
pub(super) fn filtered_statement_span(
|
||||||
statement: &'a Statement<'tcx>,
|
statement: &'a Statement<'tcx>,
|
||||||
body_span: Span,
|
body_span: Span,
|
||||||
) -> Option<Span> {
|
) -> Option<(Span, Span)> {
|
||||||
match statement.kind {
|
match statement.kind {
|
||||||
// These statements have spans that are often outside the scope of the executed source code
|
// These statements have spans that are often outside the scope of the executed source code
|
||||||
// for their parent `BasicBlock`.
|
// for their parent `BasicBlock`.
|
||||||
@@ -698,10 +840,13 @@ pub(super) fn filtered_statement_span(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// See `function_source_span()` for a description of the two returned spans.
|
||||||
|
/// If the MIR `Terminator` is not contributive to computing coverage spans,
|
||||||
|
/// returns `None`.
|
||||||
pub(super) fn filtered_terminator_span(
|
pub(super) fn filtered_terminator_span(
|
||||||
terminator: &'a Terminator<'tcx>,
|
terminator: &'a Terminator<'tcx>,
|
||||||
body_span: Span,
|
body_span: Span,
|
||||||
) -> Option<Span> {
|
) -> Option<(Span, Span)> {
|
||||||
match terminator.kind {
|
match terminator.kind {
|
||||||
// These terminators have spans that don't positively contribute to computing a reasonable
|
// 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
|
// span of actually executed source code. (For example, SwitchInt terminators extracted from
|
||||||
@@ -741,8 +886,23 @@ pub(super) fn filtered_terminator_span(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Returns two spans from the given span (the span associated with a
|
||||||
|
/// `Statement` or `Terminator`):
|
||||||
|
///
|
||||||
|
/// 1. An extrapolated span (pre-expansion[^1]) corresponding to a range within
|
||||||
|
/// the function's body source. This span is guaranteed to be contained
|
||||||
|
/// within, or equal to, the `body_span`. If the extrapolated span is not
|
||||||
|
/// contained within the `body_span`, the `body_span` is returned.
|
||||||
|
/// 2. The actual `span` value from the `Statement`, before expansion.
|
||||||
|
///
|
||||||
|
/// Only the first span is used when computing coverage code regions. The second
|
||||||
|
/// span is useful if additional expansion data is needed (such as to look up
|
||||||
|
/// the macro name for a composed span within that macro).)
|
||||||
|
///
|
||||||
|
/// [^1]Expansions result from Rust syntax including macros, syntactic
|
||||||
|
/// sugar, etc.).
|
||||||
#[inline]
|
#[inline]
|
||||||
fn function_source_span(span: Span, body_span: Span) -> Span {
|
fn function_source_span(span: Span, body_span: Span) -> (Span, Span) {
|
||||||
let span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
|
let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
|
||||||
if body_span.contains(span) { span } else { body_span }
|
(if body_span.contains(original_span) { original_span } else { body_span }, span)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
//! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR
|
//! This crate hosts a selection of "unit tests" for components of the `InstrumentCoverage` MIR
|
||||||
//! pass.
|
//! pass.
|
||||||
//!
|
//!
|
||||||
|
//! ```shell
|
||||||
|
//! ./x.py test --keep-stage 1 compiler/rustc_mir --test-args '--show-output coverage'
|
||||||
|
//! ```
|
||||||
|
//!
|
||||||
//! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage`
|
//! The tests construct a few "mock" objects, as needed, to support the `InstrumentCoverage`
|
||||||
//! functions and algorithms. Mocked objects include instances of `mir::Body`; including
|
//! functions and algorithms. Mocked objects include instances of `mir::Body`; including
|
||||||
//! `Terminator`s of various `kind`s, and `Span` objects. Some functions used by or used on
|
//! `Terminator`s of various `kind`s, and `Span` objects. Some functions used by or used on
|
||||||
@@ -679,10 +683,15 @@ fn test_make_bcb_counters() {
|
|||||||
let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
|
let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
|
||||||
let mut coverage_spans = Vec::new();
|
let mut coverage_spans = Vec::new();
|
||||||
for (bcb, data) in basic_coverage_blocks.iter_enumerated() {
|
for (bcb, data) in basic_coverage_blocks.iter_enumerated() {
|
||||||
if let Some(span) =
|
if let Some((span, expn_span)) =
|
||||||
spans::filtered_terminator_span(data.terminator(&mir_body), body_span)
|
spans::filtered_terminator_span(data.terminator(&mir_body), body_span)
|
||||||
{
|
{
|
||||||
coverage_spans.push(spans::CoverageSpan::for_terminator(span, bcb, data.last_bb()));
|
coverage_spans.push(spans::CoverageSpan::for_terminator(
|
||||||
|
span,
|
||||||
|
expn_span,
|
||||||
|
bcb,
|
||||||
|
data.last_bb(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let mut coverage_counters = counters::CoverageCounters::new(0);
|
let mut coverage_counters = counters::CoverageCounters::new(0);
|
||||||
|
|||||||
@@ -99,7 +99,11 @@ where
|
|||||||
W: Write,
|
W: Write,
|
||||||
{
|
{
|
||||||
let def_id = body.source.def_id();
|
let def_id = body.source.def_id();
|
||||||
let body_span = hir_body(tcx, def_id).value.span;
|
let hir_body = hir_body(tcx, def_id);
|
||||||
|
if hir_body.is_none() {
|
||||||
|
return Ok(());
|
||||||
|
}
|
||||||
|
let body_span = hir_body.unwrap().value.span;
|
||||||
let mut span_viewables = Vec::new();
|
let mut span_viewables = Vec::new();
|
||||||
for (bb, data) in body.basic_blocks().iter_enumerated() {
|
for (bb, data) in body.basic_blocks().iter_enumerated() {
|
||||||
match spanview {
|
match spanview {
|
||||||
@@ -664,19 +668,16 @@ fn fn_span<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Span {
|
|||||||
let hir_id =
|
let hir_id =
|
||||||
tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local"));
|
tcx.hir().local_def_id_to_hir_id(def_id.as_local().expect("expected DefId is local"));
|
||||||
let fn_decl_span = tcx.hir().span(hir_id);
|
let fn_decl_span = tcx.hir().span(hir_id);
|
||||||
let body_span = hir_body(tcx, def_id).value.span;
|
if let Some(body_span) = hir_body(tcx, def_id).map(|hir_body| hir_body.value.span) {
|
||||||
if fn_decl_span.ctxt() == body_span.ctxt() {
|
if fn_decl_span.ctxt() == body_span.ctxt() { fn_decl_span.to(body_span) } else { body_span }
|
||||||
fn_decl_span.to(body_span)
|
|
||||||
} else {
|
} else {
|
||||||
// This probably occurs for functions defined via macros
|
fn_decl_span
|
||||||
body_span
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> &'tcx rustc_hir::Body<'tcx> {
|
fn hir_body<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> Option<&'tcx rustc_hir::Body<'tcx>> {
|
||||||
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
|
let hir_node = tcx.hir().get_if_local(def_id).expect("expected DefId is local");
|
||||||
let fn_body_id = hir::map::associated_body(hir_node).expect("HIR node is a function with body");
|
hir::map::associated_body(hir_node).map(|fn_body_id| tcx.hir().body(fn_body_id))
|
||||||
tcx.hir().body(fn_body_id)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn escape_html(s: &str) -> String {
|
fn escape_html(s: &str) -> String {
|
||||||
|
|||||||
@@ -0,0 +1,42 @@
|
|||||||
|
1| |// compile-flags: --edition=2018
|
||||||
|
2| |#![feature(no_coverage)]
|
||||||
|
3| |
|
||||||
|
4| |macro_rules! bail {
|
||||||
|
5| | ($msg:literal $(,)?) => {
|
||||||
|
6| | if $msg.len() > 0 {
|
||||||
|
7| | println!("no msg");
|
||||||
|
8| | } else {
|
||||||
|
9| | println!($msg);
|
||||||
|
10| | }
|
||||||
|
11| | return Err(String::from($msg));
|
||||||
|
12| | };
|
||||||
|
13| |}
|
||||||
|
14| |
|
||||||
|
15| |macro_rules! on_error {
|
||||||
|
16| | ($value:expr, $error_message:expr) => {
|
||||||
|
17| 0| $value.or_else(|e| {
|
||||||
|
18| 0| let message = format!($error_message, e);
|
||||||
|
19| 0| if message.len() > 0 {
|
||||||
|
20| 0| println!("{}", message);
|
||||||
|
21| 0| Ok(String::from("ok"))
|
||||||
|
22| | } else {
|
||||||
|
23| 0| bail!("error");
|
||||||
|
24| | }
|
||||||
|
25| 0| })
|
||||||
|
26| | };
|
||||||
|
27| |}
|
||||||
|
28| |
|
||||||
|
29| 1|fn load_configuration_files() -> Result<String, String> {
|
||||||
|
30| 1| Ok(String::from("config"))
|
||||||
|
31| 1|}
|
||||||
|
32| |
|
||||||
|
33| 1|pub fn main() -> Result<(), String> {
|
||||||
|
34| 1| println!("Starting service");
|
||||||
|
35| 1| let config = on_error!(load_configuration_files(), "Error loading configs: {}")?;
|
||||||
|
^0
|
||||||
|
36| |
|
||||||
|
37| 1| let startup_delay_duration = String::from("arg");
|
||||||
|
38| 1| let _ = (config, startup_delay_duration);
|
||||||
|
39| 1| Ok(())
|
||||||
|
40| 1|}
|
||||||
|
|
||||||
@@ -0,0 +1,83 @@
|
|||||||
|
1| |// compile-flags: --edition=2018
|
||||||
|
2| |#![feature(no_coverage)]
|
||||||
|
3| |
|
||||||
|
4| |macro_rules! bail {
|
||||||
|
5| | ($msg:literal $(,)?) => {
|
||||||
|
6| | if $msg.len() > 0 {
|
||||||
|
7| | println!("no msg");
|
||||||
|
8| | } else {
|
||||||
|
9| | println!($msg);
|
||||||
|
10| | }
|
||||||
|
11| | return Err(String::from($msg));
|
||||||
|
12| | };
|
||||||
|
13| |}
|
||||||
|
14| |
|
||||||
|
15| |macro_rules! on_error {
|
||||||
|
16| | ($value:expr, $error_message:expr) => {
|
||||||
|
17| 0| $value.or_else(|e| {
|
||||||
|
18| 0| let message = format!($error_message, e);
|
||||||
|
19| 0| if message.len() > 0 {
|
||||||
|
20| 0| println!("{}", message);
|
||||||
|
21| 0| Ok(String::from("ok"))
|
||||||
|
22| | } else {
|
||||||
|
23| 0| bail!("error");
|
||||||
|
24| | }
|
||||||
|
25| 0| })
|
||||||
|
26| | };
|
||||||
|
27| |}
|
||||||
|
28| |
|
||||||
|
29| 1|fn load_configuration_files() -> Result<String, String> {
|
||||||
|
30| 1| Ok(String::from("config"))
|
||||||
|
31| 1|}
|
||||||
|
32| |
|
||||||
|
33| 1|pub async fn test() -> Result<(), String> {
|
||||||
|
34| 1| println!("Starting service");
|
||||||
|
35| 1| let config = on_error!(load_configuration_files(), "Error loading configs: {}")?;
|
||||||
|
^0
|
||||||
|
36| |
|
||||||
|
37| 1| let startup_delay_duration = String::from("arg");
|
||||||
|
38| 1| let _ = (config, startup_delay_duration);
|
||||||
|
39| 1| Ok(())
|
||||||
|
40| 1|}
|
||||||
|
41| |
|
||||||
|
42| |#[no_coverage]
|
||||||
|
43| |fn main() {
|
||||||
|
44| | executor::block_on(test());
|
||||||
|
45| |}
|
||||||
|
46| |
|
||||||
|
47| |mod executor {
|
||||||
|
48| | use core::{
|
||||||
|
49| | future::Future,
|
||||||
|
50| | pin::Pin,
|
||||||
|
51| | task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||||
|
52| | };
|
||||||
|
53| |
|
||||||
|
54| | #[no_coverage]
|
||||||
|
55| | pub fn block_on<F: Future>(mut future: F) -> F::Output {
|
||||||
|
56| | let mut future = unsafe { Pin::new_unchecked(&mut future) };
|
||||||
|
57| | use std::hint::unreachable_unchecked;
|
||||||
|
58| | static VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||||
|
59| |
|
||||||
|
60| | #[no_coverage]
|
||||||
|
61| | |_| unsafe { unreachable_unchecked() }, // clone
|
||||||
|
62| |
|
||||||
|
63| | #[no_coverage]
|
||||||
|
64| | |_| unsafe { unreachable_unchecked() }, // wake
|
||||||
|
65| |
|
||||||
|
66| | #[no_coverage]
|
||||||
|
67| | |_| unsafe { unreachable_unchecked() }, // wake_by_ref
|
||||||
|
68| |
|
||||||
|
69| | #[no_coverage]
|
||||||
|
70| | |_| (),
|
||||||
|
71| | );
|
||||||
|
72| | let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
|
||||||
|
73| | let mut context = Context::from_waker(&waker);
|
||||||
|
74| |
|
||||||
|
75| | loop {
|
||||||
|
76| | if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
|
||||||
|
77| | break val;
|
||||||
|
78| | }
|
||||||
|
79| | }
|
||||||
|
80| | }
|
||||||
|
81| |}
|
||||||
|
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
1| |#![allow(unused_assignments, unused_variables, dead_code)]
|
1| |#![allow(unused_assignments, unused_variables, dead_code)]
|
||||||
2| |
|
2| |
|
||||||
3| 1|fn main() {
|
3| 1|fn main() {
|
||||||
4| | // Initialize test constants in a way that cannot be determined at compile time, to ensure
|
4| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure
|
||||||
5| | // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
|
5| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
|
||||||
6| | // dependent conditions.
|
6| 1| // dependent conditions.
|
||||||
7| 1| let is_true = std::env::args().len() == 1;
|
7| 1| let is_true = std::env::args().len() == 1;
|
||||||
8| 1|
|
8| 1|
|
||||||
9| 1| let mut countdown = 0;
|
9| 1| let mut countdown = 0;
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
5| |
|
5| |
|
||||||
6| 1|fn main() {
|
6| 1|fn main() {
|
||||||
7| 1| let bar = Foo(1);
|
7| 1| let bar = Foo(1);
|
||||||
8| 0| assert_eq!(bar, Foo(1));
|
8| 1| assert_eq!(bar, Foo(1));
|
||||||
9| 1| let baz = Foo(0);
|
9| 1| let baz = Foo(0);
|
||||||
10| 0| assert_ne!(baz, Foo(1));
|
10| 1| assert_ne!(baz, Foo(1));
|
||||||
11| 1| println!("{:?}", Foo(1));
|
11| 1| println!("{:?}", Foo(1));
|
||||||
12| 1| println!("{:?}", bar);
|
12| 1| println!("{:?}", bar);
|
||||||
13| 1| println!("{:?}", baz);
|
13| 1| println!("{:?}", baz);
|
||||||
|
|||||||
@@ -0,0 +1,195 @@
|
|||||||
|
1| |// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results.
|
||||||
|
2| |
|
||||||
|
3| |// expect-exit-status-101
|
||||||
|
4| 21|#[derive(PartialEq, Eq)]
|
||||||
|
------------------
|
||||||
|
| <issue_84561::Foo as core::cmp::PartialEq>::eq:
|
||||||
|
| 4| 21|#[derive(PartialEq, Eq)]
|
||||||
|
------------------
|
||||||
|
| Unexecuted instantiation: <issue_84561::Foo as core::cmp::PartialEq>::ne
|
||||||
|
------------------
|
||||||
|
5| |struct Foo(u32);
|
||||||
|
6| 1|fn test3() {
|
||||||
|
7| 1| let is_true = std::env::args().len() == 1;
|
||||||
|
8| 1| let bar = Foo(1);
|
||||||
|
9| 1| assert_eq!(bar, Foo(1));
|
||||||
|
10| 1| let baz = Foo(0);
|
||||||
|
11| 1| assert_ne!(baz, Foo(1));
|
||||||
|
12| 1| println!("{:?}", Foo(1));
|
||||||
|
13| 1| println!("{:?}", bar);
|
||||||
|
14| 1| println!("{:?}", baz);
|
||||||
|
15| 1|
|
||||||
|
16| 1| assert_eq!(Foo(1), Foo(1));
|
||||||
|
17| 1| assert_ne!(Foo(0), Foo(1));
|
||||||
|
18| 1| assert_eq!(Foo(2), Foo(2));
|
||||||
|
19| 1| let bar = Foo(0);
|
||||||
|
20| 1| assert_ne!(bar, Foo(3));
|
||||||
|
21| 1| assert_ne!(Foo(0), Foo(4));
|
||||||
|
22| 1| assert_eq!(Foo(3), Foo(3), "with a message");
|
||||||
|
^0
|
||||||
|
23| 1| println!("{:?}", bar);
|
||||||
|
24| 1| println!("{:?}", Foo(1));
|
||||||
|
25| 1|
|
||||||
|
26| 1| assert_ne!(Foo(0), Foo(5), "{}", if is_true { "true message" } else { "false message" });
|
||||||
|
^0 ^0 ^0
|
||||||
|
27| 1| assert_ne!(
|
||||||
|
28| | Foo(0)
|
||||||
|
29| | ,
|
||||||
|
30| | Foo(5)
|
||||||
|
31| | ,
|
||||||
|
32| 0| "{}"
|
||||||
|
33| 0| ,
|
||||||
|
34| 0| if
|
||||||
|
35| 0| is_true
|
||||||
|
36| | {
|
||||||
|
37| 0| "true message"
|
||||||
|
38| | } else {
|
||||||
|
39| 0| "false message"
|
||||||
|
40| | }
|
||||||
|
41| | );
|
||||||
|
42| |
|
||||||
|
43| 1| let is_true = std::env::args().len() == 1;
|
||||||
|
44| 1|
|
||||||
|
45| 1| assert_eq!(
|
||||||
|
46| 1| Foo(1),
|
||||||
|
47| 1| Foo(1)
|
||||||
|
48| 1| );
|
||||||
|
49| 1| assert_ne!(
|
||||||
|
50| 1| Foo(0),
|
||||||
|
51| 1| Foo(1)
|
||||||
|
52| 1| );
|
||||||
|
53| 1| assert_eq!(
|
||||||
|
54| 1| Foo(2),
|
||||||
|
55| 1| Foo(2)
|
||||||
|
56| 1| );
|
||||||
|
57| 1| let bar = Foo(1);
|
||||||
|
58| 1| assert_ne!(
|
||||||
|
59| 1| bar,
|
||||||
|
60| 1| Foo(3)
|
||||||
|
61| 1| );
|
||||||
|
62| 1| if is_true {
|
||||||
|
63| 1| assert_ne!(
|
||||||
|
64| 1| Foo(0),
|
||||||
|
65| 1| Foo(4)
|
||||||
|
66| 1| );
|
||||||
|
67| | } else {
|
||||||
|
68| 0| assert_eq!(
|
||||||
|
69| 0| Foo(3),
|
||||||
|
70| 0| Foo(3)
|
||||||
|
71| 0| );
|
||||||
|
72| | }
|
||||||
|
73| 1| if is_true {
|
||||||
|
74| 1| assert_ne!(
|
||||||
|
75| | Foo(0),
|
||||||
|
76| | Foo(4),
|
||||||
|
77| 0| "with a message"
|
||||||
|
78| | );
|
||||||
|
79| | } else {
|
||||||
|
80| 0| assert_eq!(
|
||||||
|
81| | Foo(3),
|
||||||
|
82| | Foo(3),
|
||||||
|
83| 0| "with a message"
|
||||||
|
84| | );
|
||||||
|
85| | }
|
||||||
|
86| 1| assert_ne!(
|
||||||
|
87| 1| if is_true {
|
||||||
|
88| 1| Foo(0)
|
||||||
|
89| | } else {
|
||||||
|
90| 0| Foo(1)
|
||||||
|
91| | },
|
||||||
|
92| | Foo(5)
|
||||||
|
93| | );
|
||||||
|
94| 1| assert_ne!(
|
||||||
|
95| 1| Foo(5),
|
||||||
|
96| 1| if is_true {
|
||||||
|
97| 1| Foo(0)
|
||||||
|
98| | } else {
|
||||||
|
99| 0| Foo(1)
|
||||||
|
100| | }
|
||||||
|
101| | );
|
||||||
|
102| 1| assert_ne!(
|
||||||
|
103| 1| if is_true {
|
||||||
|
104| 1| assert_eq!(
|
||||||
|
105| 1| Foo(3),
|
||||||
|
106| 1| Foo(3)
|
||||||
|
107| 1| );
|
||||||
|
108| 1| Foo(0)
|
||||||
|
109| | } else {
|
||||||
|
110| 0| assert_ne!(
|
||||||
|
111| 0| if is_true {
|
||||||
|
112| 0| Foo(0)
|
||||||
|
113| | } else {
|
||||||
|
114| 0| Foo(1)
|
||||||
|
115| | },
|
||||||
|
116| | Foo(5)
|
||||||
|
117| | );
|
||||||
|
118| 0| Foo(1)
|
||||||
|
119| | },
|
||||||
|
120| | Foo(5),
|
||||||
|
121| 0| "with a message"
|
||||||
|
122| | );
|
||||||
|
123| 1| assert_eq!(
|
||||||
|
124| | Foo(1),
|
||||||
|
125| | Foo(3),
|
||||||
|
126| 1| "this assert should fail"
|
||||||
|
127| | );
|
||||||
|
128| 0| assert_eq!(
|
||||||
|
129| | Foo(3),
|
||||||
|
130| | Foo(3),
|
||||||
|
131| 0| "this assert should not be reached"
|
||||||
|
132| | );
|
||||||
|
133| 0|}
|
||||||
|
134| |
|
||||||
|
135| |impl std::fmt::Debug for Foo {
|
||||||
|
136| | fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
137| 7| write!(f, "try and succeed")?;
|
||||||
|
^0
|
||||||
|
138| 7| Ok(())
|
||||||
|
139| 7| }
|
||||||
|
140| |}
|
||||||
|
141| |
|
||||||
|
142| |static mut DEBUG_LEVEL_ENABLED: bool = false;
|
||||||
|
143| |
|
||||||
|
144| |macro_rules! debug {
|
||||||
|
145| | ($($arg:tt)+) => (
|
||||||
|
146| | if unsafe { DEBUG_LEVEL_ENABLED } {
|
||||||
|
147| | println!($($arg)+);
|
||||||
|
148| | }
|
||||||
|
149| | );
|
||||||
|
150| |}
|
||||||
|
151| |
|
||||||
|
152| 1|fn test1() {
|
||||||
|
153| 1| debug!("debug is enabled");
|
||||||
|
^0
|
||||||
|
154| 1| debug!("debug is enabled");
|
||||||
|
^0
|
||||||
|
155| 1| let _ = 0;
|
||||||
|
156| 1| debug!("debug is enabled");
|
||||||
|
^0
|
||||||
|
157| 1| unsafe {
|
||||||
|
158| 1| DEBUG_LEVEL_ENABLED = true;
|
||||||
|
159| 1| }
|
||||||
|
160| 1| debug!("debug is enabled");
|
||||||
|
161| 1|}
|
||||||
|
162| |
|
||||||
|
163| |macro_rules! call_debug {
|
||||||
|
164| | ($($arg:tt)+) => (
|
||||||
|
165| 1| fn call_print(s: &str) {
|
||||||
|
166| 1| print!("{}", s);
|
||||||
|
167| 1| }
|
||||||
|
168| |
|
||||||
|
169| | call_print("called from call_debug: ");
|
||||||
|
170| | debug!($($arg)+);
|
||||||
|
171| | );
|
||||||
|
172| |}
|
||||||
|
173| |
|
||||||
|
174| 1|fn test2() {
|
||||||
|
175| 1| call_debug!("debug is enabled");
|
||||||
|
176| 1|}
|
||||||
|
177| |
|
||||||
|
178| 1|fn main() {
|
||||||
|
179| 1| test1();
|
||||||
|
180| 1| test2();
|
||||||
|
181| 1| test3();
|
||||||
|
182| 1|}
|
||||||
|
|
||||||
@@ -3,9 +3,9 @@
|
|||||||
3| |use std::fmt::Debug;
|
3| |use std::fmt::Debug;
|
||||||
4| |
|
4| |
|
||||||
5| 1|pub fn used_function() {
|
5| 1|pub fn used_function() {
|
||||||
6| | // Initialize test constants in a way that cannot be determined at compile time, to ensure
|
6| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure
|
||||||
7| | // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
|
7| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
|
||||||
8| | // dependent conditions.
|
8| 1| // dependent conditions.
|
||||||
9| 1| let is_true = std::env::args().len() == 1;
|
9| 1| let is_true = std::env::args().len() == 1;
|
||||||
10| 1| let mut countdown = 0;
|
10| 1| let mut countdown = 0;
|
||||||
11| 1| if is_true {
|
11| 1| if is_true {
|
||||||
@@ -36,12 +36,12 @@
|
|||||||
22| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg);
|
22| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg);
|
||||||
23| 2|}
|
23| 2|}
|
||||||
------------------
|
------------------
|
||||||
| used_crate::used_only_from_this_lib_crate_generic_function::<&str>:
|
| used_crate::used_only_from_this_lib_crate_generic_function::<alloc::vec::Vec<i32>>:
|
||||||
| 21| 1|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) {
|
| 21| 1|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) {
|
||||||
| 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg);
|
| 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg);
|
||||||
| 23| 1|}
|
| 23| 1|}
|
||||||
------------------
|
------------------
|
||||||
| used_crate::used_only_from_this_lib_crate_generic_function::<alloc::vec::Vec<i32>>:
|
| used_crate::used_only_from_this_lib_crate_generic_function::<&str>:
|
||||||
| 21| 1|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) {
|
| 21| 1|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) {
|
||||||
| 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg);
|
| 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg);
|
||||||
| 23| 1|}
|
| 23| 1|}
|
||||||
|
|||||||
@@ -5,9 +5,9 @@
|
|||||||
5| |use std::fmt::Debug;
|
5| |use std::fmt::Debug;
|
||||||
6| |
|
6| |
|
||||||
7| 1|pub fn used_function() {
|
7| 1|pub fn used_function() {
|
||||||
8| | // Initialize test constants in a way that cannot be determined at compile time, to ensure
|
8| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure
|
||||||
9| | // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
|
9| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
|
||||||
10| | // dependent conditions.
|
10| 1| // dependent conditions.
|
||||||
11| 1| let is_true = std::env::args().len() == 1;
|
11| 1| let is_true = std::env::args().len() == 1;
|
||||||
12| 1| let mut countdown = 0;
|
12| 1| let mut countdown = 0;
|
||||||
13| 1| if is_true {
|
13| 1| if is_true {
|
||||||
@@ -19,9 +19,9 @@
|
|||||||
18| |
|
18| |
|
||||||
19| |#[inline(always)]
|
19| |#[inline(always)]
|
||||||
20| 1|pub fn used_inline_function() {
|
20| 1|pub fn used_inline_function() {
|
||||||
21| | // Initialize test constants in a way that cannot be determined at compile time, to ensure
|
21| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure
|
||||||
22| | // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
|
22| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
|
||||||
23| | // dependent conditions.
|
23| 1| // dependent conditions.
|
||||||
24| 1| let is_true = std::env::args().len() == 1;
|
24| 1| let is_true = std::env::args().len() == 1;
|
||||||
25| 1| let mut countdown = 0;
|
25| 1| let mut countdown = 0;
|
||||||
26| 1| if is_true {
|
26| 1| if is_true {
|
||||||
|
|||||||
40
src/test/run-make-fulldeps/coverage/closure_macro.rs
Normal file
40
src/test/run-make-fulldeps/coverage/closure_macro.rs
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
// compile-flags: --edition=2018
|
||||||
|
#![feature(no_coverage)]
|
||||||
|
|
||||||
|
macro_rules! bail {
|
||||||
|
($msg:literal $(,)?) => {
|
||||||
|
if $msg.len() > 0 {
|
||||||
|
println!("no msg");
|
||||||
|
} else {
|
||||||
|
println!($msg);
|
||||||
|
}
|
||||||
|
return Err(String::from($msg));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! on_error {
|
||||||
|
($value:expr, $error_message:expr) => {
|
||||||
|
$value.or_else(|e| {
|
||||||
|
let message = format!($error_message, e);
|
||||||
|
if message.len() > 0 {
|
||||||
|
println!("{}", message);
|
||||||
|
Ok(String::from("ok"))
|
||||||
|
} else {
|
||||||
|
bail!("error");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_configuration_files() -> Result<String, String> {
|
||||||
|
Ok(String::from("config"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn main() -> Result<(), String> {
|
||||||
|
println!("Starting service");
|
||||||
|
let config = on_error!(load_configuration_files(), "Error loading configs: {}")?;
|
||||||
|
|
||||||
|
let startup_delay_duration = String::from("arg");
|
||||||
|
let _ = (config, startup_delay_duration);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
81
src/test/run-make-fulldeps/coverage/closure_macro_async.rs
Normal file
81
src/test/run-make-fulldeps/coverage/closure_macro_async.rs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
// compile-flags: --edition=2018
|
||||||
|
#![feature(no_coverage)]
|
||||||
|
|
||||||
|
macro_rules! bail {
|
||||||
|
($msg:literal $(,)?) => {
|
||||||
|
if $msg.len() > 0 {
|
||||||
|
println!("no msg");
|
||||||
|
} else {
|
||||||
|
println!($msg);
|
||||||
|
}
|
||||||
|
return Err(String::from($msg));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! on_error {
|
||||||
|
($value:expr, $error_message:expr) => {
|
||||||
|
$value.or_else(|e| {
|
||||||
|
let message = format!($error_message, e);
|
||||||
|
if message.len() > 0 {
|
||||||
|
println!("{}", message);
|
||||||
|
Ok(String::from("ok"))
|
||||||
|
} else {
|
||||||
|
bail!("error");
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn load_configuration_files() -> Result<String, String> {
|
||||||
|
Ok(String::from("config"))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn test() -> Result<(), String> {
|
||||||
|
println!("Starting service");
|
||||||
|
let config = on_error!(load_configuration_files(), "Error loading configs: {}")?;
|
||||||
|
|
||||||
|
let startup_delay_duration = String::from("arg");
|
||||||
|
let _ = (config, startup_delay_duration);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
#[no_coverage]
|
||||||
|
fn main() {
|
||||||
|
executor::block_on(test());
|
||||||
|
}
|
||||||
|
|
||||||
|
mod executor {
|
||||||
|
use core::{
|
||||||
|
future::Future,
|
||||||
|
pin::Pin,
|
||||||
|
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
|
||||||
|
};
|
||||||
|
|
||||||
|
#[no_coverage]
|
||||||
|
pub fn block_on<F: Future>(mut future: F) -> F::Output {
|
||||||
|
let mut future = unsafe { Pin::new_unchecked(&mut future) };
|
||||||
|
use std::hint::unreachable_unchecked;
|
||||||
|
static VTABLE: RawWakerVTable = RawWakerVTable::new(
|
||||||
|
|
||||||
|
#[no_coverage]
|
||||||
|
|_| unsafe { unreachable_unchecked() }, // clone
|
||||||
|
|
||||||
|
#[no_coverage]
|
||||||
|
|_| unsafe { unreachable_unchecked() }, // wake
|
||||||
|
|
||||||
|
#[no_coverage]
|
||||||
|
|_| unsafe { unreachable_unchecked() }, // wake_by_ref
|
||||||
|
|
||||||
|
#[no_coverage]
|
||||||
|
|_| (),
|
||||||
|
);
|
||||||
|
let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
|
||||||
|
let mut context = Context::from_waker(&waker);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
|
||||||
|
break val;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
182
src/test/run-make-fulldeps/coverage/issue-84561.rs
Normal file
182
src/test/run-make-fulldeps/coverage/issue-84561.rs
Normal file
@@ -0,0 +1,182 @@
|
|||||||
|
// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results.
|
||||||
|
|
||||||
|
// expect-exit-status-101
|
||||||
|
#[derive(PartialEq, Eq)]
|
||||||
|
struct Foo(u32);
|
||||||
|
fn test3() {
|
||||||
|
let is_true = std::env::args().len() == 1;
|
||||||
|
let bar = Foo(1);
|
||||||
|
assert_eq!(bar, Foo(1));
|
||||||
|
let baz = Foo(0);
|
||||||
|
assert_ne!(baz, Foo(1));
|
||||||
|
println!("{:?}", Foo(1));
|
||||||
|
println!("{:?}", bar);
|
||||||
|
println!("{:?}", baz);
|
||||||
|
|
||||||
|
assert_eq!(Foo(1), Foo(1));
|
||||||
|
assert_ne!(Foo(0), Foo(1));
|
||||||
|
assert_eq!(Foo(2), Foo(2));
|
||||||
|
let bar = Foo(0);
|
||||||
|
assert_ne!(bar, Foo(3));
|
||||||
|
assert_ne!(Foo(0), Foo(4));
|
||||||
|
assert_eq!(Foo(3), Foo(3), "with a message");
|
||||||
|
println!("{:?}", bar);
|
||||||
|
println!("{:?}", Foo(1));
|
||||||
|
|
||||||
|
assert_ne!(Foo(0), Foo(5), "{}", if is_true { "true message" } else { "false message" });
|
||||||
|
assert_ne!(
|
||||||
|
Foo(0)
|
||||||
|
,
|
||||||
|
Foo(5)
|
||||||
|
,
|
||||||
|
"{}"
|
||||||
|
,
|
||||||
|
if
|
||||||
|
is_true
|
||||||
|
{
|
||||||
|
"true message"
|
||||||
|
} else {
|
||||||
|
"false message"
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
let is_true = std::env::args().len() == 1;
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
Foo(1),
|
||||||
|
Foo(1)
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
Foo(0),
|
||||||
|
Foo(1)
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Foo(2),
|
||||||
|
Foo(2)
|
||||||
|
);
|
||||||
|
let bar = Foo(1);
|
||||||
|
assert_ne!(
|
||||||
|
bar,
|
||||||
|
Foo(3)
|
||||||
|
);
|
||||||
|
if is_true {
|
||||||
|
assert_ne!(
|
||||||
|
Foo(0),
|
||||||
|
Foo(4)
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert_eq!(
|
||||||
|
Foo(3),
|
||||||
|
Foo(3)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if is_true {
|
||||||
|
assert_ne!(
|
||||||
|
Foo(0),
|
||||||
|
Foo(4),
|
||||||
|
"with a message"
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
assert_eq!(
|
||||||
|
Foo(3),
|
||||||
|
Foo(3),
|
||||||
|
"with a message"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
assert_ne!(
|
||||||
|
if is_true {
|
||||||
|
Foo(0)
|
||||||
|
} else {
|
||||||
|
Foo(1)
|
||||||
|
},
|
||||||
|
Foo(5)
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
Foo(5),
|
||||||
|
if is_true {
|
||||||
|
Foo(0)
|
||||||
|
} else {
|
||||||
|
Foo(1)
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
if is_true {
|
||||||
|
assert_eq!(
|
||||||
|
Foo(3),
|
||||||
|
Foo(3)
|
||||||
|
);
|
||||||
|
Foo(0)
|
||||||
|
} else {
|
||||||
|
assert_ne!(
|
||||||
|
if is_true {
|
||||||
|
Foo(0)
|
||||||
|
} else {
|
||||||
|
Foo(1)
|
||||||
|
},
|
||||||
|
Foo(5)
|
||||||
|
);
|
||||||
|
Foo(1)
|
||||||
|
},
|
||||||
|
Foo(5),
|
||||||
|
"with a message"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Foo(1),
|
||||||
|
Foo(3),
|
||||||
|
"this assert should fail"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
Foo(3),
|
||||||
|
Foo(3),
|
||||||
|
"this assert should not be reached"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Debug for Foo {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
write!(f, "try and succeed")?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut DEBUG_LEVEL_ENABLED: bool = false;
|
||||||
|
|
||||||
|
macro_rules! debug {
|
||||||
|
($($arg:tt)+) => (
|
||||||
|
if unsafe { DEBUG_LEVEL_ENABLED } {
|
||||||
|
println!($($arg)+);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test1() {
|
||||||
|
debug!("debug is enabled");
|
||||||
|
debug!("debug is enabled");
|
||||||
|
let _ = 0;
|
||||||
|
debug!("debug is enabled");
|
||||||
|
unsafe {
|
||||||
|
DEBUG_LEVEL_ENABLED = true;
|
||||||
|
}
|
||||||
|
debug!("debug is enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
macro_rules! call_debug {
|
||||||
|
($($arg:tt)+) => (
|
||||||
|
fn call_print(s: &str) {
|
||||||
|
print!("{}", s);
|
||||||
|
}
|
||||||
|
|
||||||
|
call_print("called from call_debug: ");
|
||||||
|
debug!($($arg)+);
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test2() {
|
||||||
|
call_debug!("debug is enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test1();
|
||||||
|
test2();
|
||||||
|
test3();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user