use rustc_middle::ty::TyCtxt;
use rustc_span::source_map::original_sp;
-use rustc_span::{BytePos, ExpnKind, MacroKind, Span};
+use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol};
use std::cmp::Ordering;
#[derive(Debug, Clone)]
pub(super) struct CoverageSpan {
pub span: Span,
- pub is_macro_expansion: bool,
+ pub expn_span: Span,
pub bcb: BasicCoverageBlock,
pub coverage_statements: Vec<CoverageStatement>,
pub is_closure: bool,
impl CoverageSpan {
pub fn for_fn_sig(fn_sig_span: Span) -> Self {
- // Whether the function signature is from a macro or not, it should not be treated like
- // macro-expanded statements and terminators.
- let is_macro_expansion = false;
Self {
span: fn_sig_span,
- is_macro_expansion,
+ expn_span: fn_sig_span,
bcb: START_BCB,
coverage_statements: vec![],
is_closure: false,
pub fn for_statement(
statement: &Statement<'tcx>,
span: Span,
- is_macro_expansion: bool,
+ expn_span: Span,
bcb: BasicCoverageBlock,
bb: BasicBlock,
stmt_index: usize,
Self {
span,
- is_macro_expansion,
+ expn_span,
bcb,
coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)],
is_closure,
pub fn for_terminator(
span: Span,
- is_macro_expansion: bool,
+ expn_span: Span,
bcb: BasicCoverageBlock,
bb: BasicBlock,
) -> Self {
Self {
span,
- is_macro_expansion,
+ expn_span,
bcb,
coverage_statements: vec![CoverageStatement::Terminator(bb, span)],
is_closure: false,
.collect::<Vec<_>>()
.join("\n")
}
+
+ /// 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 current_macro(&self) -> Option<Symbol> {
+ if let ExpnKind::Macro(MacroKind::Bang, current_macro) =
+ self.expn_span.ctxt().outer_expn_data().kind
+ {
+ return Some(current_macro);
+ }
+ None
+ }
+
+ /// 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
/// Assigned from `curr_original_span` from the previous iteration.
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
/// 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
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)),
+ prev_expn_span: None,
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()
}
/// de-duplicated `CoverageSpan`s.
fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
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());
let prev = self.take_prev();
self.curr_mut().merge_from(prev);
+ self.check_invoked_macro_name_span();
// Note that curr.span may now differ from curr_original_span
} else if self.prev_ends_before_curr() {
debug!(
self.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 {
// drop any equal or overlapping span (`curr`) and keep `prev` to test again in the
// next iter
} 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).
- if self.prev().is_macro_expansion && self.curr().is_macro_expansion {
+ 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
}
} else {
self.cutoff_prev_at_overlapping_curr();
+ self.check_invoked_macro_name_span();
}
}
debug!(" AT END, adding last prev={:?}", self.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 {
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
// 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() {
+ let body_ends_with_closure = if let Some(last_covspan) = self.refined_spans.last() {
last_covspan.is_closure && last_covspan.span.hi() == self.body_span.hi()
} else {
false
};
if !body_ends_with_closure {
- refined_spans.push(prev);
+ self.push_refined_span(prev);
}
// Remove `CoverageSpan`s derived from closures, originally added to ensure the coverage
// regions for the current function leave room for the closure's own coverage regions
// (injected separately, from the closure's own MIR).
- refined_spans.retain(|covspan| !covspan.is_closure);
- refined_spans
+ self.refined_spans.retain(|covspan| !covspan.is_closure);
+ 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.to_string().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
.enumerate()
.filter_map(move |(index, statement)| {
filtered_statement_span(statement, self.body_span).map(
- |(span, is_macro_expansion)| {
+ |(span, expn_span)| {
CoverageSpan::for_statement(
- statement,
- span,
- is_macro_expansion,
- bcb,
- bb,
- index,
+ statement, span, expn_span, bcb, bb, index,
)
},
)
})
.chain(filtered_terminator_span(data.terminator(), self.body_span).map(
- |(span, is_macro_expansion)| {
- CoverageSpan::for_terminator(span, is_macro_expansion, bcb, bb)
- },
+ |(span, expn_span)| CoverageSpan::for_terminator(span, expn_span, bcb, bb),
))
})
.collect()
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);
+ self.push_refined_span(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.prev_expn_span = Some(curr.expn_span);
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) {
+ if self.some_prev.is_some() && self.prev_starts_after_next(&curr) {
debug!(
" prev.span starts after curr.span, so curr will be dropped (skipping past \
closure?); prev={:?}",
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.push_refined_span(dup);
}
}
- self.refined_spans.push(pre_closure);
+ self.push_refined_span(pre_closure);
}
if has_post_closure_span {
// Update prev.span to start after the closure (and discard curr)
}
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
+ self.push_refined_span(closure_covspan); // since self.prev() was already updated
} else {
pending_dups.clear();
}
} else {
debug!(" ... adding modified prev={:?}", self.prev());
let prev = self.take_prev();
- self.refined_spans.push(prev);
+ self.push_refined_span(prev);
}
} else {
// with `pending_dups`, `prev` cannot have any statements that don't overlap
pub(super) fn filtered_statement_span(
statement: &'a Statement<'tcx>,
body_span: Span,
-) -> Option<(Span, bool)> {
+) -> Option<(Span, Span)> {
match statement.kind {
// These statements have spans that are often outside the scope of the executed source code
// for their parent `BasicBlock`.
pub(super) fn filtered_terminator_span(
terminator: &'a Terminator<'tcx>,
body_span: Span,
-) -> Option<(Span, bool)> {
+) -> Option<(Span, Span)> {
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
}
}
+/// Returns the span within the function source body, and the given span, which will be different
+/// if the given span is an expansion (macro, syntactic sugar, etc.).
#[inline]
-fn function_source_span(span: Span, body_span: Span) -> (Span, bool) {
- let is_macro_expansion = span.ctxt() != body_span.ctxt()
- && if let ExpnKind::Macro(MacroKind::Bang, _) = span.ctxt().outer_expn_data().kind {
- true
- } else {
- false
- };
- let span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
- (if body_span.contains(span) { span } else { body_span }, is_macro_expansion)
+fn function_source_span(span: Span, body_span: Span) -> (Span, Span) {
+ let original_span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
+ (if body_span.contains(original_span) { original_span } else { body_span }, span)
}
- 1| |// FIXME(#84561): function-like macros produce unintuitive coverage results.
- 2| |// This test demonstrates some of the problems.
- 3| |
- 4| 18|#[derive(Debug, PartialEq, Eq)]
- ^5 ^0
+ 1| |// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results.
+ 2| |
+ 3| |// expect-exit-status-101
+ 4| 21|#[derive(PartialEq, Eq)]
+ ^0
------------------
| <issue_84561::Foo as core::cmp::PartialEq>::eq:
- | 4| 18|#[derive(Debug, PartialEq, Eq)]
+ | 4| 21|#[derive(PartialEq, Eq)]
------------------
| Unexecuted instantiation: <issue_84561::Foo as core::cmp::PartialEq>::ne
------------------
5| |struct Foo(u32);
- 6| |
- 7| 1|fn main() {
+ 6| 1|fn test2() {
+ 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);
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(1);
- 20| 1| assert_ne!(Foo(0), Foo(3));
+ 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));
- 23| 1| assert_ne!(Foo(0), Foo(5));
- 24| 1| println!("{:?}", bar);
- 25| 1| println!("{:?}", Foo(1));
- 26| 1|
- 27| 1| let is_true = std::env::args().len() == 1;
- 28| 1|
- 29| 1| assert_eq!(
- 30| 1| Foo(1),
- 31| 1| Foo(1)
- 32| 1| );
- 33| 1| assert_ne!(
- 34| 1| Foo(0),
- 35| 1| Foo(1)
- 36| 1| );
- 37| 1| assert_eq!(
- 38| 1| Foo(2),
- 39| 1| Foo(2)
- 40| 1| );
- 41| 1| let bar = Foo(1
- 42| 1| );
- 43| 1| assert_ne!(
- 44| 1| Foo(0),
- 45| 1| Foo(3)
- 46| 1| );
- 47| 1| if is_true {
- 48| 1| assert_ne!(
- 49| 1| Foo(0),
- 50| 1| Foo(4)
- 51| 1| );
- 52| | } else {
- 53| 0| assert_eq!(
- 54| 0| Foo(3),
- 55| 0| Foo(3)
- 56| 0| );
- 57| | }
- 58| | assert_ne!(
- 59| 1| if is_true {
- 60| 1| Foo(0)
- 61| | } else {
- 62| 0| Foo(1)
- 63| | },
- 64| | Foo(5)
- 65| | );
- 66| 1| assert_ne!(
- 67| 1| Foo(5),
- 68| 1| if is_true {
- 69| 1| Foo(0)
- 70| | } else {
- 71| 0| Foo(1)
- 72| | }
- 73| | );
- 74| | assert_ne!(
- 75| 1| if is_true {
- 76| 1| assert_eq!(
- 77| 1| Foo(3),
- 78| 1| Foo(3)
- 79| 1| );
- 80| 1| Foo(0)
- 81| | } else {
- 82| | assert_ne!(
- 83| 0| if is_true {
- 84| 0| Foo(0)
- 85| | } else {
- 86| 0| Foo(1)
- 87| | },
- 88| | Foo(5)
- 89| | );
+ 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|}
+ 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| 1|fn main() {
+ 164| 1| test1();
+ 165| 1| test2();
+ 166| 1|}