]> git.lizzy.rs Git - rust.git/commitdiff
More improvements to macro coverage
authorRich Kadel <richkadel@google.com>
Mon, 26 Apr 2021 23:27:54 +0000 (16:27 -0700)
committerRich Kadel <richkadel@google.com>
Thu, 29 Apr 2021 03:27:27 +0000 (20:27 -0700)
compiler/rustc_mir/src/transform/coverage/spans.rs
compiler/rustc_mir/src/transform/coverage/tests.rs
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.inner_items.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.issue-84561.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_crate.txt
src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.uses_inline_crate.txt
src/test/run-make-fulldeps/coverage/issue-84561.rs

index ddbd1680430d29f40b4e44c05cc9bf3409a3d6b7..e1db5191c896da6f8172a3b37e3b54050306a2e5 100644 (file)
@@ -11,7 +11,7 @@
 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;
 
@@ -67,7 +67,7 @@ pub fn span(&self) -> &Span {
 #[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,
@@ -75,12 +75,9 @@ pub(super) struct CoverageSpan {
 
 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,
@@ -90,7 +87,7 @@ pub fn for_fn_sig(fn_sig_span: Span) -> Self {
     pub fn for_statement(
         statement: &Statement<'tcx>,
         span: Span,
-        is_macro_expansion: bool,
+        expn_span: Span,
         bcb: BasicCoverageBlock,
         bb: BasicBlock,
         stmt_index: usize,
@@ -105,7 +102,7 @@ pub fn for_statement(
 
         Self {
             span,
-            is_macro_expansion,
+            expn_span,
             bcb,
             coverage_statements: vec![CoverageStatement::Statement(bb, span, stmt_index)],
             is_closure,
@@ -114,13 +111,13 @@ pub fn for_statement(
 
     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,
@@ -176,6 +173,34 @@ pub fn format_coverage_statements(
             .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
@@ -219,6 +244,9 @@ pub struct CoverageSpans<'a, 'tcx> {
     /// 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
@@ -273,15 +301,13 @@ pub(super) fn generate_coverage_spans(
             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()
     }
@@ -335,10 +361,14 @@ fn mir_to_initial_sorted_coverage_spans(&self) -> Vec<CoverageSpan> {
     /// 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!(
@@ -347,7 +377,8 @@ fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
                     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
@@ -362,7 +393,7 @@ fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
             } 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
@@ -385,15 +416,16 @@ fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
                 }
             } 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
@@ -403,21 +435,60 @@ fn to_refined_spans(mut self) -> Vec<CoverageSpan> {
         // 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
@@ -440,22 +511,15 @@ fn bcb_to_initial_coverage_spans(
                     .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()
@@ -509,7 +573,7 @@ fn check_pending_dups(&mut self) {
                     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();
@@ -521,12 +585,13 @@ fn check_pending_dups(&mut self) {
     /// 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={:?}",
@@ -583,10 +648,10 @@ fn carve_out_span_for_closure(&mut self) {
                 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)
@@ -597,7 +662,7 @@ fn carve_out_span_for_closure(&mut self) {
             }
             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();
         }
@@ -688,7 +753,7 @@ fn cutoff_prev_at_overlapping_curr(&mut self) {
             } 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
@@ -704,7 +769,7 @@ fn span_bcb_is_dominated_by(&self, covspan: &CoverageSpan, dom_covspan: &Coverag
 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`.
@@ -749,7 +814,7 @@ pub(super) fn filtered_statement_span(
 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
@@ -789,14 +854,10 @@ pub(super) fn filtered_terminator_span(
     }
 }
 
+/// 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)
 }
index dbbd677fd638de39809947e3707350a6882e775a..9b84173c8a29307ba11ed5b15a007c11e8e22177 100644 (file)
@@ -683,12 +683,12 @@ fn test_make_bcb_counters() {
         let mut basic_coverage_blocks = graph::CoverageGraph::from_mir(&mir_body);
         let mut coverage_spans = Vec::new();
         for (bcb, data) in basic_coverage_blocks.iter_enumerated() {
-            if let Some((span, is_macro_expansion)) =
+            if let Some((span, expn_span)) =
                 spans::filtered_terminator_span(data.terminator(&mir_body), body_span)
             {
                 coverage_spans.push(spans::CoverageSpan::for_terminator(
                     span,
-                    is_macro_expansion,
+                    expn_span,
                     bcb,
                     data.last_bb(),
                 ));
index f5b5184044f65dccc327989669afe153a77edfb3..883254a09ba7d64edeac10c5c96dbe7a6abf453c 100644 (file)
@@ -1,9 +1,9 @@
     1|       |#![allow(unused_assignments, unused_variables, dead_code)]
     2|       |
     3|      1|fn main() {
-    4|       |    // 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
-    6|       |    // dependent conditions.
+    4|      1|    // Initialize test constants in a way that cannot be determined at compile time, to ensure
+    5|      1|    // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
+    6|      1|    // dependent conditions.
     7|      1|    let is_true = std::env::args().len() == 1;
     8|      1|
     9|      1|    let mut countdown = 0;
index 34d584f9eae669519d125b2ecff6b52f2aa42cd3..5d266b5db15f2f86c47432a8586f91a16dbe48af 100644 (file)
@@ -1,17 +1,17 @@
-    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|}
 
index f5beb9ef24a0e4b355772651115ce519a316b16f..c2d5143a61816f881992c53bf2c3fa2a412201dc 100644 (file)
@@ -3,9 +3,9 @@
     3|       |use std::fmt::Debug;
     4|       |
     5|      1|pub fn used_function() {
-    6|       |    // 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
-    8|       |    // dependent conditions.
+    6|      1|    // Initialize test constants in a way that cannot be determined at compile time, to ensure
+    7|      1|    // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
+    8|      1|    // dependent conditions.
     9|      1|    let is_true = std::env::args().len() == 1;
    10|      1|    let mut countdown = 0;
    11|      1|    if is_true {
index cc98956e3073af92156ac696e618b3697e84646c..dab31cbf4ac9eef4770d4c4a0b292c5da3f0aa25 100644 (file)
@@ -5,9 +5,9 @@
     5|       |use std::fmt::Debug;
     6|       |
     7|      1|pub fn used_function() {
-    8|       |    // 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
-   10|       |    // dependent conditions.
+    8|      1|    // Initialize test constants in a way that cannot be determined at compile time, to ensure
+    9|      1|    // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
+   10|      1|    // dependent conditions.
    11|      1|    let is_true = std::env::args().len() == 1;
    12|      1|    let mut countdown = 0;
    13|      1|    if is_true {
@@ -19,9 +19,9 @@
    18|       |
    19|       |#[inline(always)]
    20|      1|pub fn used_inline_function() {
-   21|       |    // 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
-   23|       |    // dependent conditions.
+   21|      1|    // Initialize test constants in a way that cannot be determined at compile time, to ensure
+   22|      1|    // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from
+   23|      1|    // dependent conditions.
    24|      1|    let is_true = std::env::args().len() == 1;
    25|      1|    let mut countdown = 0;
    26|      1|    if is_true {
index a5a0e1dc7581ddd449bcdf8c818c6a41b97923ac..5c8fd0b7caeaa9c4a2c19f9e2c36bcf818fe0b1b 100644 (file)
@@ -1,10 +1,10 @@
-// FIXME(#84561): function-like macros produce unintuitive coverage results.
-// This test demonstrates some of the problems.
+// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results.
 
-#[derive(Debug, PartialEq, Eq)]
+// expect-exit-status-101
+#[derive(PartialEq, Eq)]
 struct Foo(u32);
-
-fn main() {
+fn test2() {
+    let is_true = std::env::args().len() == 1;
     let bar = Foo(1);
     assert_eq!(bar, Foo(1));
     let baz = Foo(0);
@@ -16,14 +16,30 @@ fn main() {
     assert_eq!(Foo(1), Foo(1));
     assert_ne!(Foo(0), Foo(1));
     assert_eq!(Foo(2), Foo(2));
-    let bar = Foo(1);
-    assert_ne!(Foo(0), Foo(3));
+    let bar = Foo(0);
+    assert_ne!(bar, Foo(3));
     assert_ne!(Foo(0), Foo(4));
-    assert_eq!(Foo(3), Foo(3));
-    assert_ne!(Foo(0), Foo(5));
+    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!(
@@ -38,10 +54,9 @@ fn main() {
         Foo(2),
         Foo(2)
     );
-    let bar = Foo(1
-    );
+    let bar = Foo(1);
     assert_ne!(
-        Foo(0),
+        bar,
         Foo(3)
     );
     if is_true {
@@ -55,6 +70,19 @@ fn main() {
             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)
@@ -89,6 +117,50 @@ fn main() {
             );
             Foo(1)
         },
-        Foo(5)
+        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");
+}
+
+fn main() {
+    test1();
+    test2();
+}