diff options
Diffstat (limited to 'tests/run-coverage')
92 files changed, 5172 insertions, 0 deletions
diff --git a/tests/run-coverage/abort.coverage b/tests/run-coverage/abort.coverage new file mode 100644 index 000000000..a71c58d61 --- /dev/null +++ b/tests/run-coverage/abort.coverage @@ -0,0 +1,69 @@ + 1| |#![feature(c_unwind)] + 2| |#![allow(unused_assignments)] + 3| | + 4| 12|extern "C" fn might_abort(should_abort: bool) { + 5| 12| if should_abort { + 6| 0| println!("aborting..."); + 7| 0| panic!("panics and aborts"); + 8| 12| } else { + 9| 12| println!("Don't Panic"); + 10| 12| } + 11| 12|} + 12| | + 13| 1|fn main() -> Result<(), u8> { + 14| 1| let mut countdown = 10; + 15| 11| while countdown > 0 { + 16| 10| if countdown < 5 { + 17| 4| might_abort(false); + 18| 6| } + 19| | // See discussion (below the `Notes` section) on coverage results for the closing brace. + 20| 10| if countdown < 5 { might_abort(false); } // Counts for different regions on one line. + ^4 ^6 + 21| | // For the following example, the closing brace is the last character on the line. + 22| | // This shows the character after the closing brace is highlighted, even if that next + 23| | // character is a newline. + 24| 10| if countdown < 5 { might_abort(false); } + ^4 ^6 + 25| 10| countdown -= 1; + 26| | } + 27| 1| Ok(()) + 28| 1|} + 29| | + 30| |// Notes: + 31| |// 1. Compare this program and its coverage results to those of the similar tests + 32| |// `panic_unwind.rs` and `try_error_result.rs`. + 33| |// 2. This test confirms the coverage generated when a program includes `UnwindAction::Terminate`. + 34| |// 3. The test does not invoke the abort. By executing to a successful completion, the coverage + 35| |// results show where the program did and did not execute. + 36| |// 4. If the program actually aborted, the coverage counters would not be saved (which "works as + 37| |// intended"). Coverage results would show no executed coverage regions. + 38| |// 6. If `should_abort` is `true` and the program aborts, the program exits with a `132` status + 39| |// (on Linux at least). + 40| | + 41| |/* + 42| | + 43| |Expect the following coverage results: + 44| | + 45| |```text + 46| | 16| 11| while countdown > 0 { + 47| | 17| 10| if countdown < 5 { + 48| | 18| 4| might_abort(false); + 49| | 19| 6| } + 50| |``` + 51| | + 52| |This is actually correct. + 53| | + 54| |The condition `countdown < 5` executed 10 times (10 loop iterations). + 55| | + 56| |It evaluated to `true` 4 times, and executed the `might_abort()` call. + 57| | + 58| |It skipped the body of the `might_abort()` call 6 times. If an `if` does not include an explicit + 59| |`else`, the coverage implementation injects a counter, at the character immediately after the `if`s + 60| |closing brace, to count the "implicit" `else`. This is the only way to capture the coverage of the + 61| |non-true condition. + 62| | + 63| |As another example of why this is important, say the condition was `countdown < 50`, which is always + 64| |`true`. In that case, we wouldn't have a test for what happens if `might_abort()` is not called. + 65| |The closing brace would have a count of `0`, highlighting the missed coverage. + 66| |*/ + diff --git a/tests/run-coverage/abort.rs b/tests/run-coverage/abort.rs new file mode 100644 index 000000000..98264bdc1 --- /dev/null +++ b/tests/run-coverage/abort.rs @@ -0,0 +1,66 @@ +#![feature(c_unwind)] +#![allow(unused_assignments)] + +extern "C" fn might_abort(should_abort: bool) { + if should_abort { + println!("aborting..."); + panic!("panics and aborts"); + } else { + println!("Don't Panic"); + } +} + +fn main() -> Result<(), u8> { + let mut countdown = 10; + while countdown > 0 { + if countdown < 5 { + might_abort(false); + } + // See discussion (below the `Notes` section) on coverage results for the closing brace. + if countdown < 5 { might_abort(false); } // Counts for different regions on one line. + // For the following example, the closing brace is the last character on the line. + // This shows the character after the closing brace is highlighted, even if that next + // character is a newline. + if countdown < 5 { might_abort(false); } + countdown -= 1; + } + Ok(()) +} + +// Notes: +// 1. Compare this program and its coverage results to those of the similar tests +// `panic_unwind.rs` and `try_error_result.rs`. +// 2. This test confirms the coverage generated when a program includes `UnwindAction::Terminate`. +// 3. The test does not invoke the abort. By executing to a successful completion, the coverage +// results show where the program did and did not execute. +// 4. If the program actually aborted, the coverage counters would not be saved (which "works as +// intended"). Coverage results would show no executed coverage regions. +// 6. If `should_abort` is `true` and the program aborts, the program exits with a `132` status +// (on Linux at least). + +/* + +Expect the following coverage results: + +```text + 16| 11| while countdown > 0 { + 17| 10| if countdown < 5 { + 18| 4| might_abort(false); + 19| 6| } +``` + +This is actually correct. + +The condition `countdown < 5` executed 10 times (10 loop iterations). + +It evaluated to `true` 4 times, and executed the `might_abort()` call. + +It skipped the body of the `might_abort()` call 6 times. If an `if` does not include an explicit +`else`, the coverage implementation injects a counter, at the character immediately after the `if`s +closing brace, to count the "implicit" `else`. This is the only way to capture the coverage of the +non-true condition. + +As another example of why this is important, say the condition was `countdown < 50`, which is always +`true`. In that case, we wouldn't have a test for what happens if `might_abort()` is not called. +The closing brace would have a count of `0`, highlighting the missed coverage. +*/ diff --git a/tests/run-coverage/assert.coverage b/tests/run-coverage/assert.coverage new file mode 100644 index 000000000..a7134a149 --- /dev/null +++ b/tests/run-coverage/assert.coverage @@ -0,0 +1,34 @@ + 1| |#![allow(unused_assignments)] + 2| |// failure-status: 101 + 3| | + 4| 4|fn might_fail_assert(one_plus_one: u32) { + 5| 4| println!("does 1 + 1 = {}?", one_plus_one); + 6| 4| assert_eq!(1 + 1, one_plus_one, "the argument was wrong"); + ^1 + 7| 3|} + 8| | + 9| 1|fn main() -> Result<(),u8> { + 10| 1| let mut countdown = 10; + 11| 11| while countdown > 0 { + 12| 11| if countdown == 1 { + 13| 1| might_fail_assert(3); + 14| 10| } else if countdown < 5 { + 15| 3| might_fail_assert(2); + 16| 6| } + 17| 10| countdown -= 1; + 18| | } + 19| 0| Ok(()) + 20| 0|} + 21| | + 22| |// Notes: + 23| |// 1. Compare this program and its coverage results to those of the very similar test + 24| |// `panic_unwind.rs`, and similar tests `abort.rs` and `try_error_result.rs`. + 25| |// 2. This test confirms the coverage generated when a program passes or fails an `assert!()` or + 26| |// related `assert_*!()` macro. + 27| |// 3. Notably, the `assert` macros *do not* generate `TerminatorKind::Assert`. The macros produce + 28| |// conditional expressions, `TerminatorKind::SwitchInt` branches, and a possible call to + 29| |// `begin_panic_fmt()` (that begins a panic unwind, if the assertion test fails). + 30| |// 4. `TerminatoKind::Assert` is, however, also present in the MIR generated for this test + 31| |// (and in many other coverage tests). The `Assert` terminator is typically generated by the + 32| |// Rust compiler to check for runtime failures, such as numeric overflows. + diff --git a/tests/run-coverage/assert.rs b/tests/run-coverage/assert.rs new file mode 100644 index 000000000..d32a37e07 --- /dev/null +++ b/tests/run-coverage/assert.rs @@ -0,0 +1,32 @@ +#![allow(unused_assignments)] +// failure-status: 101 + +fn might_fail_assert(one_plus_one: u32) { + println!("does 1 + 1 = {}?", one_plus_one); + assert_eq!(1 + 1, one_plus_one, "the argument was wrong"); +} + +fn main() -> Result<(),u8> { + let mut countdown = 10; + while countdown > 0 { + if countdown == 1 { + might_fail_assert(3); + } else if countdown < 5 { + might_fail_assert(2); + } + countdown -= 1; + } + Ok(()) +} + +// Notes: +// 1. Compare this program and its coverage results to those of the very similar test +// `panic_unwind.rs`, and similar tests `abort.rs` and `try_error_result.rs`. +// 2. This test confirms the coverage generated when a program passes or fails an `assert!()` or +// related `assert_*!()` macro. +// 3. Notably, the `assert` macros *do not* generate `TerminatorKind::Assert`. The macros produce +// conditional expressions, `TerminatorKind::SwitchInt` branches, and a possible call to +// `begin_panic_fmt()` (that begins a panic unwind, if the assertion test fails). +// 4. `TerminatoKind::Assert` is, however, also present in the MIR generated for this test +// (and in many other coverage tests). The `Assert` terminator is typically generated by the +// Rust compiler to check for runtime failures, such as numeric overflows. diff --git a/tests/run-coverage/async.coverage b/tests/run-coverage/async.coverage new file mode 100644 index 000000000..93c1535b0 --- /dev/null +++ b/tests/run-coverage/async.coverage @@ -0,0 +1,139 @@ + 1| |#![allow(unused_assignments, dead_code)] + 2| | + 3| |// compile-flags: --edition=2018 -C opt-level=1 + 4| | + 5| 1|async fn c(x: u8) -> u8 { + 6| 1| if x == 8 { + 7| 1| 1 + 8| | } else { + 9| 0| 0 + 10| | } + 11| 1|} + 12| | + 13| 0|async fn d() -> u8 { 1 } + 14| | + 15| 0|async fn e() -> u8 { 1 } // unused function; executor does not block on `g()` + 16| | + 17| 1|async fn f() -> u8 { 1 } + 18| | + 19| 0|async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()` + 20| | + 21| 1|pub async fn g(x: u8) { + 22| 0| match x { + 23| 0| y if e().await == y => (), + 24| 0| y if f().await == y => (), + 25| 0| _ => (), + 26| | } + 27| 0|} + 28| | + 29| 1|async fn h(x: usize) { // The function signature is counted when called, but the body is not + 30| 0| // executed (not awaited) so the open brace has a `0` count (at least when + 31| 0| // displayed with `llvm-cov show` in color-mode). + 32| 0| match x { + 33| 0| y if foo().await[y] => (), + 34| 0| _ => (), + 35| | } + 36| 0|} + 37| | + 38| 1|async fn i(x: u8) { // line coverage is 1, but there are 2 regions: + 39| 1| // (a) the function signature, counted when the function is called; and + 40| 1| // (b) the open brace for the function body, counted once when the body is + 41| 1| // executed asynchronously. + 42| 1| match x { + 43| 1| y if c(x).await == y + 1 => { d().await; } + ^0 ^0 ^0 ^0 + 44| 1| y if f().await == y + 1 => (), + ^0 ^0 ^0 + 45| 1| _ => (), + 46| | } + 47| 1|} + 48| | + 49| 1|fn j(x: u8) { + 50| 1| // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. + 51| 1| fn c(x: u8) -> u8 { + 52| 1| if x == 8 { + 53| 1| 1 // This line appears covered, but the 1-character expression span covering the `1` + ^0 + 54| 1| // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because + 55| 1| // `fn j()` executes the open brace for the function body, followed by the function's + 56| 1| // first executable statement, `match x`. Inner function declarations are not + 57| 1| // "visible" to the MIR for `j()`, so the code region counts all lines between the + 58| 1| // open brace and the first statement as executed, which is, in a sense, true. + 59| 1| // `llvm-cov show` overcomes this kind of situation by showing the actual counts + 60| 1| // of the enclosed coverages, (that is, the `1` expression was not executed, and + 61| 1| // accurately displays a `0`). + 62| 1| } else { + 63| 1| 0 + 64| 1| } + 65| 1| } + 66| 1| fn d() -> u8 { 1 } // inner function is defined in-line, but the function is not executed + ^0 + 67| 1| fn f() -> u8 { 1 } + 68| 1| match x { + 69| 1| y if c(x) == y + 1 => { d(); } + ^0 ^0 + 70| 1| y if f() == y + 1 => (), + ^0 ^0 + 71| 1| _ => (), + 72| | } + 73| 1|} + 74| | + 75| 0|fn k(x: u8) { // unused function + 76| 0| match x { + 77| 0| 1 => (), + 78| 0| 2 => (), + 79| 0| _ => (), + 80| | } + 81| 0|} + 82| | + 83| 1|fn l(x: u8) { + 84| 1| match x { + 85| 0| 1 => (), + 86| 0| 2 => (), + 87| 1| _ => (), + 88| | } + 89| 1|} + 90| | + 91| 1|async fn m(x: u8) -> u8 { x - 1 } + ^0 + 92| | + 93| 1|fn main() { + 94| 1| let _ = g(10); + 95| 1| let _ = h(9); + 96| 1| let mut future = Box::pin(i(8)); + 97| 1| j(7); + 98| 1| l(6); + 99| 1| let _ = m(5); + 100| 1| executor::block_on(future.as_mut()); + 101| 1|} + 102| | + 103| |mod executor { + 104| | use core::{ + 105| | future::Future, + 106| | pin::Pin, + 107| | task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + 108| | }; + 109| | + 110| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output { + 111| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) }; + 112| 1| use std::hint::unreachable_unchecked; + 113| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new( + 114| 1| |_| unsafe { unreachable_unchecked() }, // clone + ^0 + 115| 1| |_| unsafe { unreachable_unchecked() }, // wake + ^0 + 116| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref + ^0 + 117| 1| |_| (), + 118| 1| ); + 119| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + 120| 1| let mut context = Context::from_waker(&waker); + 121| | + 122| | loop { + 123| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + 124| 1| break val; + 125| 0| } + 126| | } + 127| 1| } + 128| |} + diff --git a/tests/run-coverage/async.rs b/tests/run-coverage/async.rs new file mode 100644 index 000000000..efd9e62d6 --- /dev/null +++ b/tests/run-coverage/async.rs @@ -0,0 +1,128 @@ +#![allow(unused_assignments, dead_code)] + +// compile-flags: --edition=2018 -C opt-level=1 + +async fn c(x: u8) -> u8 { + if x == 8 { + 1 + } else { + 0 + } +} + +async fn d() -> u8 { 1 } + +async fn e() -> u8 { 1 } // unused function; executor does not block on `g()` + +async fn f() -> u8 { 1 } + +async fn foo() -> [bool; 10] { [false; 10] } // unused function; executor does not block on `h()` + +pub async fn g(x: u8) { + match x { + y if e().await == y => (), + y if f().await == y => (), + _ => (), + } +} + +async fn h(x: usize) { // The function signature is counted when called, but the body is not + // executed (not awaited) so the open brace has a `0` count (at least when + // displayed with `llvm-cov show` in color-mode). + match x { + y if foo().await[y] => (), + _ => (), + } +} + +async fn i(x: u8) { // line coverage is 1, but there are 2 regions: + // (a) the function signature, counted when the function is called; and + // (b) the open brace for the function body, counted once when the body is + // executed asynchronously. + match x { + y if c(x).await == y + 1 => { d().await; } + y if f().await == y + 1 => (), + _ => (), + } +} + +fn j(x: u8) { + // non-async versions of `c()`, `d()`, and `f()` to make it similar to async `i()`. + fn c(x: u8) -> u8 { + if x == 8 { + 1 // This line appears covered, but the 1-character expression span covering the `1` + // is not executed. (`llvm-cov show` displays a `^0` below the `1` ). This is because + // `fn j()` executes the open brace for the function body, followed by the function's + // first executable statement, `match x`. Inner function declarations are not + // "visible" to the MIR for `j()`, so the code region counts all lines between the + // open brace and the first statement as executed, which is, in a sense, true. + // `llvm-cov show` overcomes this kind of situation by showing the actual counts + // of the enclosed coverages, (that is, the `1` expression was not executed, and + // accurately displays a `0`). + } else { + 0 + } + } + fn d() -> u8 { 1 } // inner function is defined in-line, but the function is not executed + fn f() -> u8 { 1 } + match x { + y if c(x) == y + 1 => { d(); } + y if f() == y + 1 => (), + _ => (), + } +} + +fn k(x: u8) { // unused function + match x { + 1 => (), + 2 => (), + _ => (), + } +} + +fn l(x: u8) { + match x { + 1 => (), + 2 => (), + _ => (), + } +} + +async fn m(x: u8) -> u8 { x - 1 } + +fn main() { + let _ = g(10); + let _ = h(9); + let mut future = Box::pin(i(8)); + j(7); + l(6); + let _ = m(5); + executor::block_on(future.as_mut()); +} + +mod executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + 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( + |_| unsafe { unreachable_unchecked() }, // clone + |_| unsafe { unreachable_unchecked() }, // wake + |_| unsafe { unreachable_unchecked() }, // wake_by_ref + |_| (), + ); + 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; + } + } + } +} diff --git a/tests/run-coverage/async2.coverage b/tests/run-coverage/async2.coverage new file mode 100644 index 000000000..500dde1f2 --- /dev/null +++ b/tests/run-coverage/async2.coverage @@ -0,0 +1,116 @@ + 1| |// compile-flags: --edition=2018 + 2| | + 3| |use core::{ + 4| | future::Future, + 5| | marker::Send, + 6| | pin::Pin, + 7| |}; + 8| | + 9| 1|fn non_async_func() { + 10| 1| println!("non_async_func was covered"); + 11| 1| let b = true; + 12| 1| if b { + 13| 1| println!("non_async_func println in block"); + 14| 1| } + ^0 + 15| 1|} + 16| | + 17| | + 18| | + 19| | + 20| 1|async fn async_func() { + 21| 1| println!("async_func was covered"); + 22| 1| let b = true; + 23| 1| if b { + 24| 1| println!("async_func println in block"); + 25| 1| } + ^0 + 26| 1|} + 27| | + 28| | + 29| | + 30| | + 31| 1|async fn async_func_just_println() { + 32| 1| println!("async_func_just_println was covered"); + 33| 1|} + 34| | + 35| 1|fn main() { + 36| 1| println!("codecovsample::main"); + 37| 1| + 38| 1| non_async_func(); + 39| 1| + 40| 1| executor::block_on(async_func()); + 41| 1| executor::block_on(async_func_just_println()); + 42| 1|} + 43| | + 44| |mod executor { + 45| | use core::{ + 46| | future::Future, + 47| | pin::Pin, + 48| | task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + 49| | }; + 50| | + 51| 2| pub fn block_on<F: Future>(mut future: F) -> F::Output { + 52| 2| let mut future = unsafe { Pin::new_unchecked(&mut future) }; + 53| 2| use std::hint::unreachable_unchecked; + 54| 2| static VTABLE: RawWakerVTable = RawWakerVTable::new( + 55| 2| |_| unsafe { unreachable_unchecked() }, // clone + ^0 + 56| 2| |_| unsafe { unreachable_unchecked() }, // wake + ^0 + 57| 2| |_| unsafe { unreachable_unchecked() }, // wake_by_ref + ^0 + 58| 2| |_| (), + 59| 2| ); + 60| 2| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + 61| 2| let mut context = Context::from_waker(&waker); + 62| | + 63| | loop { + 64| 2| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + 65| 2| break val; + 66| 0| } + 67| | } + 68| 2| } + ------------------ + | async2::executor::block_on::<async2::async_func::{closure#0}>: + | 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output { + | 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) }; + | 53| 1| use std::hint::unreachable_unchecked; + | 54| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new( + | 55| 1| |_| unsafe { unreachable_unchecked() }, // clone + | 56| 1| |_| unsafe { unreachable_unchecked() }, // wake + | 57| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref + | 58| 1| |_| (), + | 59| 1| ); + | 60| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + | 61| 1| let mut context = Context::from_waker(&waker); + | 62| | + | 63| | loop { + | 64| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + | 65| 1| break val; + | 66| 0| } + | 67| | } + | 68| 1| } + ------------------ + | async2::executor::block_on::<async2::async_func_just_println::{closure#0}>: + | 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output { + | 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) }; + | 53| 1| use std::hint::unreachable_unchecked; + | 54| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new( + | 55| 1| |_| unsafe { unreachable_unchecked() }, // clone + | 56| 1| |_| unsafe { unreachable_unchecked() }, // wake + | 57| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref + | 58| 1| |_| (), + | 59| 1| ); + | 60| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) }; + | 61| 1| let mut context = Context::from_waker(&waker); + | 62| | + | 63| | loop { + | 64| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) { + | 65| 1| break val; + | 66| 0| } + | 67| | } + | 68| 1| } + ------------------ + 69| |} + diff --git a/tests/run-coverage/async2.rs b/tests/run-coverage/async2.rs new file mode 100644 index 000000000..959d48ce9 --- /dev/null +++ b/tests/run-coverage/async2.rs @@ -0,0 +1,69 @@ +// compile-flags: --edition=2018 + +use core::{ + future::Future, + marker::Send, + pin::Pin, +}; + +fn non_async_func() { + println!("non_async_func was covered"); + let b = true; + if b { + println!("non_async_func println in block"); + } +} + + + + +async fn async_func() { + println!("async_func was covered"); + let b = true; + if b { + println!("async_func println in block"); + } +} + + + + +async fn async_func_just_println() { + println!("async_func_just_println was covered"); +} + +fn main() { + println!("codecovsample::main"); + + non_async_func(); + + executor::block_on(async_func()); + executor::block_on(async_func_just_println()); +} + +mod executor { + use core::{ + future::Future, + pin::Pin, + task::{Context, Poll, RawWaker, RawWakerVTable, Waker}, + }; + + 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( + |_| unsafe { unreachable_unchecked() }, // clone + |_| unsafe { unreachable_unchecked() }, // wake + |_| unsafe { unreachable_unchecked() }, // wake_by_ref + |_| (), + ); + 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; + } + } + } +} diff --git a/tests/run-coverage/auxiliary/inline_always_with_dead_code.rs b/tests/run-coverage/auxiliary/inline_always_with_dead_code.rs new file mode 100644 index 000000000..2b21dee6c --- /dev/null +++ b/tests/run-coverage/auxiliary/inline_always_with_dead_code.rs @@ -0,0 +1,22 @@ +// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 -Copt-level=0 + +#![allow(dead_code)] + +mod foo { + #[inline(always)] + pub fn called() { } + + fn uncalled() { } +} + +pub mod bar { + pub fn call_me() { + super::foo::called(); + } +} + +pub mod baz { + pub fn call_me() { + super::foo::called(); + } +} diff --git a/tests/run-coverage/auxiliary/unused_mod_helper.rs b/tests/run-coverage/auxiliary/unused_mod_helper.rs new file mode 100644 index 000000000..ae1cc1531 --- /dev/null +++ b/tests/run-coverage/auxiliary/unused_mod_helper.rs @@ -0,0 +1,3 @@ +pub fn never_called_function() { + println!("I am never called"); +} diff --git a/tests/run-coverage/auxiliary/used_crate.rs b/tests/run-coverage/auxiliary/used_crate.rs new file mode 100644 index 000000000..16592d48d --- /dev/null +++ b/tests/run-coverage/auxiliary/used_crate.rs @@ -0,0 +1,100 @@ +#![allow(unused_assignments, unused_variables)] +// compile-flags: -C opt-level=3 +use std::fmt::Debug; // ^^ validates coverage now works with optimizations + +pub fn used_function() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let mut countdown = 0; + if is_true { + countdown = 10; + } + use_this_lib_crate(); +} + +pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + println!("used_only_from_bin_crate_generic_function with {:?}", arg); +} +// Expect for above function: `Unexecuted instantiation` (see below) +pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); +} + +pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +pub fn unused_generic_function<T: Debug>(arg: T) { + println!("unused_generic_function with {:?}", arg); +} + +pub fn unused_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +fn unused_private_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +fn use_this_lib_crate() { + used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + "used from library used_crate.rs", + ); + let some_vec = vec![5, 6, 7, 8]; + used_only_from_this_lib_crate_generic_function(some_vec); + used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); +} + +// FIXME(#79651): "Unexecuted instantiation" errors appear in coverage results, +// for example: +// +// | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> +// +// These notices appear when `llvm-cov` shows instantiations. This may be a +// default option, but it can be suppressed with: +// +// ```shell +// $ `llvm-cov show --show-instantiations=0 ...` +// ``` +// +// The notice is triggered because the function is unused by the library itself, +// and when the library is compiled, a synthetic function is generated, so +// unused function coverage can be reported. Coverage can be skipped for unused +// generic functions with: +// +// ```shell +// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...` +// ``` +// +// Even though this function is used by `uses_crate.rs` (and +// counted), with substitutions for `T`, those instantiations are only generated +// when the generic function is actually used (from the binary, not from this +// library crate). So the test result shows coverage for all instantiated +// versions and their generic type substitutions, plus the `Unexecuted +// instantiation` message for the non-substituted version. This is valid, but +// unfortunately a little confusing. +// +// The library crate has its own coverage map, and the only way to show unused +// coverage of a generic function is to include the generic function in the +// coverage map, marked as an "unused function". If the library were used by +// another binary that never used this generic function, then it would be valid +// to show the unused generic, with unknown substitution (`_`). +// +// The alternative is to exclude all generics from being included in the "unused +// functions" list, which would then omit coverage results for +// `unused_generic_function<T>()`, below. diff --git a/tests/run-coverage/auxiliary/used_inline_crate.rs b/tests/run-coverage/auxiliary/used_inline_crate.rs new file mode 100644 index 000000000..8b8e9d548 --- /dev/null +++ b/tests/run-coverage/auxiliary/used_inline_crate.rs @@ -0,0 +1,90 @@ +#![allow(unused_assignments, unused_variables)] + +// compile-flags: -C opt-level=3 +// ^^ validates coverage now works with optimizations +use std::fmt::Debug; + +pub fn used_function() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let mut countdown = 0; + if is_true { + countdown = 10; + } + use_this_lib_crate(); +} + +#[inline(always)] +pub fn used_inline_function() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let mut countdown = 0; + if is_true { + countdown = 10; + } + use_this_lib_crate(); +} + + + + + + + +#[inline(always)] +pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + println!("used_only_from_bin_crate_generic_function with {:?}", arg); +} +// Expect for above function: `Unexecuted instantiation` (see notes in `used_crate.rs`) + +#[inline(always)] +pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn unused_generic_function<T: Debug>(arg: T) { + println!("unused_generic_function with {:?}", arg); +} + +#[inline(always)] +pub fn unused_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +#[inline(always)] +fn unused_private_function() { + let is_true = std::env::args().len() == 1; + let mut countdown = 2; + if !is_true { + countdown = 20; + } +} + +fn use_this_lib_crate() { + used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + "used from library used_crate.rs", + ); + let some_vec = vec![5, 6, 7, 8]; + used_only_from_this_lib_crate_generic_function(some_vec); + used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); +} diff --git a/tests/run-coverage/closure.coverage b/tests/run-coverage/closure.coverage new file mode 100644 index 000000000..45d36b72e --- /dev/null +++ b/tests/run-coverage/closure.coverage @@ -0,0 +1,222 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| |// compile-flags: -C opt-level=2 + 3| 1|fn main() { // ^^ fix described in rustc_middle/mir/mono.rs + 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| let is_false = ! is_true; + 9| 1| + 10| 1| let mut some_string = Some(String::from("the string content")); + 11| 1| println!( + 12| 1| "The string or alt: {}" + 13| 1| , + 14| 1| some_string + 15| 1| . + 16| 1| unwrap_or_else + 17| 1| ( + 18| 1| || + 19| 0| { + 20| 0| let mut countdown = 0; + 21| 0| if is_false { + 22| 0| countdown = 10; + 23| 0| } + 24| 0| "alt string 1".to_owned() + 25| 1| } + 26| 1| ) + 27| 1| ); + 28| 1| + 29| 1| some_string = Some(String::from("the string content")); + 30| 1| let + 31| 1| a + 32| | = + 33| | || + 34| 0| { + 35| 0| let mut countdown = 0; + 36| 0| if is_false { + 37| 0| countdown = 10; + 38| 0| } + 39| 0| "alt string 2".to_owned() + 40| 0| }; + 41| 1| println!( + 42| 1| "The string or alt: {}" + 43| 1| , + 44| 1| some_string + 45| 1| . + 46| 1| unwrap_or_else + 47| 1| ( + 48| 1| a + 49| 1| ) + 50| 1| ); + 51| 1| + 52| 1| some_string = None; + 53| 1| println!( + 54| 1| "The string or alt: {}" + 55| 1| , + 56| 1| some_string + 57| 1| . + 58| 1| unwrap_or_else + 59| 1| ( + 60| 1| || + 61| 1| { + 62| 1| let mut countdown = 0; + 63| 1| if is_false { + 64| 0| countdown = 10; + 65| 1| } + 66| 1| "alt string 3".to_owned() + 67| 1| } + 68| 1| ) + 69| 1| ); + 70| 1| + 71| 1| some_string = None; + 72| 1| let + 73| 1| a + 74| 1| = + 75| 1| || + 76| 1| { + 77| 1| let mut countdown = 0; + 78| 1| if is_false { + 79| 0| countdown = 10; + 80| 1| } + 81| 1| "alt string 4".to_owned() + 82| 1| }; + 83| 1| println!( + 84| 1| "The string or alt: {}" + 85| 1| , + 86| 1| some_string + 87| 1| . + 88| 1| unwrap_or_else + 89| 1| ( + 90| 1| a + 91| 1| ) + 92| 1| ); + 93| 1| + 94| 1| let + 95| 1| quote_closure + 96| 1| = + 97| 1| |val| + 98| 5| { + 99| 5| let mut countdown = 0; + 100| 5| if is_false { + 101| 0| countdown = 10; + 102| 5| } + 103| 5| format!("'{}'", val) + 104| 5| }; + 105| 1| println!( + 106| 1| "Repeated, quoted string: {:?}" + 107| 1| , + 108| 1| std::iter::repeat("repeat me") + 109| 1| .take(5) + 110| 1| .map + 111| 1| ( + 112| 1| quote_closure + 113| 1| ) + 114| 1| .collect::<Vec<_>>() + 115| 1| ); + 116| 1| + 117| 1| let + 118| 1| _unused_closure + 119| | = + 120| | | + 121| | mut countdown + 122| | | + 123| 0| { + 124| 0| if is_false { + 125| 0| countdown = 10; + 126| 0| } + 127| 0| "closure should be unused".to_owned() + 128| 0| }; + 129| | + 130| 1| let mut countdown = 10; + 131| 1| let _short_unused_closure = | _unused_arg: u8 | countdown += 1; + ^0 + 132| | + 133| | + 134| 1| let short_used_covered_closure_macro = | used_arg: u8 | println!("called"); + 135| 1| let short_used_not_covered_closure_macro = | used_arg: u8 | println!("not called"); + ^0 + 136| 1| let _short_unused_closure_macro = | _unused_arg: u8 | println!("not called"); + ^0 + 137| | + 138| | + 139| | + 140| | + 141| 1| let _short_unused_closure_block = | _unused_arg: u8 | { println!("not called") }; + ^0 + 142| | + 143| 1| let _shortish_unused_closure = | _unused_arg: u8 | { + 144| 0| println!("not called") + 145| 0| }; + 146| | + 147| 1| let _as_short_unused_closure = | + 148| | _unused_arg: u8 + 149| 0| | { println!("not called") }; + 150| | + 151| 1| let _almost_as_short_unused_closure = | + 152| | _unused_arg: u8 + 153| 0| | { println!("not called") } + 154| | ; + 155| | + 156| | + 157| | + 158| | + 159| | + 160| 1| let _short_unused_closure_line_break_no_block = | _unused_arg: u8 | + 161| 0|println!("not called") + 162| | ; + 163| | + 164| 1| let _short_unused_closure_line_break_no_block2 = + 165| | | _unused_arg: u8 | + 166| 0| println!( + 167| 0| "not called" + 168| 0| ) + 169| | ; + 170| | + 171| 1| let short_used_not_covered_closure_line_break_no_block_embedded_branch = + 172| | | _unused_arg: u8 | + 173| 0| println!( + 174| 0| "not called: {}", + 175| 0| if is_true { "check" } else { "me" } + 176| 0| ) + 177| | ; + 178| | + 179| 1| let short_used_not_covered_closure_line_break_block_embedded_branch = + 180| 1| | _unused_arg: u8 | + 181| 0| { + 182| 0| println!( + 183| 0| "not called: {}", + 184| 0| if is_true { "check" } else { "me" } + 185| | ) + 186| 0| } + 187| | ; + 188| | + 189| 1| let short_used_covered_closure_line_break_no_block_embedded_branch = + 190| 1| | _unused_arg: u8 | + 191| 1| println!( + 192| 1| "not called: {}", + 193| 1| if is_true { "check" } else { "me" } + ^0 + 194| 1| ) + 195| | ; + 196| | + 197| 1| let short_used_covered_closure_line_break_block_embedded_branch = + 198| 1| | _unused_arg: u8 | + 199| 1| { + 200| 1| println!( + 201| 1| "not called: {}", + 202| 1| if is_true { "check" } else { "me" } + ^0 + 203| | ) + 204| 1| } + 205| | ; + 206| | + 207| 1| if is_false { + 208| 0| short_used_not_covered_closure_macro(0); + 209| 0| short_used_not_covered_closure_line_break_no_block_embedded_branch(0); + 210| 0| short_used_not_covered_closure_line_break_block_embedded_branch(0); + 211| 1| } + 212| 1| short_used_covered_closure_macro(0); + 213| 1| short_used_covered_closure_line_break_no_block_embedded_branch(0); + 214| 1| short_used_covered_closure_line_break_block_embedded_branch(0); + 215| 1|} + diff --git a/tests/run-coverage/closure.rs b/tests/run-coverage/closure.rs new file mode 100644 index 000000000..eb3a1ebff --- /dev/null +++ b/tests/run-coverage/closure.rs @@ -0,0 +1,215 @@ +#![allow(unused_assignments, unused_variables)] +// compile-flags: -C opt-level=2 +fn main() { // ^^ fix described in rustc_middle/mir/mono.rs + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + let is_false = ! is_true; + + let mut some_string = Some(String::from("the string content")); + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 1".to_owned() + } + ) + ); + + some_string = Some(String::from("the string content")); + let + a + = + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 2".to_owned() + }; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); + + some_string = None; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 3".to_owned() + } + ) + ); + + some_string = None; + let + a + = + || + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + "alt string 4".to_owned() + }; + println!( + "The string or alt: {}" + , + some_string + . + unwrap_or_else + ( + a + ) + ); + + let + quote_closure + = + |val| + { + let mut countdown = 0; + if is_false { + countdown = 10; + } + format!("'{}'", val) + }; + println!( + "Repeated, quoted string: {:?}" + , + std::iter::repeat("repeat me") + .take(5) + .map + ( + quote_closure + ) + .collect::<Vec<_>>() + ); + + let + _unused_closure + = + | + mut countdown + | + { + if is_false { + countdown = 10; + } + "closure should be unused".to_owned() + }; + + let mut countdown = 10; + let _short_unused_closure = | _unused_arg: u8 | countdown += 1; + + + let short_used_covered_closure_macro = | used_arg: u8 | println!("called"); + let short_used_not_covered_closure_macro = | used_arg: u8 | println!("not called"); + let _short_unused_closure_macro = | _unused_arg: u8 | println!("not called"); + + + + + let _short_unused_closure_block = | _unused_arg: u8 | { println!("not called") }; + + let _shortish_unused_closure = | _unused_arg: u8 | { + println!("not called") + }; + + let _as_short_unused_closure = | + _unused_arg: u8 + | { println!("not called") }; + + let _almost_as_short_unused_closure = | + _unused_arg: u8 + | { println!("not called") } + ; + + + + + + let _short_unused_closure_line_break_no_block = | _unused_arg: u8 | +println!("not called") + ; + + let _short_unused_closure_line_break_no_block2 = + | _unused_arg: u8 | + println!( + "not called" + ) + ; + + let short_used_not_covered_closure_line_break_no_block_embedded_branch = + | _unused_arg: u8 | + println!( + "not called: {}", + if is_true { "check" } else { "me" } + ) + ; + + let short_used_not_covered_closure_line_break_block_embedded_branch = + | _unused_arg: u8 | + { + println!( + "not called: {}", + if is_true { "check" } else { "me" } + ) + } + ; + + let short_used_covered_closure_line_break_no_block_embedded_branch = + | _unused_arg: u8 | + println!( + "not called: {}", + if is_true { "check" } else { "me" } + ) + ; + + let short_used_covered_closure_line_break_block_embedded_branch = + | _unused_arg: u8 | + { + println!( + "not called: {}", + if is_true { "check" } else { "me" } + ) + } + ; + + if is_false { + short_used_not_covered_closure_macro(0); + short_used_not_covered_closure_line_break_no_block_embedded_branch(0); + short_used_not_covered_closure_line_break_block_embedded_branch(0); + } + short_used_covered_closure_macro(0); + short_used_covered_closure_line_break_no_block_embedded_branch(0); + short_used_covered_closure_line_break_block_embedded_branch(0); +} diff --git a/tests/run-coverage/closure_macro.coverage b/tests/run-coverage/closure_macro.coverage new file mode 100644 index 000000000..87f701476 --- /dev/null +++ b/tests/run-coverage/closure_macro.coverage @@ -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| | $value.or_else(|e| { // FIXME(85000): no coverage in closure macros + 18| | let message = format!($error_message, e); + 19| | if message.len() > 0 { + 20| | println!("{}", message); + 21| | Ok(String::from("ok")) + 22| | } else { + 23| | bail!("error"); + 24| | } + 25| | }) + 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|} + diff --git a/tests/run-coverage/closure_macro.rs b/tests/run-coverage/closure_macro.rs new file mode 100644 index 000000000..5e3b00d1e --- /dev/null +++ b/tests/run-coverage/closure_macro.rs @@ -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| { // FIXME(85000): no coverage in closure macros + 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(()) +} diff --git a/tests/run-coverage/closure_macro_async.coverage b/tests/run-coverage/closure_macro_async.coverage new file mode 100644 index 000000000..2b5418132 --- /dev/null +++ b/tests/run-coverage/closure_macro_async.coverage @@ -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| | $value.or_else(|e| { // FIXME(85000): no coverage in closure macros + 18| | let message = format!($error_message, e); + 19| | if message.len() > 0 { + 20| | println!("{}", message); + 21| | Ok(String::from("ok")) + 22| | } else { + 23| | bail!("error"); + 24| | } + 25| | }) + 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| |} + diff --git a/tests/run-coverage/closure_macro_async.rs b/tests/run-coverage/closure_macro_async.rs new file mode 100644 index 000000000..e3e89e9c8 --- /dev/null +++ b/tests/run-coverage/closure_macro_async.rs @@ -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| { // FIXME(85000): no coverage in closure macros + 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; + } + } + } +} diff --git a/tests/run-coverage/conditions.coverage b/tests/run-coverage/conditions.coverage new file mode 100644 index 000000000..2d8a98a5d --- /dev/null +++ b/tests/run-coverage/conditions.coverage @@ -0,0 +1,94 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| let mut countdown = 0; + 5| 1| if true { + 6| 1| countdown = 10; + 7| 1| } + ^0 + 8| | + 9| | const B: u32 = 100; + 10| 1| let x = if countdown > 7 { + 11| 1| countdown -= 4; + 12| 1| B + 13| 0| } else if countdown > 2 { + 14| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 15| 0| countdown = 0; + 16| 0| } + 17| 0| countdown -= 5; + 18| 0| countdown + 19| | } else { + 20| 0| return; + 21| | }; + 22| | + 23| 1| let mut countdown = 0; + 24| 1| if true { + 25| 1| countdown = 10; + 26| 1| } + ^0 + 27| | + 28| 1| if countdown > 7 { + 29| 1| countdown -= 4; + 30| 1| } else if countdown > 2 { + ^0 + 31| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 32| 0| countdown = 0; + 33| 0| } + 34| 0| countdown -= 5; + 35| | } else { + 36| 0| return; + 37| | } + 38| | + 39| 1| if true { + 40| 1| let mut countdown = 0; + 41| 1| if true { + 42| 1| countdown = 10; + 43| 1| } + ^0 + 44| | + 45| 1| if countdown > 7 { + 46| 1| countdown -= 4; + 47| 1| } + 48| 0| else if countdown > 2 { + 49| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 50| 0| countdown = 0; + 51| 0| } + 52| 0| countdown -= 5; + 53| | } else { + 54| 0| return; + 55| | } + 56| 0| } + 57| | + 58| | + 59| 1| let mut countdown = 0; + 60| 1| if true { + 61| 1| countdown = 1; + 62| 1| } + ^0 + 63| | + 64| 1| let z = if countdown > 7 { + ^0 + 65| 0| countdown -= 4; + 66| 1| } else if countdown > 2 { + 67| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 68| 0| countdown = 0; + 69| 0| } + 70| 0| countdown -= 5; + 71| | } else { + 72| 1| let should_be_reachable = countdown; + 73| 1| println!("reached"); + 74| 1| return; + 75| | }; + 76| | + 77| 0| let w = if countdown > 7 { + 78| 0| countdown -= 4; + 79| 0| } else if countdown > 2 { + 80| 0| if countdown < 1 || countdown > 5 || countdown != 9 { + 81| 0| countdown = 0; + 82| 0| } + 83| 0| countdown -= 5; + 84| | } else { + 85| 0| return; + 86| | }; + 87| 1|} + diff --git a/tests/run-coverage/conditions.rs b/tests/run-coverage/conditions.rs new file mode 100644 index 000000000..057599d1b --- /dev/null +++ b/tests/run-coverage/conditions.rs @@ -0,0 +1,87 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + let mut countdown = 0; + if true { + countdown = 10; + } + + const B: u32 = 100; + let x = if countdown > 7 { + countdown -= 4; + B + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + countdown + } else { + return; + }; + + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } + + if true { + let mut countdown = 0; + if true { + countdown = 10; + } + + if countdown > 7 { + countdown -= 4; + } + else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + } + } + + + let mut countdown = 0; + if true { + countdown = 1; + } + + let z = if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + let should_be_reachable = countdown; + println!("reached"); + return; + }; + + let w = if countdown > 7 { + countdown -= 4; + } else if countdown > 2 { + if countdown < 1 || countdown > 5 || countdown != 9 { + countdown = 0; + } + countdown -= 5; + } else { + return; + }; +} diff --git a/tests/run-coverage/continue.coverage b/tests/run-coverage/continue.coverage new file mode 100644 index 000000000..bf42924b1 --- /dev/null +++ b/tests/run-coverage/continue.coverage @@ -0,0 +1,70 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| let is_true = std::env::args().len() == 1; + 5| 1| + 6| 1| let mut x = 0; + 7| 11| for _ in 0..10 { + 8| 10| match is_true { + 9| | true => { + 10| 10| continue; + 11| | } + 12| 0| _ => { + 13| 0| x = 1; + 14| 0| } + 15| 0| } + 16| 0| x = 3; + 17| | } + 18| 11| for _ in 0..10 { + 19| 10| match is_true { + 20| 0| false => { + 21| 0| x = 1; + 22| 0| } + 23| | _ => { + 24| 10| continue; + 25| | } + 26| | } + 27| 0| x = 3; + 28| | } + 29| 11| for _ in 0..10 { + 30| 10| match is_true { + 31| 10| true => { + 32| 10| x = 1; + 33| 10| } + 34| | _ => { + 35| 0| continue; + 36| | } + 37| | } + 38| 10| x = 3; + 39| | } + 40| 11| for _ in 0..10 { + 41| 10| if is_true { + 42| 10| continue; + 43| 0| } + 44| 0| x = 3; + 45| | } + 46| 11| for _ in 0..10 { + 47| 10| match is_true { + 48| 0| false => { + 49| 0| x = 1; + 50| 0| } + 51| 10| _ => { + 52| 10| let _ = x; + 53| 10| } + 54| | } + 55| 10| x = 3; + 56| | } + 57| 1| for _ in 0..10 { + 58| 1| match is_true { + 59| 0| false => { + 60| 0| x = 1; + 61| 0| } + 62| | _ => { + 63| 1| break; + 64| | } + 65| | } + 66| 0| x = 3; + 67| | } + 68| 1| let _ = x; + 69| 1|} + diff --git a/tests/run-coverage/continue.rs b/tests/run-coverage/continue.rs new file mode 100644 index 000000000..624aa9834 --- /dev/null +++ b/tests/run-coverage/continue.rs @@ -0,0 +1,69 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + let is_true = std::env::args().len() == 1; + + let mut x = 0; + for _ in 0..10 { + match is_true { + true => { + continue; + } + _ => { + x = 1; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + continue; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + true => { + x = 1; + } + _ => { + continue; + } + } + x = 3; + } + for _ in 0..10 { + if is_true { + continue; + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + let _ = x; + } + } + x = 3; + } + for _ in 0..10 { + match is_true { + false => { + x = 1; + } + _ => { + break; + } + } + x = 3; + } + let _ = x; +} diff --git a/tests/run-coverage/dead_code.coverage b/tests/run-coverage/dead_code.coverage new file mode 100644 index 000000000..09ff14c6f --- /dev/null +++ b/tests/run-coverage/dead_code.coverage @@ -0,0 +1,39 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 0|pub fn unused_pub_fn_not_in_library() { + 4| 0| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 5| 0| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 6| 0| // dependent conditions. + 7| 0| let is_true = std::env::args().len() == 1; + 8| 0| + 9| 0| let mut countdown = 0; + 10| 0| if is_true { + 11| 0| countdown = 10; + 12| 0| } + 13| 0|} + 14| | + 15| 0|fn unused_fn() { + 16| 0| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 17| 0| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 18| 0| // dependent conditions. + 19| 0| let is_true = std::env::args().len() == 1; + 20| 0| + 21| 0| let mut countdown = 0; + 22| 0| if is_true { + 23| 0| countdown = 10; + 24| 0| } + 25| 0|} + 26| | + 27| 1|fn main() { + 28| 1| // Initialize test constants in a way that cannot be determined at compile time, to ensure + 29| 1| // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + 30| 1| // dependent conditions. + 31| 1| let is_true = std::env::args().len() == 1; + 32| 1| + 33| 1| let mut countdown = 0; + 34| 1| if is_true { + 35| 1| countdown = 10; + 36| 1| } + ^0 + 37| 1|} + diff --git a/tests/run-coverage/dead_code.rs b/tests/run-coverage/dead_code.rs new file mode 100644 index 000000000..a1285df0e --- /dev/null +++ b/tests/run-coverage/dead_code.rs @@ -0,0 +1,37 @@ +#![allow(unused_assignments, unused_variables)] + +pub fn unused_pub_fn_not_in_library() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } +} + +fn unused_fn() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } +} + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } +} diff --git a/tests/run-coverage/drop_trait.coverage b/tests/run-coverage/drop_trait.coverage new file mode 100644 index 000000000..293001e95 --- /dev/null +++ b/tests/run-coverage/drop_trait.coverage @@ -0,0 +1,34 @@ + 1| |#![allow(unused_assignments)] + 2| |// failure-status: 1 + 3| | + 4| |struct Firework { + 5| | strength: i32, + 6| |} + 7| | + 8| |impl Drop for Firework { + 9| 2| fn drop(&mut self) { + 10| 2| println!("BOOM times {}!!!", self.strength); + 11| 2| } + 12| |} + 13| | + 14| 1|fn main() -> Result<(),u8> { + 15| 1| let _firecracker = Firework { strength: 1 }; + 16| 1| + 17| 1| let _tnt = Firework { strength: 100 }; + 18| 1| + 19| 1| if true { + 20| 1| println!("Exiting with error..."); + 21| 1| return Err(1); + 22| 0| } + 23| 0| + 24| 0| let _ = Firework { strength: 1000 }; + 25| 0| + 26| 0| Ok(()) + 27| 1|} + 28| | + 29| |// Expected program output: + 30| |// Exiting with error... + 31| |// BOOM times 100!!! + 32| |// BOOM times 1!!! + 33| |// Error: 1 + diff --git a/tests/run-coverage/drop_trait.rs b/tests/run-coverage/drop_trait.rs new file mode 100644 index 000000000..a9b5d1d1e --- /dev/null +++ b/tests/run-coverage/drop_trait.rs @@ -0,0 +1,33 @@ +#![allow(unused_assignments)] +// failure-status: 1 + +struct Firework { + strength: i32, +} + +impl Drop for Firework { + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() -> Result<(),u8> { + let _firecracker = Firework { strength: 1 }; + + let _tnt = Firework { strength: 100 }; + + if true { + println!("Exiting with error..."); + return Err(1); + } + + let _ = Firework { strength: 1000 }; + + Ok(()) +} + +// Expected program output: +// Exiting with error... +// BOOM times 100!!! +// BOOM times 1!!! +// Error: 1 diff --git a/tests/run-coverage/generator.coverage b/tests/run-coverage/generator.coverage new file mode 100644 index 000000000..0fb3808ff --- /dev/null +++ b/tests/run-coverage/generator.coverage @@ -0,0 +1,32 @@ + 1| |#![feature(generators, generator_trait)] + 2| | + 3| |use std::ops::{Generator, GeneratorState}; + 4| |use std::pin::Pin; + 5| | + 6| |// The following implementation of a function called from a `yield` statement + 7| |// (apparently requiring the Result and the `String` type or constructor) + 8| |// creates conditions where the `generator::StateTransform` MIR transform will + 9| |// drop all `Counter` `Coverage` statements from a MIR. `simplify.rs` has logic + 10| |// to handle this condition, and still report dead block coverage. + 11| 1|fn get_u32(val: bool) -> Result<u32, String> { + 12| 1| if val { Ok(1) } else { Err(String::from("some error")) } + ^0 + 13| 1|} + 14| | + 15| 1|fn main() { + 16| 1| let is_true = std::env::args().len() == 1; + 17| 1| let mut generator = || { + 18| 1| yield get_u32(is_true); + 19| 1| return "foo"; + 20| 1| }; + 21| | + 22| 1| match Pin::new(&mut generator).resume(()) { + 23| 1| GeneratorState::Yielded(Ok(1)) => {} + 24| 0| _ => panic!("unexpected return from resume"), + 25| | } + 26| 1| match Pin::new(&mut generator).resume(()) { + 27| 1| GeneratorState::Complete("foo") => {} + 28| 0| _ => panic!("unexpected return from resume"), + 29| | } + 30| 1|} + diff --git a/tests/run-coverage/generator.rs b/tests/run-coverage/generator.rs new file mode 100644 index 000000000..431999102 --- /dev/null +++ b/tests/run-coverage/generator.rs @@ -0,0 +1,30 @@ +#![feature(generators, generator_trait)] + +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +// The following implementation of a function called from a `yield` statement +// (apparently requiring the Result and the `String` type or constructor) +// creates conditions where the `generator::StateTransform` MIR transform will +// drop all `Counter` `Coverage` statements from a MIR. `simplify.rs` has logic +// to handle this condition, and still report dead block coverage. +fn get_u32(val: bool) -> Result<u32, String> { + if val { Ok(1) } else { Err(String::from("some error")) } +} + +fn main() { + let is_true = std::env::args().len() == 1; + let mut generator = || { + yield get_u32(is_true); + return "foo"; + }; + + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(Ok(1)) => {} + _ => panic!("unexpected return from resume"), + } + match Pin::new(&mut generator).resume(()) { + GeneratorState::Complete("foo") => {} + _ => panic!("unexpected return from resume"), + } +} diff --git a/tests/run-coverage/generics.coverage b/tests/run-coverage/generics.coverage new file mode 100644 index 000000000..7a7649674 --- /dev/null +++ b/tests/run-coverage/generics.coverage @@ -0,0 +1,71 @@ + 1| |#![allow(unused_assignments)] + 2| |// failure-status: 1 + 3| | + 4| |struct Firework<T> where T: Copy + std::fmt::Display { + 5| | strength: T, + 6| |} + 7| | + 8| |impl<T> Firework<T> where T: Copy + std::fmt::Display { + 9| | #[inline(always)] + 10| 3| fn set_strength(&mut self, new_strength: T) { + 11| 3| self.strength = new_strength; + 12| 3| } + ------------------ + | <generics::Firework<f64>>::set_strength: + | 10| 2| fn set_strength(&mut self, new_strength: T) { + | 11| 2| self.strength = new_strength; + | 12| 2| } + ------------------ + | <generics::Firework<i32>>::set_strength: + | 10| 1| fn set_strength(&mut self, new_strength: T) { + | 11| 1| self.strength = new_strength; + | 12| 1| } + ------------------ + 13| |} + 14| | + 15| |impl<T> Drop for Firework<T> where T: Copy + std::fmt::Display { + 16| | #[inline(always)] + 17| 2| fn drop(&mut self) { + 18| 2| println!("BOOM times {}!!!", self.strength); + 19| 2| } + ------------------ + | <generics::Firework<f64> as core::ops::drop::Drop>::drop: + | 17| 1| fn drop(&mut self) { + | 18| 1| println!("BOOM times {}!!!", self.strength); + | 19| 1| } + ------------------ + | <generics::Firework<i32> as core::ops::drop::Drop>::drop: + | 17| 1| fn drop(&mut self) { + | 18| 1| println!("BOOM times {}!!!", self.strength); + | 19| 1| } + ------------------ + 20| |} + 21| | + 22| 1|fn main() -> Result<(),u8> { + 23| 1| let mut firecracker = Firework { strength: 1 }; + 24| 1| firecracker.set_strength(2); + 25| 1| + 26| 1| let mut tnt = Firework { strength: 100.1 }; + 27| 1| tnt.set_strength(200.1); + 28| 1| tnt.set_strength(300.3); + 29| 1| + 30| 1| if true { + 31| 1| println!("Exiting with error..."); + 32| 1| return Err(1); + 33| 0| } + 34| 0| + 35| 0| + 36| 0| + 37| 0| + 38| 0| + 39| 0| let _ = Firework { strength: 1000 }; + 40| 0| + 41| 0| Ok(()) + 42| 1|} + 43| | + 44| |// Expected program output: + 45| |// Exiting with error... + 46| |// BOOM times 100!!! + 47| |// BOOM times 1!!! + 48| |// Error: 1 + diff --git a/tests/run-coverage/generics.rs b/tests/run-coverage/generics.rs new file mode 100644 index 000000000..150ffb9db --- /dev/null +++ b/tests/run-coverage/generics.rs @@ -0,0 +1,48 @@ +#![allow(unused_assignments)] +// failure-status: 1 + +struct Firework<T> where T: Copy + std::fmt::Display { + strength: T, +} + +impl<T> Firework<T> where T: Copy + std::fmt::Display { + #[inline(always)] + fn set_strength(&mut self, new_strength: T) { + self.strength = new_strength; + } +} + +impl<T> Drop for Firework<T> where T: Copy + std::fmt::Display { + #[inline(always)] + fn drop(&mut self) { + println!("BOOM times {}!!!", self.strength); + } +} + +fn main() -> Result<(),u8> { + let mut firecracker = Firework { strength: 1 }; + firecracker.set_strength(2); + + let mut tnt = Firework { strength: 100.1 }; + tnt.set_strength(200.1); + tnt.set_strength(300.3); + + if true { + println!("Exiting with error..."); + return Err(1); + } + + + + + + let _ = Firework { strength: 1000 }; + + Ok(()) +} + +// Expected program output: +// Exiting with error... +// BOOM times 100!!! +// BOOM times 1!!! +// Error: 1 diff --git a/tests/run-coverage/if.coverage b/tests/run-coverage/if.coverage new file mode 100644 index 000000000..0c9eff227 --- /dev/null +++ b/tests/run-coverage/if.coverage @@ -0,0 +1,30 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 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 + 8| 1| is_true + 9| 1| = + 10| 1| std::env::args().len() + 11| 1| == + 12| 1| 1 + 13| 1| ; + 14| 1| let + 15| 1| mut + 16| 1| countdown + 17| 1| = + 18| 1| 0 + 19| 1| ; + 20| 1| if + 21| 1| is_true + 22| 1| { + 23| 1| countdown + 24| 1| = + 25| 1| 10 + 26| 1| ; + 27| 1| } + ^0 + 28| 1|} + diff --git a/tests/run-coverage/if.rs b/tests/run-coverage/if.rs new file mode 100644 index 000000000..8ad5042ff --- /dev/null +++ b/tests/run-coverage/if.rs @@ -0,0 +1,28 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let + is_true + = + std::env::args().len() + == + 1 + ; + let + mut + countdown + = + 0 + ; + if + is_true + { + countdown + = + 10 + ; + } +} diff --git a/tests/run-coverage/if_else.coverage b/tests/run-coverage/if_else.coverage new file mode 100644 index 000000000..4285d3186 --- /dev/null +++ b/tests/run-coverage/if_else.coverage @@ -0,0 +1,41 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 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; + 10| 1| if + 11| 1| is_true + 12| 1| { + 13| 1| countdown + 14| 1| = + 15| 1| 10 + 16| 1| ; + 17| 1| } + 18| | else // Note coverage region difference without semicolon + 19| | { + 20| 0| countdown + 21| 0| = + 22| 0| 100 + 23| | } + 24| | + 25| | if + 26| 1| is_true + 27| 1| { + 28| 1| countdown + 29| 1| = + 30| 1| 10 + 31| 1| ; + 32| 1| } + 33| | else + 34| 0| { + 35| 0| countdown + 36| 0| = + 37| 0| 100 + 38| 0| ; + 39| 0| } + 40| 1|} + diff --git a/tests/run-coverage/if_else.rs b/tests/run-coverage/if_else.rs new file mode 100644 index 000000000..3244e1e3a --- /dev/null +++ b/tests/run-coverage/if_else.rs @@ -0,0 +1,40 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if + is_true + { + countdown + = + 10 + ; + } + else // Note coverage region difference without semicolon + { + countdown + = + 100 + } + + if + is_true + { + countdown + = + 10 + ; + } + else + { + countdown + = + 100 + ; + } +} diff --git a/tests/run-coverage/inline-dead.coverage b/tests/run-coverage/inline-dead.coverage new file mode 100644 index 000000000..a59fe1146 --- /dev/null +++ b/tests/run-coverage/inline-dead.coverage @@ -0,0 +1,28 @@ + 1| |// Regression test for issue #98833. + 2| |// compile-flags: -Zinline-mir -Cdebug-assertions=off + 3| | + 4| 1|fn main() { + 5| 1| println!("{}", live::<false>()); + 6| 1| + 7| 1| let f = |x: bool| { + 8| | debug_assert!( + 9| 0| x + 10| | ); + 11| 1| }; + 12| 1| f(false); + 13| 1|} + 14| | + 15| |#[inline] + 16| 1|fn live<const B: bool>() -> u32 { + 17| 1| if B { + 18| 0| dead() + 19| | } else { + 20| 1| 0 + 21| | } + 22| 1|} + 23| | + 24| |#[inline] + 25| 0|fn dead() -> u32 { + 26| 0| 42 + 27| 0|} + diff --git a/tests/run-coverage/inline-dead.rs b/tests/run-coverage/inline-dead.rs new file mode 100644 index 000000000..854fa0629 --- /dev/null +++ b/tests/run-coverage/inline-dead.rs @@ -0,0 +1,27 @@ +// Regression test for issue #98833. +// compile-flags: -Zinline-mir -Cdebug-assertions=off + +fn main() { + println!("{}", live::<false>()); + + let f = |x: bool| { + debug_assert!( + x + ); + }; + f(false); +} + +#[inline] +fn live<const B: bool>() -> u32 { + if B { + dead() + } else { + 0 + } +} + +#[inline] +fn dead() -> u32 { + 42 +} diff --git a/tests/run-coverage/inline.coverage b/tests/run-coverage/inline.coverage new file mode 100644 index 000000000..6f5d1544f --- /dev/null +++ b/tests/run-coverage/inline.coverage @@ -0,0 +1,54 @@ + 1| |// compile-flags: -Zinline-mir + 2| | + 3| |use std::fmt::Display; + 4| | + 5| 1|fn main() { + 6| 1| permutations(&['a', 'b', 'c']); + 7| 1|} + 8| | + 9| |#[inline(always)] + 10| 1|fn permutations<T: Copy + Display>(xs: &[T]) { + 11| 1| let mut ys = xs.to_owned(); + 12| 1| permutate(&mut ys, 0); + 13| 1|} + 14| | + 15| 16|fn permutate<T: Copy + Display>(xs: &mut [T], k: usize) { + 16| 16| let n = length(xs); + 17| 16| if k == n { + 18| 6| display(xs); + 19| 10| } else if k < n { + 20| 15| for i in k..n { + ^10 + 21| 15| swap(xs, i, k); + 22| 15| permutate(xs, k + 1); + 23| 15| swap(xs, i, k); + 24| 15| } + 25| 0| } else { + 26| 0| error(); + 27| 0| } + 28| 16|} + 29| | + 30| 16|fn length<T>(xs: &[T]) -> usize { + 31| 16| xs.len() + 32| 16|} + 33| | + 34| |#[inline] + 35| 30|fn swap<T: Copy>(xs: &mut [T], i: usize, j: usize) { + 36| 30| let t = xs[i]; + 37| 30| xs[i] = xs[j]; + 38| 30| xs[j] = t; + 39| 30|} + 40| | + 41| 6|fn display<T: Display>(xs: &[T]) { + 42| 24| for x in xs { + ^18 + 43| 18| print!("{}", x); + 44| 18| } + 45| 6| println!(); + 46| 6|} + 47| | + 48| |#[inline(always)] + 49| 0|fn error() { + 50| 0| panic!("error"); + 51| 0|} + diff --git a/tests/run-coverage/inline.rs b/tests/run-coverage/inline.rs new file mode 100644 index 000000000..9cfab9ddb --- /dev/null +++ b/tests/run-coverage/inline.rs @@ -0,0 +1,51 @@ +// compile-flags: -Zinline-mir + +use std::fmt::Display; + +fn main() { + permutations(&['a', 'b', 'c']); +} + +#[inline(always)] +fn permutations<T: Copy + Display>(xs: &[T]) { + let mut ys = xs.to_owned(); + permutate(&mut ys, 0); +} + +fn permutate<T: Copy + Display>(xs: &mut [T], k: usize) { + let n = length(xs); + if k == n { + display(xs); + } else if k < n { + for i in k..n { + swap(xs, i, k); + permutate(xs, k + 1); + swap(xs, i, k); + } + } else { + error(); + } +} + +fn length<T>(xs: &[T]) -> usize { + xs.len() +} + +#[inline] +fn swap<T: Copy>(xs: &mut [T], i: usize, j: usize) { + let t = xs[i]; + xs[i] = xs[j]; + xs[j] = t; +} + +fn display<T: Display>(xs: &[T]) { + for x in xs { + print!("{}", x); + } + println!(); +} + +#[inline(always)] +fn error() { + panic!("error"); +} diff --git a/tests/run-coverage/inner_items.coverage b/tests/run-coverage/inner_items.coverage new file mode 100644 index 000000000..883254a09 --- /dev/null +++ b/tests/run-coverage/inner_items.coverage @@ -0,0 +1,60 @@ + 1| |#![allow(unused_assignments, unused_variables, dead_code)] + 2| | + 3| 1|fn main() { + 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; + 10| 1| if is_true { + 11| 1| countdown = 10; + 12| 1| } + ^0 + 13| | + 14| | mod in_mod { + 15| | const IN_MOD_CONST: u32 = 1000; + 16| | } + 17| | + 18| 3| fn in_func(a: u32) { + 19| 3| let b = 1; + 20| 3| let c = a + b; + 21| 3| println!("c = {}", c) + 22| 3| } + 23| | + 24| | struct InStruct { + 25| | in_struct_field: u32, + 26| | } + 27| | + 28| | const IN_CONST: u32 = 1234; + 29| | + 30| | trait InTrait { + 31| | fn trait_func(&mut self, incr: u32); + 32| | + 33| 1| fn default_trait_func(&mut self) { + 34| 1| in_func(IN_CONST); + 35| 1| self.trait_func(IN_CONST); + 36| 1| } + 37| | } + 38| | + 39| | impl InTrait for InStruct { + 40| 1| fn trait_func(&mut self, incr: u32) { + 41| 1| self.in_struct_field += incr; + 42| 1| in_func(self.in_struct_field); + 43| 1| } + 44| | } + 45| | + 46| | type InType = String; + 47| | + 48| 1| if is_true { + 49| 1| in_func(countdown); + 50| 1| } + ^0 + 51| | + 52| 1| let mut val = InStruct { + 53| 1| in_struct_field: 101, + 54| 1| }; + 55| 1| + 56| 1| val.default_trait_func(); + 57| 1|} + diff --git a/tests/run-coverage/inner_items.rs b/tests/run-coverage/inner_items.rs new file mode 100644 index 000000000..bcb62b303 --- /dev/null +++ b/tests/run-coverage/inner_items.rs @@ -0,0 +1,57 @@ +#![allow(unused_assignments, unused_variables, dead_code)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + if is_true { + countdown = 10; + } + + mod in_mod { + const IN_MOD_CONST: u32 = 1000; + } + + fn in_func(a: u32) { + let b = 1; + let c = a + b; + println!("c = {}", c) + } + + struct InStruct { + in_struct_field: u32, + } + + const IN_CONST: u32 = 1234; + + trait InTrait { + fn trait_func(&mut self, incr: u32); + + fn default_trait_func(&mut self) { + in_func(IN_CONST); + self.trait_func(IN_CONST); + } + } + + impl InTrait for InStruct { + fn trait_func(&mut self, incr: u32) { + self.in_struct_field += incr; + in_func(self.in_struct_field); + } + } + + type InType = String; + + if is_true { + in_func(countdown); + } + + let mut val = InStruct { + in_struct_field: 101, + }; + + val.default_trait_func(); +} diff --git a/tests/run-coverage/issue-83601.coverage b/tests/run-coverage/issue-83601.coverage new file mode 100644 index 000000000..25c74ab2e --- /dev/null +++ b/tests/run-coverage/issue-83601.coverage @@ -0,0 +1,16 @@ + 1| |// Shows that rust-lang/rust/83601 is resolved + 2| | + 3| 3|#[derive(Debug, PartialEq, Eq)] + ^2 + 4| |struct Foo(u32); + 5| | + 6| 1|fn main() { + 7| 1| let bar = Foo(1); + 8| 1| assert_eq!(bar, Foo(1)); + 9| 1| let baz = Foo(0); + 10| 1| assert_ne!(baz, Foo(1)); + 11| 1| println!("{:?}", Foo(1)); + 12| 1| println!("{:?}", bar); + 13| 1| println!("{:?}", baz); + 14| 1|} + diff --git a/tests/run-coverage/issue-83601.rs b/tests/run-coverage/issue-83601.rs new file mode 100644 index 000000000..0b72a8194 --- /dev/null +++ b/tests/run-coverage/issue-83601.rs @@ -0,0 +1,14 @@ +// Shows that rust-lang/rust/83601 is resolved + +#[derive(Debug, PartialEq, Eq)] +struct Foo(u32); + +fn main() { + 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); +} diff --git a/tests/run-coverage/issue-84561.coverage b/tests/run-coverage/issue-84561.coverage new file mode 100644 index 000000000..7a97e3532 --- /dev/null +++ b/tests/run-coverage/issue-84561.coverage @@ -0,0 +1,189 @@ + 1| |// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results. + 2| | + 3| |// failure-status: 101 + 4| 21|#[derive(PartialEq, Eq)] + 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|} + diff --git a/tests/run-coverage/issue-84561.rs b/tests/run-coverage/issue-84561.rs new file mode 100644 index 000000000..facf5b5b4 --- /dev/null +++ b/tests/run-coverage/issue-84561.rs @@ -0,0 +1,182 @@ +// This demonstrated Issue #84561: function-like macros produce unintuitive coverage results. + +// failure-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(); +} diff --git a/tests/run-coverage/issue-85461.coverage b/tests/run-coverage/issue-85461.coverage new file mode 100644 index 000000000..d78a4a112 --- /dev/null +++ b/tests/run-coverage/issue-85461.coverage @@ -0,0 +1,36 @@ +$DIR/auxiliary/inline_always_with_dead_code.rs: + 1| |// compile-flags: -Cinstrument-coverage -Ccodegen-units=4 -Copt-level=0 + 2| | + 3| |#![allow(dead_code)] + 4| | + 5| |mod foo { + 6| | #[inline(always)] + 7| 2| pub fn called() { } + 8| | + 9| 0| fn uncalled() { } + 10| |} + 11| | + 12| |pub mod bar { + 13| 1| pub fn call_me() { + 14| 1| super::foo::called(); + 15| 1| } + 16| |} + 17| | + 18| |pub mod baz { + 19| 1| pub fn call_me() { + 20| 1| super::foo::called(); + 21| 1| } + 22| |} + +$DIR/issue-85461.rs: + 1| |// Regression test for #85461: MSVC sometimes fail to link with dead code and #[inline(always)] + 2| |// aux-build:inline_always_with_dead_code.rs + 3| |extern crate inline_always_with_dead_code; + 4| | + 5| |use inline_always_with_dead_code::{bar, baz}; + 6| | + 7| 1|fn main() { + 8| 1| bar::call_me(); + 9| 1| baz::call_me(); + 10| 1|} + diff --git a/tests/run-coverage/issue-85461.rs b/tests/run-coverage/issue-85461.rs new file mode 100644 index 000000000..6f626b4a6 --- /dev/null +++ b/tests/run-coverage/issue-85461.rs @@ -0,0 +1,10 @@ +// Regression test for #85461: MSVC sometimes fail to link with dead code and #[inline(always)] +// aux-build:inline_always_with_dead_code.rs +extern crate inline_always_with_dead_code; + +use inline_always_with_dead_code::{bar, baz}; + +fn main() { + bar::call_me(); + baz::call_me(); +} diff --git a/tests/run-coverage/issue-93054.coverage b/tests/run-coverage/issue-93054.coverage new file mode 100644 index 000000000..a1655aded --- /dev/null +++ b/tests/run-coverage/issue-93054.coverage @@ -0,0 +1,29 @@ + 1| |// Regression test for #93054: Functions using uninhabited types often only have a single, + 2| |// unreachable basic block which doesn't get instrumented. This should not cause llvm-cov to fail. + 3| |// Since these kinds functions can't be invoked anyway, it's ok to not have coverage data for them. + 4| | + 5| |// compile-flags: --edition=2021 + 6| | + 7| |enum Never { } + 8| | + 9| |impl Never { + 10| | fn foo(self) { + 11| | match self { } + 12| | make().map(|never| match never { }); + 13| | } + 14| | + 15| | fn bar(&self) { + 16| | match *self { } + 17| | } + 18| |} + 19| | + 20| 0|async fn foo2(never: Never) { + 21| | match never { } + 22| |} + 23| | + 24| 0|fn make() -> Option<Never> { + 25| 0| None + 26| 0|} + 27| | + 28| 1|fn main() { } + diff --git a/tests/run-coverage/issue-93054.rs b/tests/run-coverage/issue-93054.rs new file mode 100644 index 000000000..c160b3db0 --- /dev/null +++ b/tests/run-coverage/issue-93054.rs @@ -0,0 +1,28 @@ +// Regression test for #93054: Functions using uninhabited types often only have a single, +// unreachable basic block which doesn't get instrumented. This should not cause llvm-cov to fail. +// Since these kinds functions can't be invoked anyway, it's ok to not have coverage data for them. + +// compile-flags: --edition=2021 + +enum Never { } + +impl Never { + fn foo(self) { + match self { } + make().map(|never| match never { }); + } + + fn bar(&self) { + match *self { } + } +} + +async fn foo2(never: Never) { + match never { } +} + +fn make() -> Option<Never> { + None +} + +fn main() { } diff --git a/tests/run-coverage/lazy_boolean.coverage b/tests/run-coverage/lazy_boolean.coverage new file mode 100644 index 000000000..bd349df2f --- /dev/null +++ b/tests/run-coverage/lazy_boolean.coverage @@ -0,0 +1,64 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 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 a, mut b, mut c) = (0, 0, 0); + 10| 1| if is_true { + 11| 1| a = 1; + 12| 1| b = 10; + 13| 1| c = 100; + 14| 1| } + ^0 + 15| | let + 16| 1| somebool + 17| | = + 18| 1| a < b + 19| | || + 20| 0| b < c + 21| | ; + 22| | let + 23| 1| somebool + 24| | = + 25| 1| b < a + 26| | || + 27| 1| b < c + 28| | ; + 29| 1| let somebool = a < b && b < c; + 30| 1| let somebool = b < a && b < c; + ^0 + 31| | + 32| | if + 33| 1| ! + 34| 1| is_true + 35| 0| { + 36| 0| a = 2 + 37| 0| ; + 38| 1| } + 39| | + 40| | if + 41| 1| is_true + 42| 1| { + 43| 1| b = 30 + 44| 1| ; + 45| 1| } + 46| | else + 47| 0| { + 48| 0| c = 400 + 49| 0| ; + 50| 0| } + 51| | + 52| 1| if !is_true { + 53| 0| a = 2; + 54| 1| } + 55| | + 56| 1| if is_true { + 57| 1| b = 30; + 58| 1| } else { + 59| 0| c = 400; + 60| 0| } + 61| 1|} + diff --git a/tests/run-coverage/lazy_boolean.rs b/tests/run-coverage/lazy_boolean.rs new file mode 100644 index 000000000..bb6219e85 --- /dev/null +++ b/tests/run-coverage/lazy_boolean.rs @@ -0,0 +1,61 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let (mut a, mut b, mut c) = (0, 0, 0); + if is_true { + a = 1; + b = 10; + c = 100; + } + let + somebool + = + a < b + || + b < c + ; + let + somebool + = + b < a + || + b < c + ; + let somebool = a < b && b < c; + let somebool = b < a && b < c; + + if + ! + is_true + { + a = 2 + ; + } + + if + is_true + { + b = 30 + ; + } + else + { + c = 400 + ; + } + + if !is_true { + a = 2; + } + + if is_true { + b = 30; + } else { + c = 400; + } +} diff --git a/tests/run-coverage/loop_break_value.coverage b/tests/run-coverage/loop_break_value.coverage new file mode 100644 index 000000000..022fe4c59 --- /dev/null +++ b/tests/run-coverage/loop_break_value.coverage @@ -0,0 +1,14 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 4| 1| let result + 5| 1| = + 6| 1| loop + 7| 1| { + 8| 1| break + 9| 1| 10 + 10| 1| ; + 11| 1| } + 12| 1| ; + 13| 1|} + diff --git a/tests/run-coverage/loop_break_value.rs b/tests/run-coverage/loop_break_value.rs new file mode 100644 index 000000000..dbc4fad7a --- /dev/null +++ b/tests/run-coverage/loop_break_value.rs @@ -0,0 +1,13 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + let result + = + loop + { + break + 10 + ; + } + ; +} diff --git a/tests/run-coverage/loops_branches.coverage b/tests/run-coverage/loops_branches.coverage new file mode 100644 index 000000000..b7ad79a24 --- /dev/null +++ b/tests/run-coverage/loops_branches.coverage @@ -0,0 +1,68 @@ + 1| |#![allow(unused_assignments, unused_variables, while_true)] + 2| | + 3| |// This test confirms that (1) unexecuted infinite loops are handled correctly by the + 4| |// InstrumentCoverage MIR pass; and (2) Counter Expressions that subtract from zero can be dropped. + 5| | + 6| |struct DebugTest; + 7| | + 8| |impl std::fmt::Debug for DebugTest { + 9| 1| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 10| 1| if true { + 11| 1| if false { + 12| 0| while true { + 13| 0| } + 14| 1| } + 15| 1| write!(f, "cool")?; + ^0 + 16| 0| } else { + 17| 0| } + 18| | + 19| 11| for i in 0..10 { + ^10 + 20| 10| if true { + 21| 10| if false { + 22| 0| while true {} + 23| 10| } + 24| 10| write!(f, "cool")?; + ^0 + 25| 0| } else { + 26| 0| } + 27| | } + 28| 1| Ok(()) + 29| 1| } + 30| |} + 31| | + 32| |struct DisplayTest; + 33| | + 34| |impl std::fmt::Display for DisplayTest { + 35| 1| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + 36| 1| if false { + 37| 0| } else { + 38| 1| if false { + 39| 0| while true {} + 40| 1| } + 41| 1| write!(f, "cool")?; + ^0 + 42| | } + 43| 11| for i in 0..10 { + ^10 + 44| 10| if false { + 45| 0| } else { + 46| 10| if false { + 47| 0| while true {} + 48| 10| } + 49| 10| write!(f, "cool")?; + ^0 + 50| | } + 51| | } + 52| 1| Ok(()) + 53| 1| } + 54| |} + 55| | + 56| 1|fn main() { + 57| 1| let debug_test = DebugTest; + 58| 1| println!("{:?}", debug_test); + 59| 1| let display_test = DisplayTest; + 60| 1| println!("{}", display_test); + 61| 1|} + diff --git a/tests/run-coverage/loops_branches.rs b/tests/run-coverage/loops_branches.rs new file mode 100644 index 000000000..7116ce47f --- /dev/null +++ b/tests/run-coverage/loops_branches.rs @@ -0,0 +1,61 @@ +#![allow(unused_assignments, unused_variables, while_true)] + +// This test confirms that (1) unexecuted infinite loops are handled correctly by the +// InstrumentCoverage MIR pass; and (2) Counter Expressions that subtract from zero can be dropped. + +struct DebugTest; + +impl std::fmt::Debug for DebugTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if true { + if false { + while true { + } + } + write!(f, "cool")?; + } else { + } + + for i in 0..10 { + if true { + if false { + while true {} + } + write!(f, "cool")?; + } else { + } + } + Ok(()) + } +} + +struct DisplayTest; + +impl std::fmt::Display for DisplayTest { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + if false { + } else { + if false { + while true {} + } + write!(f, "cool")?; + } + for i in 0..10 { + if false { + } else { + if false { + while true {} + } + write!(f, "cool")?; + } + } + Ok(()) + } +} + +fn main() { + let debug_test = DebugTest; + println!("{:?}", debug_test); + let display_test = DisplayTest; + println!("{}", display_test); +} diff --git a/tests/run-coverage/match_or_pattern.coverage b/tests/run-coverage/match_or_pattern.coverage new file mode 100644 index 000000000..a0fccb24f --- /dev/null +++ b/tests/run-coverage/match_or_pattern.coverage @@ -0,0 +1,50 @@ + 1| |#![feature(or_patterns)] + 2| | + 3| 1|fn main() { + 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 a: u8 = 0; + 10| 1| let mut b: u8 = 0; + 11| 1| if is_true { + 12| 1| a = 2; + 13| 1| b = 0; + 14| 1| } + ^0 + 15| 1| match (a, b) { + 16| | // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`. + 17| | // This test confirms a fix for Issue #79569. + 18| 0| (0 | 1, 2 | 3) => {} + 19| 1| _ => {} + 20| | } + 21| 1| if is_true { + 22| 1| a = 0; + 23| 1| b = 0; + 24| 1| } + ^0 + 25| 1| match (a, b) { + 26| 0| (0 | 1, 2 | 3) => {} + 27| 1| _ => {} + 28| | } + 29| 1| if is_true { + 30| 1| a = 2; + 31| 1| b = 2; + 32| 1| } + ^0 + 33| 1| match (a, b) { + 34| 0| (0 | 1, 2 | 3) => {} + 35| 1| _ => {} + 36| | } + 37| 1| if is_true { + 38| 1| a = 0; + 39| 1| b = 2; + 40| 1| } + ^0 + 41| 1| match (a, b) { + 42| 1| (0 | 1, 2 | 3) => {} + 43| 0| _ => {} + 44| | } + 45| 1|} + diff --git a/tests/run-coverage/match_or_pattern.rs b/tests/run-coverage/match_or_pattern.rs new file mode 100644 index 000000000..4c6a8a9b7 --- /dev/null +++ b/tests/run-coverage/match_or_pattern.rs @@ -0,0 +1,45 @@ +#![feature(or_patterns)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut a: u8 = 0; + let mut b: u8 = 0; + if is_true { + a = 2; + b = 0; + } + match (a, b) { + // Or patterns generate MIR `SwitchInt` with multiple targets to the same `BasicBlock`. + // This test confirms a fix for Issue #79569. + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 0; + b = 0; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 2; + b = 2; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } + if is_true { + a = 0; + b = 2; + } + match (a, b) { + (0 | 1, 2 | 3) => {} + _ => {} + } +} diff --git a/tests/run-coverage/nested_loops.coverage b/tests/run-coverage/nested_loops.coverage new file mode 100644 index 000000000..0dbd6bcf3 --- /dev/null +++ b/tests/run-coverage/nested_loops.coverage @@ -0,0 +1,26 @@ + 1| 1|fn main() { + 2| 1| let is_true = std::env::args().len() == 1; + 3| 1| let mut countdown = 10; + 4| | + 5| 1| 'outer: while countdown > 0 { + 6| 1| let mut a = 100; + 7| 1| let mut b = 100; + 8| 3| for _ in 0..50 { + 9| 3| if a < 30 { + 10| 0| break; + 11| 3| } + 12| 3| a -= 5; + 13| 3| b -= 5; + 14| 3| if b < 90 { + 15| 1| a -= 10; + 16| 1| if is_true { + 17| 1| break 'outer; + 18| 0| } else { + 19| 0| a -= 2; + 20| 0| } + 21| 2| } + 22| | } + 23| 0| countdown -= 1; + 24| | } + 25| 1|} + diff --git a/tests/run-coverage/nested_loops.rs b/tests/run-coverage/nested_loops.rs new file mode 100644 index 000000000..4c7c78427 --- /dev/null +++ b/tests/run-coverage/nested_loops.rs @@ -0,0 +1,25 @@ +fn main() { + let is_true = std::env::args().len() == 1; + let mut countdown = 10; + + 'outer: while countdown > 0 { + let mut a = 100; + let mut b = 100; + for _ in 0..50 { + if a < 30 { + break; + } + a -= 5; + b -= 5; + if b < 90 { + a -= 10; + if is_true { + break 'outer; + } else { + a -= 2; + } + } + } + countdown -= 1; + } +} diff --git a/tests/run-coverage/no_cov_crate.coverage b/tests/run-coverage/no_cov_crate.coverage new file mode 100644 index 000000000..83a920413 --- /dev/null +++ b/tests/run-coverage/no_cov_crate.coverage @@ -0,0 +1,87 @@ + 1| |// Enables `no_coverage` on the entire crate + 2| |#![feature(no_coverage)] + 3| | + 4| |#[no_coverage] + 5| |fn do_not_add_coverage_1() { + 6| | println!("called but not covered"); + 7| |} + 8| | + 9| |fn do_not_add_coverage_2() { + 10| | #![no_coverage] + 11| | println!("called but not covered"); + 12| |} + 13| | + 14| |#[no_coverage] + 15| |fn do_not_add_coverage_not_called() { + 16| | println!("not called and not covered"); + 17| |} + 18| | + 19| 1|fn add_coverage_1() { + 20| 1| println!("called and covered"); + 21| 1|} + 22| | + 23| 1|fn add_coverage_2() { + 24| 1| println!("called and covered"); + 25| 1|} + 26| | + 27| 0|fn add_coverage_not_called() { + 28| 0| println!("not called but covered"); + 29| 0|} + 30| | + 31| |// FIXME: These test-cases illustrate confusing results of nested functions. + 32| |// See https://github.com/rust-lang/rust/issues/93319 + 33| |mod nested_fns { + 34| | #[no_coverage] + 35| | pub fn outer_not_covered(is_true: bool) { + 36| 1| fn inner(is_true: bool) { + 37| 1| if is_true { + 38| 1| println!("called and covered"); + 39| 1| } else { + 40| 0| println!("absolutely not covered"); + 41| 0| } + 42| 1| } + 43| | println!("called but not covered"); + 44| | inner(is_true); + 45| | } + 46| | + 47| 1| pub fn outer(is_true: bool) { + 48| 1| println!("called and covered"); + 49| 1| inner_not_covered(is_true); + 50| 1| + 51| 1| #[no_coverage] + 52| 1| fn inner_not_covered(is_true: bool) { + 53| 1| if is_true { + 54| 1| println!("called but not covered"); + 55| 1| } else { + 56| 1| println!("absolutely not covered"); + 57| 1| } + 58| 1| } + 59| 1| } + 60| | + 61| 1| pub fn outer_both_covered(is_true: bool) { + 62| 1| println!("called and covered"); + 63| 1| inner(is_true); + 64| 1| + 65| 1| fn inner(is_true: bool) { + 66| 1| if is_true { + 67| 1| println!("called and covered"); + 68| 1| } else { + 69| 0| println!("absolutely not covered"); + 70| 0| } + 71| 1| } + 72| 1| } + 73| |} + 74| | + 75| 1|fn main() { + 76| 1| let is_true = std::env::args().len() == 1; + 77| 1| + 78| 1| do_not_add_coverage_1(); + 79| 1| do_not_add_coverage_2(); + 80| 1| add_coverage_1(); + 81| 1| add_coverage_2(); + 82| 1| + 83| 1| nested_fns::outer_not_covered(is_true); + 84| 1| nested_fns::outer(is_true); + 85| 1| nested_fns::outer_both_covered(is_true); + 86| 1|} + diff --git a/tests/run-coverage/no_cov_crate.rs b/tests/run-coverage/no_cov_crate.rs new file mode 100644 index 000000000..0bfbdda2c --- /dev/null +++ b/tests/run-coverage/no_cov_crate.rs @@ -0,0 +1,86 @@ +// Enables `no_coverage` on the entire crate +#![feature(no_coverage)] + +#[no_coverage] +fn do_not_add_coverage_1() { + println!("called but not covered"); +} + +fn do_not_add_coverage_2() { + #![no_coverage] + println!("called but not covered"); +} + +#[no_coverage] +fn do_not_add_coverage_not_called() { + println!("not called and not covered"); +} + +fn add_coverage_1() { + println!("called and covered"); +} + +fn add_coverage_2() { + println!("called and covered"); +} + +fn add_coverage_not_called() { + println!("not called but covered"); +} + +// FIXME: These test-cases illustrate confusing results of nested functions. +// See https://github.com/rust-lang/rust/issues/93319 +mod nested_fns { + #[no_coverage] + pub fn outer_not_covered(is_true: bool) { + fn inner(is_true: bool) { + if is_true { + println!("called and covered"); + } else { + println!("absolutely not covered"); + } + } + println!("called but not covered"); + inner(is_true); + } + + pub fn outer(is_true: bool) { + println!("called and covered"); + inner_not_covered(is_true); + + #[no_coverage] + fn inner_not_covered(is_true: bool) { + if is_true { + println!("called but not covered"); + } else { + println!("absolutely not covered"); + } + } + } + + pub fn outer_both_covered(is_true: bool) { + println!("called and covered"); + inner(is_true); + + fn inner(is_true: bool) { + if is_true { + println!("called and covered"); + } else { + println!("absolutely not covered"); + } + } + } +} + +fn main() { + let is_true = std::env::args().len() == 1; + + do_not_add_coverage_1(); + do_not_add_coverage_2(); + add_coverage_1(); + add_coverage_2(); + + nested_fns::outer_not_covered(is_true); + nested_fns::outer(is_true); + nested_fns::outer_both_covered(is_true); +} diff --git a/tests/run-coverage/overflow.coverage b/tests/run-coverage/overflow.coverage new file mode 100644 index 000000000..950437591 --- /dev/null +++ b/tests/run-coverage/overflow.coverage @@ -0,0 +1,64 @@ + 1| |#![allow(unused_assignments)] + 2| |// failure-status: 101 + 3| | + 4| 4|fn might_overflow(to_add: u32) -> u32 { + 5| 4| if to_add > 5 { + 6| 1| println!("this will probably overflow"); + 7| 3| } + 8| 4| let add_to = u32::MAX - 5; + 9| 4| println!("does {} + {} overflow?", add_to, to_add); + 10| 4| let result = to_add + add_to; + 11| 4| println!("continuing after overflow check"); + 12| 4| result + 13| 4|} + 14| | + 15| 1|fn main() -> Result<(),u8> { + 16| 1| let mut countdown = 10; + 17| 11| while countdown > 0 { + 18| 11| if countdown == 1 { + 19| 1| let result = might_overflow(10); + 20| 1| println!("Result: {}", result); + 21| 10| } else if countdown < 5 { + 22| 3| let result = might_overflow(1); + 23| 3| println!("Result: {}", result); + 24| 6| } + 25| 10| countdown -= 1; + 26| | } + 27| 0| Ok(()) + 28| 0|} + 29| | + 30| |// Notes: + 31| |// 1. Compare this program and its coverage results to those of the very similar test `assert.rs`, + 32| |// and similar tests `panic_unwind.rs`, abort.rs` and `try_error_result.rs`. + 33| |// 2. This test confirms the coverage generated when a program passes or fails a + 34| |// compiler-generated `TerminatorKind::Assert` (based on an overflow check, in this case). + 35| |// 3. Similar to how the coverage instrumentation handles `TerminatorKind::Call`, + 36| |// compiler-generated assertion failures are assumed to be a symptom of a program bug, not + 37| |// expected behavior. To simplify the coverage graphs and keep instrumented programs as + 38| |// small and fast as possible, `Assert` terminators are assumed to always succeed, and + 39| |// therefore are considered "non-branching" terminators. So, an `Assert` terminator does not + 40| |// get its own coverage counter. + 41| |// 4. After an unhandled panic or failed Assert, coverage results may not always be intuitive. + 42| |// In this test, the final count for the statements after the `if` block in `might_overflow()` + 43| |// is 4, even though the lines after `to_add + add_to` were executed only 3 times. Depending + 44| |// on the MIR graph and the structure of the code, this count could have been 3 (which might + 45| |// have been valid for the overflowed add `+`, but should have been 4 for the lines before + 46| |// the overflow. The reason for this potential uncertainty is, a `CounterKind` is incremented + 47| |// via StatementKind::Counter at the end of the block, but (as in the case in this test), + 48| |// a CounterKind::Expression is always evaluated. In this case, the expression was based on + 49| |// a `Counter` incremented as part of the evaluation of the `if` expression, which was + 50| |// executed, and counted, 4 times, before reaching the overflow add. + 51| | + 52| |// If the program did not overflow, the coverage for `might_overflow()` would look like this: + 53| |// + 54| |// 4| |fn might_overflow(to_add: u32) -> u32 { + 55| |// 5| 4| if to_add > 5 { + 56| |// 6| 0| println!("this will probably overflow"); + 57| |// 7| 4| } + 58| |// 8| 4| let add_to = u32::MAX - 5; + 59| |// 9| 4| println!("does {} + {} overflow?", add_to, to_add); + 60| |// 10| 4| let result = to_add + add_to; + 61| |// 11| 4| println!("continuing after overflow check"); + 62| |// 12| 4| result + 63| |// 13| 4|} + diff --git a/tests/run-coverage/overflow.rs b/tests/run-coverage/overflow.rs new file mode 100644 index 000000000..7df8de6f3 --- /dev/null +++ b/tests/run-coverage/overflow.rs @@ -0,0 +1,63 @@ +#![allow(unused_assignments)] +// failure-status: 101 + +fn might_overflow(to_add: u32) -> u32 { + if to_add > 5 { + println!("this will probably overflow"); + } + let add_to = u32::MAX - 5; + println!("does {} + {} overflow?", add_to, to_add); + let result = to_add + add_to; + println!("continuing after overflow check"); + result +} + +fn main() -> Result<(),u8> { + let mut countdown = 10; + while countdown > 0 { + if countdown == 1 { + let result = might_overflow(10); + println!("Result: {}", result); + } else if countdown < 5 { + let result = might_overflow(1); + println!("Result: {}", result); + } + countdown -= 1; + } + Ok(()) +} + +// Notes: +// 1. Compare this program and its coverage results to those of the very similar test `assert.rs`, +// and similar tests `panic_unwind.rs`, abort.rs` and `try_error_result.rs`. +// 2. This test confirms the coverage generated when a program passes or fails a +// compiler-generated `TerminatorKind::Assert` (based on an overflow check, in this case). +// 3. Similar to how the coverage instrumentation handles `TerminatorKind::Call`, +// compiler-generated assertion failures are assumed to be a symptom of a program bug, not +// expected behavior. To simplify the coverage graphs and keep instrumented programs as +// small and fast as possible, `Assert` terminators are assumed to always succeed, and +// therefore are considered "non-branching" terminators. So, an `Assert` terminator does not +// get its own coverage counter. +// 4. After an unhandled panic or failed Assert, coverage results may not always be intuitive. +// In this test, the final count for the statements after the `if` block in `might_overflow()` +// is 4, even though the lines after `to_add + add_to` were executed only 3 times. Depending +// on the MIR graph and the structure of the code, this count could have been 3 (which might +// have been valid for the overflowed add `+`, but should have been 4 for the lines before +// the overflow. The reason for this potential uncertainty is, a `CounterKind` is incremented +// via StatementKind::Counter at the end of the block, but (as in the case in this test), +// a CounterKind::Expression is always evaluated. In this case, the expression was based on +// a `Counter` incremented as part of the evaluation of the `if` expression, which was +// executed, and counted, 4 times, before reaching the overflow add. + +// If the program did not overflow, the coverage for `might_overflow()` would look like this: +// +// 4| |fn might_overflow(to_add: u32) -> u32 { +// 5| 4| if to_add > 5 { +// 6| 0| println!("this will probably overflow"); +// 7| 4| } +// 8| 4| let add_to = u32::MAX - 5; +// 9| 4| println!("does {} + {} overflow?", add_to, to_add); +// 10| 4| let result = to_add + add_to; +// 11| 4| println!("continuing after overflow check"); +// 12| 4| result +// 13| 4|} diff --git a/tests/run-coverage/panic_unwind.coverage b/tests/run-coverage/panic_unwind.coverage new file mode 100644 index 000000000..58b9ba448 --- /dev/null +++ b/tests/run-coverage/panic_unwind.coverage @@ -0,0 +1,32 @@ + 1| |#![allow(unused_assignments)] + 2| |// failure-status: 101 + 3| | + 4| 4|fn might_panic(should_panic: bool) { + 5| 4| if should_panic { + 6| 1| println!("panicking..."); + 7| 1| panic!("panics"); + 8| 3| } else { + 9| 3| println!("Don't Panic"); + 10| 3| } + 11| 3|} + 12| | + 13| 1|fn main() -> Result<(), u8> { + 14| 1| let mut countdown = 10; + 15| 11| while countdown > 0 { + 16| 11| if countdown == 1 { + 17| 1| might_panic(true); + 18| 10| } else if countdown < 5 { + 19| 3| might_panic(false); + 20| 6| } + 21| 10| countdown -= 1; + 22| | } + 23| 0| Ok(()) + 24| 0|} + 25| | + 26| |// Notes: + 27| |// 1. Compare this program and its coverage results to those of the similar tests `abort.rs` and + 28| |// `try_error_result.rs`. + 29| |// 2. Since the `panic_unwind.rs` test is allowed to unwind, it is also allowed to execute the + 30| |// normal program exit cleanup, including writing out the current values of the coverage + 31| |// counters. + diff --git a/tests/run-coverage/panic_unwind.rs b/tests/run-coverage/panic_unwind.rs new file mode 100644 index 000000000..638d2eb6a --- /dev/null +++ b/tests/run-coverage/panic_unwind.rs @@ -0,0 +1,31 @@ +#![allow(unused_assignments)] +// failure-status: 101 + +fn might_panic(should_panic: bool) { + if should_panic { + println!("panicking..."); + panic!("panics"); + } else { + println!("Don't Panic"); + } +} + +fn main() -> Result<(), u8> { + let mut countdown = 10; + while countdown > 0 { + if countdown == 1 { + might_panic(true); + } else if countdown < 5 { + might_panic(false); + } + countdown -= 1; + } + Ok(()) +} + +// Notes: +// 1. Compare this program and its coverage results to those of the similar tests `abort.rs` and +// `try_error_result.rs`. +// 2. Since the `panic_unwind.rs` test is allowed to unwind, it is also allowed to execute the +// normal program exit cleanup, including writing out the current values of the coverage +// counters. diff --git a/tests/run-coverage/partial_eq.coverage b/tests/run-coverage/partial_eq.coverage new file mode 100644 index 000000000..a77175af6 --- /dev/null +++ b/tests/run-coverage/partial_eq.coverage @@ -0,0 +1,48 @@ + 1| |// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the + 2| |// structure of this test. + 3| | + 4| 2|#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] + ^0 ^0 ^0 ^1 ^1 ^0^0 + 5| |pub struct Version { + 6| | major: usize, + 7| | minor: usize, + 8| | patch: usize, + 9| |} + 10| | + 11| |impl Version { + 12| 2| pub fn new(major: usize, minor: usize, patch: usize) -> Self { + 13| 2| Self { + 14| 2| major, + 15| 2| minor, + 16| 2| patch, + 17| 2| } + 18| 2| } + 19| |} + 20| | + 21| 1|fn main() { + 22| 1| let version_3_2_1 = Version::new(3, 2, 1); + 23| 1| let version_3_3_0 = Version::new(3, 3, 0); + 24| 1| + 25| 1| println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); + 26| 1|} + 27| | + 28| |/* + 29| | + 30| |This test verifies a bug was fixed that otherwise generated this error: + 31| | + 32| |thread 'rustc' panicked at 'No counters provided the source_hash for function: + 33| | Instance { + 34| | def: Item(WithOptConstParam { + 35| | did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + 36| | const_param_did: None + 37| | }), + 38| | substs: [] + 39| | }' + 40| |The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage + 41| |without a code region associated with any `Counter`. Code regions were associated with at least + 42| |one expression, which is allowed, but the `function_source_hash` was only passed to the codegen + 43| |(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the + 44| |`function_source_hash` without a code region, if necessary. + 45| | + 46| |*/ + diff --git a/tests/run-coverage/partial_eq.rs b/tests/run-coverage/partial_eq.rs new file mode 100644 index 000000000..4ceaba9b1 --- /dev/null +++ b/tests/run-coverage/partial_eq.rs @@ -0,0 +1,46 @@ +// This test confirms an earlier problem was resolved, supporting the MIR graph generated by the +// structure of this test. + +#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] +pub struct Version { + major: usize, + minor: usize, + patch: usize, +} + +impl Version { + pub fn new(major: usize, minor: usize, patch: usize) -> Self { + Self { + major, + minor, + patch, + } + } +} + +fn main() { + let version_3_2_1 = Version::new(3, 2, 1); + let version_3_3_0 = Version::new(3, 3, 0); + + println!("{:?} < {:?} = {}", version_3_2_1, version_3_3_0, version_3_2_1 < version_3_3_0); +} + +/* + +This test verifies a bug was fixed that otherwise generated this error: + +thread 'rustc' panicked at 'No counters provided the source_hash for function: + Instance { + def: Item(WithOptConstParam { + did: DefId(0:101 ~ autocfg[c44a]::version::{impl#2}::partial_cmp), + const_param_did: None + }), + substs: [] + }' +The `PartialOrd` derived by `Version` happened to generate a MIR that generated coverage +without a code region associated with any `Counter`. Code regions were associated with at least +one expression, which is allowed, but the `function_source_hash` was only passed to the codegen +(coverage mapgen) phase from a `Counter`s code region. A new method was added to pass the +`function_source_hash` without a code region, if necessary. + +*/ diff --git a/tests/run-coverage/simple_loop.coverage b/tests/run-coverage/simple_loop.coverage new file mode 100644 index 000000000..feb83bad6 --- /dev/null +++ b/tests/run-coverage/simple_loop.coverage @@ -0,0 +1,37 @@ + 1| |#![allow(unused_assignments)] + 2| | + 3| 1|fn main() { + 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; + 10| 1| + 11| 1| if + 12| 1| is_true + 13| 1| { + 14| 1| countdown + 15| 1| = + 16| 1| 10 + 17| 1| ; + 18| 1| } + ^0 + 19| | + 20| | loop + 21| | { + 22| | if + 23| 11| countdown + 24| 11| == + 25| 11| 0 + 26| | { + 27| 1| break + 28| | ; + 29| 10| } + 30| 10| countdown + 31| 10| -= + 32| 10| 1 + 33| | ; + 34| | } + 35| 1|} + diff --git a/tests/run-coverage/simple_loop.rs b/tests/run-coverage/simple_loop.rs new file mode 100644 index 000000000..6f7f23475 --- /dev/null +++ b/tests/run-coverage/simple_loop.rs @@ -0,0 +1,35 @@ +#![allow(unused_assignments)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 0; + + if + is_true + { + countdown + = + 10 + ; + } + + loop + { + if + countdown + == + 0 + { + break + ; + } + countdown + -= + 1 + ; + } +} diff --git a/tests/run-coverage/simple_match.coverage b/tests/run-coverage/simple_match.coverage new file mode 100644 index 000000000..b92982131 --- /dev/null +++ b/tests/run-coverage/simple_match.coverage @@ -0,0 +1,45 @@ + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| 1|fn main() { + 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 = 1; + 10| 1| if is_true { + 11| 1| countdown = 0; + 12| 1| } + ^0 + 13| | + 14| | for + 15| | _ + 16| | in + 17| 3| 0..2 + 18| | { + 19| | let z + 20| | ; + 21| | match + 22| 2| countdown + 23| | { + 24| 1| x + 25| | if + 26| 2| x + 27| 2| < + 28| 2| 1 + 29| | => + 30| 1| { + 31| 1| z = countdown + 32| 1| ; + 33| 1| let y = countdown + 34| 1| ; + 35| 1| countdown = 10 + 36| 1| ; + 37| 1| } + 38| | _ + 39| | => + 40| 1| {} + 41| | } + 42| | } + 43| 1|} + diff --git a/tests/run-coverage/simple_match.rs b/tests/run-coverage/simple_match.rs new file mode 100644 index 000000000..be99e59a8 --- /dev/null +++ b/tests/run-coverage/simple_match.rs @@ -0,0 +1,43 @@ +#![allow(unused_assignments, unused_variables)] + +fn main() { + // Initialize test constants in a way that cannot be determined at compile time, to ensure + // rustc and LLVM cannot optimize out statements (or coverage counters) downstream from + // dependent conditions. + let is_true = std::env::args().len() == 1; + + let mut countdown = 1; + if is_true { + countdown = 0; + } + + for + _ + in + 0..2 + { + let z + ; + match + countdown + { + x + if + x + < + 1 + => + { + z = countdown + ; + let y = countdown + ; + countdown = 10 + ; + } + _ + => + {} + } + } +} diff --git a/tests/run-coverage/sort_groups.coverage b/tests/run-coverage/sort_groups.coverage new file mode 100644 index 000000000..81468cb35 --- /dev/null +++ b/tests/run-coverage/sort_groups.coverage @@ -0,0 +1,49 @@ + 1| |// compile-flags: --edition=2021 + 2| | + 3| |// Demonstrate that `sort_subviews.py` can sort instantiation groups into a + 4| |// predictable order, while preserving their heterogeneous contents. + 5| | + 6| 1|fn main() { + 7| 1| let cond = std::env::args().len() > 1; + 8| 1| generic_fn::<()>(cond); + 9| 1| generic_fn::<&'static str>(!cond); + 10| 1| if false { + 11| 0| generic_fn::<char>(cond); + 12| 1| } + 13| 1| generic_fn::<i32>(cond); + 14| 1| other_fn(); + 15| 1|} + 16| | + 17| 3|fn generic_fn<T>(cond: bool) { + 18| 3| if cond { + 19| 1| println!("{}", std::any::type_name::<T>()); + 20| 2| } + 21| 3|} + ------------------ + | Unexecuted instantiation: sort_groups::generic_fn::<char> + ------------------ + | sort_groups::generic_fn::<&str>: + | 17| 1|fn generic_fn<T>(cond: bool) { + | 18| 1| if cond { + | 19| 1| println!("{}", std::any::type_name::<T>()); + | 20| 1| } + | ^0 + | 21| 1|} + ------------------ + | sort_groups::generic_fn::<()>: + | 17| 1|fn generic_fn<T>(cond: bool) { + | 18| 1| if cond { + | 19| 0| println!("{}", std::any::type_name::<T>()); + | 20| 1| } + | 21| 1|} + ------------------ + | sort_groups::generic_fn::<i32>: + | 17| 1|fn generic_fn<T>(cond: bool) { + | 18| 1| if cond { + | 19| 0| println!("{}", std::any::type_name::<T>()); + | 20| 1| } + | 21| 1|} + ------------------ + 22| | + 23| 1|fn other_fn() {} + diff --git a/tests/run-coverage/sort_groups.rs b/tests/run-coverage/sort_groups.rs new file mode 100644 index 000000000..f89f9f3ec --- /dev/null +++ b/tests/run-coverage/sort_groups.rs @@ -0,0 +1,23 @@ +// compile-flags: --edition=2021 + +// Demonstrate that `sort_subviews.py` can sort instantiation groups into a +// predictable order, while preserving their heterogeneous contents. + +fn main() { + let cond = std::env::args().len() > 1; + generic_fn::<()>(cond); + generic_fn::<&'static str>(!cond); + if false { + generic_fn::<char>(cond); + } + generic_fn::<i32>(cond); + other_fn(); +} + +fn generic_fn<T>(cond: bool) { + if cond { + println!("{}", std::any::type_name::<T>()); + } +} + +fn other_fn() {} diff --git a/tests/run-coverage/test_harness.coverage b/tests/run-coverage/test_harness.coverage new file mode 100644 index 000000000..93bd1cfcb --- /dev/null +++ b/tests/run-coverage/test_harness.coverage @@ -0,0 +1,11 @@ + 1| |// Verify that the entry point injected by the test harness doesn't cause + 2| |// weird artifacts in the coverage report (e.g. issue #10749). + 3| | + 4| |// compile-flags: --test + 5| | + 6| |#[allow(dead_code)] + 7| 0|fn unused() {} + 8| | + 9| 1|#[test] + 10| 1|fn my_test() {} + diff --git a/tests/run-coverage/test_harness.rs b/tests/run-coverage/test_harness.rs new file mode 100644 index 000000000..12a755734 --- /dev/null +++ b/tests/run-coverage/test_harness.rs @@ -0,0 +1,10 @@ +// Verify that the entry point injected by the test harness doesn't cause +// weird artifacts in the coverage report (e.g. issue #10749). + +// compile-flags: --test + +#[allow(dead_code)] +fn unused() {} + +#[test] +fn my_test() {} diff --git a/tests/run-coverage/tight_inf_loop.coverage b/tests/run-coverage/tight_inf_loop.coverage new file mode 100644 index 000000000..2d4c57f45 --- /dev/null +++ b/tests/run-coverage/tight_inf_loop.coverage @@ -0,0 +1,6 @@ + 1| 1|fn main() { + 2| 1| if false { + 3| 0| loop {} + 4| 1| } + 5| 1|} + diff --git a/tests/run-coverage/tight_inf_loop.rs b/tests/run-coverage/tight_inf_loop.rs new file mode 100644 index 000000000..cef99027a --- /dev/null +++ b/tests/run-coverage/tight_inf_loop.rs @@ -0,0 +1,5 @@ +fn main() { + if false { + loop {} + } +} diff --git a/tests/run-coverage/try_error_result.coverage b/tests/run-coverage/try_error_result.coverage new file mode 100644 index 000000000..efe573a56 --- /dev/null +++ b/tests/run-coverage/try_error_result.coverage @@ -0,0 +1,125 @@ + 1| |#![allow(unused_assignments)] + 2| |// failure-status: 1 + 3| | + 4| 6|fn call(return_error: bool) -> Result<(),()> { + 5| 6| if return_error { + 6| 1| Err(()) + 7| | } else { + 8| 5| Ok(()) + 9| | } + 10| 6|} + 11| | + 12| 1|fn test1() -> Result<(),()> { + 13| 1| let mut + 14| 1| countdown = 10 + 15| | ; + 16| | for + 17| | _ + 18| | in + 19| 6| 0..10 + 20| | { + 21| 6| countdown + 22| 6| -= 1 + 23| 6| ; + 24| 6| if + 25| 6| countdown < 5 + 26| | { + 27| 1| call(/*return_error=*/ true)?; + 28| 0| call(/*return_error=*/ false)?; + 29| | } + 30| | else + 31| | { + 32| 5| call(/*return_error=*/ false)?; + ^0 + 33| | } + 34| | } + 35| 0| Ok(()) + 36| 1|} + 37| | + 38| |struct Thing1; + 39| |impl Thing1 { + 40| 18| fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> { + 41| 18| if return_error { + 42| 1| Err(()) + 43| | } else { + 44| 17| Ok(Thing2{}) + 45| | } + 46| 18| } + 47| |} + 48| | + 49| |struct Thing2; + 50| |impl Thing2 { + 51| 17| fn call(&self, return_error: bool) -> Result<u32,()> { + 52| 17| if return_error { + 53| 2| Err(()) + 54| | } else { + 55| 15| Ok(57) + 56| | } + 57| 17| } + 58| |} + 59| | + 60| 1|fn test2() -> Result<(),()> { + 61| 1| let thing1 = Thing1{}; + 62| 1| let mut + 63| 1| countdown = 10 + 64| | ; + 65| | for + 66| | _ + 67| | in + 68| 6| 0..10 + 69| | { + 70| 6| countdown + 71| 6| -= 1 + 72| 6| ; + 73| 6| if + 74| 6| countdown < 5 + 75| | { + 76| 1| thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail"); + ^0 + 77| 1| thing1 + 78| 1| . + 79| 1| get_thing_2(/*return_error=*/ false) + 80| 0| ? + 81| | . + 82| 1| call(/*return_error=*/ true) + 83| 1| . + 84| 1| expect_err( + 85| 1| "call should fail" + 86| 1| ); + 87| 1| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?; + ^0 ^0 ^0 + 88| 0| assert_eq!(val, 57); + 89| 0| let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?; + 90| 0| assert_eq!(val, 57); + 91| | } + 92| | else + 93| | { + 94| 5| let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?; + ^0 ^0 + 95| 5| assert_eq!(val, 57); + 96| 5| let val = thing1 + 97| 5| .get_thing_2(/*return_error=*/ false)? + ^0 + 98| 5| .call(/*return_error=*/ false)?; + ^0 + 99| 5| assert_eq!(val, 57); + 100| 5| let val = thing1 + 101| 5| .get_thing_2(/*return_error=*/ false) + 102| 0| ? + 103| 5| .call(/*return_error=*/ false) + 104| 0| ? + 105| | ; + 106| 5| assert_eq!(val, 57); + 107| | } + 108| | } + 109| 0| Ok(()) + 110| 1|} + 111| | + 112| 1|fn main() -> Result<(),()> { + 113| 1| test1().expect_err("test1 should fail"); + 114| 1| test2() + 115| 1| ? + 116| | ; + 117| 0| Ok(()) + 118| 1|} + diff --git a/tests/run-coverage/try_error_result.rs b/tests/run-coverage/try_error_result.rs new file mode 100644 index 000000000..9eb1d2db2 --- /dev/null +++ b/tests/run-coverage/try_error_result.rs @@ -0,0 +1,118 @@ +#![allow(unused_assignments)] +// failure-status: 1 + +fn call(return_error: bool) -> Result<(),()> { + if return_error { + Err(()) + } else { + Ok(()) + } +} + +fn test1() -> Result<(),()> { + let mut + countdown = 10 + ; + for + _ + in + 0..10 + { + countdown + -= 1 + ; + if + countdown < 5 + { + call(/*return_error=*/ true)?; + call(/*return_error=*/ false)?; + } + else + { + call(/*return_error=*/ false)?; + } + } + Ok(()) +} + +struct Thing1; +impl Thing1 { + fn get_thing_2(&self, return_error: bool) -> Result<Thing2,()> { + if return_error { + Err(()) + } else { + Ok(Thing2{}) + } + } +} + +struct Thing2; +impl Thing2 { + fn call(&self, return_error: bool) -> Result<u32,()> { + if return_error { + Err(()) + } else { + Ok(57) + } + } +} + +fn test2() -> Result<(),()> { + let thing1 = Thing1{}; + let mut + countdown = 10 + ; + for + _ + in + 0..10 + { + countdown + -= 1 + ; + if + countdown < 5 + { + thing1.get_thing_2(/*err=*/ false)?.call(/*err=*/ true).expect_err("call should fail"); + thing1 + . + get_thing_2(/*return_error=*/ false) + ? + . + call(/*return_error=*/ true) + . + expect_err( + "call should fail" + ); + let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ true)?; + assert_eq!(val, 57); + let val = thing1.get_thing_2(/*return_error=*/ true)?.call(/*return_error=*/ false)?; + assert_eq!(val, 57); + } + else + { + let val = thing1.get_thing_2(/*return_error=*/ false)?.call(/*return_error=*/ false)?; + assert_eq!(val, 57); + let val = thing1 + .get_thing_2(/*return_error=*/ false)? + .call(/*return_error=*/ false)?; + assert_eq!(val, 57); + let val = thing1 + .get_thing_2(/*return_error=*/ false) + ? + .call(/*return_error=*/ false) + ? + ; + assert_eq!(val, 57); + } + } + Ok(()) +} + +fn main() -> Result<(),()> { + test1().expect_err("test1 should fail"); + test2() + ? + ; + Ok(()) +} diff --git a/tests/run-coverage/unused.coverage b/tests/run-coverage/unused.coverage new file mode 100644 index 000000000..15fcf21c0 --- /dev/null +++ b/tests/run-coverage/unused.coverage @@ -0,0 +1,62 @@ + 1| 2|fn foo<T>(x: T) { + 2| 2| let mut i = 0; + 3| 22| while i < 10 { + 4| 20| i != 0 || i != 0; + ^2 + 5| 20| i += 1; + 6| | } + 7| 2|} + ------------------ + | unused::foo::<f32>: + | 1| 1|fn foo<T>(x: T) { + | 2| 1| let mut i = 0; + | 3| 11| while i < 10 { + | 4| 10| i != 0 || i != 0; + | ^1 + | 5| 10| i += 1; + | 6| | } + | 7| 1|} + ------------------ + | unused::foo::<u32>: + | 1| 1|fn foo<T>(x: T) { + | 2| 1| let mut i = 0; + | 3| 11| while i < 10 { + | 4| 10| i != 0 || i != 0; + | ^1 + | 5| 10| i += 1; + | 6| | } + | 7| 1|} + ------------------ + 8| | + 9| 0|fn unused_template_func<T>(x: T) { + 10| 0| let mut i = 0; + 11| 0| while i < 10 { + 12| 0| i != 0 || i != 0; + 13| 0| i += 1; + 14| | } + 15| 0|} + 16| | + 17| 0|fn unused_func(mut a: u32) { + 18| 0| if a != 0 { + 19| 0| a += 1; + 20| 0| } + 21| 0|} + 22| | + 23| 0|fn unused_func2(mut a: u32) { + 24| 0| if a != 0 { + 25| 0| a += 1; + 26| 0| } + 27| 0|} + 28| | + 29| 0|fn unused_func3(mut a: u32) { + 30| 0| if a != 0 { + 31| 0| a += 1; + 32| 0| } + 33| 0|} + 34| | + 35| 1|fn main() -> Result<(), u8> { + 36| 1| foo::<u32>(0); + 37| 1| foo::<f32>(0.0); + 38| 1| Ok(()) + 39| 1|} + diff --git a/tests/run-coverage/unused.rs b/tests/run-coverage/unused.rs new file mode 100644 index 000000000..fb6113eb0 --- /dev/null +++ b/tests/run-coverage/unused.rs @@ -0,0 +1,39 @@ +fn foo<T>(x: T) { + let mut i = 0; + while i < 10 { + i != 0 || i != 0; + i += 1; + } +} + +fn unused_template_func<T>(x: T) { + let mut i = 0; + while i < 10 { + i != 0 || i != 0; + i += 1; + } +} + +fn unused_func(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn unused_func2(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn unused_func3(mut a: u32) { + if a != 0 { + a += 1; + } +} + +fn main() -> Result<(), u8> { + foo::<u32>(0); + foo::<f32>(0.0); + Ok(()) +} diff --git a/tests/run-coverage/unused_mod.coverage b/tests/run-coverage/unused_mod.coverage new file mode 100644 index 000000000..e1d82f66f --- /dev/null +++ b/tests/run-coverage/unused_mod.coverage @@ -0,0 +1,13 @@ +$DIR/auxiliary/unused_mod_helper.rs: + 1| 0|pub fn never_called_function() { + 2| 0| println!("I am never called"); + 3| 0|} + +$DIR/unused_mod.rs: + 1| |#[path = "auxiliary/unused_mod_helper.rs"] + 2| |mod unused_module; + 3| | + 4| 1|fn main() { + 5| 1| println!("hello world!"); + 6| 1|} + diff --git a/tests/run-coverage/unused_mod.rs b/tests/run-coverage/unused_mod.rs new file mode 100644 index 000000000..6e62839c9 --- /dev/null +++ b/tests/run-coverage/unused_mod.rs @@ -0,0 +1,6 @@ +#[path = "auxiliary/unused_mod_helper.rs"] +mod unused_module; + +fn main() { + println!("hello world!"); +} diff --git a/tests/run-coverage/uses_crate.coverage b/tests/run-coverage/uses_crate.coverage new file mode 100644 index 000000000..a3b78e214 --- /dev/null +++ b/tests/run-coverage/uses_crate.coverage @@ -0,0 +1,170 @@ +$DIR/auxiliary/used_crate.rs: + 1| |#![allow(unused_assignments, unused_variables)] + 2| |// compile-flags: -C opt-level=3 + 3| |use std::fmt::Debug; // ^^ validates coverage now works with optimizations + 4| | + 5| 1|pub fn used_function() { + 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 { + 12| 1| countdown = 10; + 13| 1| } + ^0 + 14| 1| use_this_lib_crate(); + 15| 1|} + 16| | + 17| 2|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + 18| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + 19| 2|} + ------------------ + | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> + ------------------ + | used_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>: + | 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + | 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 19| 1|} + ------------------ + | used_crate::used_only_from_bin_crate_generic_function::<&str>: + | 17| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + | 18| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 19| 1|} + ------------------ + 20| |// Expect for above function: `Unexecuted instantiation` (see below) + 21| 2|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + 22| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + 23| 2|} + ------------------ + | 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) { + | 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | 23| 1|} + ------------------ + | 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) { + | 22| 1| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | 23| 1|} + ------------------ + 24| | + 25| 2|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + 26| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + 27| 2|} + ------------------ + | used_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 25| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 26| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 27| 1|} + ------------------ + | used_crate::used_from_bin_crate_and_lib_crate_generic_function::<alloc::vec::Vec<i32>>: + | 25| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 26| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 27| 1|} + ------------------ + 28| | + 29| 2|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + 30| 2| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + 31| 2|} + ------------------ + | used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 29| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 30| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 31| 1|} + ------------------ + | used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 29| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 30| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 31| 1|} + ------------------ + 32| | + 33| 0|pub fn unused_generic_function<T: Debug>(arg: T) { + 34| 0| println!("unused_generic_function with {:?}", arg); + 35| 0|} + 36| | + 37| 0|pub fn unused_function() { + 38| 0| let is_true = std::env::args().len() == 1; + 39| 0| let mut countdown = 2; + 40| 0| if !is_true { + 41| 0| countdown = 20; + 42| 0| } + 43| 0|} + 44| | + 45| 0|fn unused_private_function() { + 46| 0| let is_true = std::env::args().len() == 1; + 47| 0| let mut countdown = 2; + 48| 0| if !is_true { + 49| 0| countdown = 20; + 50| 0| } + 51| 0|} + 52| | + 53| 1|fn use_this_lib_crate() { + 54| 1| used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + 55| 1| used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + 56| 1| "used from library used_crate.rs", + 57| 1| ); + 58| 1| let some_vec = vec![5, 6, 7, 8]; + 59| 1| used_only_from_this_lib_crate_generic_function(some_vec); + 60| 1| used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); + 61| 1|} + 62| | + 63| |// FIXME(#79651): "Unexecuted instantiation" errors appear in coverage results, + 64| |// for example: + 65| |// + 66| |// | Unexecuted instantiation: used_crate::used_only_from_bin_crate_generic_function::<_> + 67| |// + 68| |// These notices appear when `llvm-cov` shows instantiations. This may be a + 69| |// default option, but it can be suppressed with: + 70| |// + 71| |// ```shell + 72| |// $ `llvm-cov show --show-instantiations=0 ...` + 73| |// ``` + 74| |// + 75| |// The notice is triggered because the function is unused by the library itself, + 76| |// and when the library is compiled, a synthetic function is generated, so + 77| |// unused function coverage can be reported. Coverage can be skipped for unused + 78| |// generic functions with: + 79| |// + 80| |// ```shell + 81| |// $ `rustc -Zunstable-options -C instrument-coverage=except-unused-generics ...` + 82| |// ``` + 83| |// + 84| |// Even though this function is used by `uses_crate.rs` (and + 85| |// counted), with substitutions for `T`, those instantiations are only generated + 86| |// when the generic function is actually used (from the binary, not from this + 87| |// library crate). So the test result shows coverage for all instantiated + 88| |// versions and their generic type substitutions, plus the `Unexecuted + 89| |// instantiation` message for the non-substituted version. This is valid, but + 90| |// unfortunately a little confusing. + 91| |// + 92| |// The library crate has its own coverage map, and the only way to show unused + 93| |// coverage of a generic function is to include the generic function in the + 94| |// coverage map, marked as an "unused function". If the library were used by + 95| |// another binary that never used this generic function, then it would be valid + 96| |// to show the unused generic, with unknown substitution (`_`). + 97| |// + 98| |// The alternative is to exclude all generics from being included in the "unused + 99| |// functions" list, which would then omit coverage results for + 100| |// `unused_generic_function<T>()`, below. + +$DIR/uses_crate.rs: + 1| |// FIXME #110395 + 2| |// ignore-linux + 3| | + 4| |// Validates coverage now works with optimizations + 5| |// compile-flags: -C opt-level=3 + 6| | + 7| |#![allow(unused_assignments, unused_variables)] + 8| | + 9| |// aux-build:used_crate.rs + 10| |extern crate used_crate; + 11| | + 12| 1|fn main() { + 13| 1| used_crate::used_function(); + 14| 1| let some_vec = vec![1, 2, 3, 4]; + 15| 1| used_crate::used_only_from_bin_crate_generic_function(&some_vec); + 16| 1| used_crate::used_only_from_bin_crate_generic_function("used from bin uses_crate.rs"); + 17| 1| used_crate::used_from_bin_crate_and_lib_crate_generic_function(some_vec); + 18| 1| used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function("interesting?"); + 19| 1|} + diff --git a/tests/run-coverage/uses_crate.rs b/tests/run-coverage/uses_crate.rs new file mode 100644 index 000000000..ab466970f --- /dev/null +++ b/tests/run-coverage/uses_crate.rs @@ -0,0 +1,19 @@ +// FIXME #110395 +// ignore-linux + +// Validates coverage now works with optimizations +// compile-flags: -C opt-level=3 + +#![allow(unused_assignments, unused_variables)] + +// aux-build:used_crate.rs +extern crate used_crate; + +fn main() { + used_crate::used_function(); + let some_vec = vec![1, 2, 3, 4]; + used_crate::used_only_from_bin_crate_generic_function(&some_vec); + used_crate::used_only_from_bin_crate_generic_function("used from bin uses_crate.rs"); + used_crate::used_from_bin_crate_and_lib_crate_generic_function(some_vec); + used_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function("interesting?"); +} diff --git a/tests/run-coverage/uses_inline_crate.coverage b/tests/run-coverage/uses_inline_crate.coverage new file mode 100644 index 000000000..f878d8107 --- /dev/null +++ b/tests/run-coverage/uses_inline_crate.coverage @@ -0,0 +1,164 @@ +$DIR/auxiliary/used_inline_crate.rs: + 1| |#![allow(unused_assignments, unused_variables)] + 2| | + 3| |// compile-flags: -C opt-level=3 + 4| |// ^^ validates coverage now works with optimizations + 5| |use std::fmt::Debug; + 6| | + 7| 1|pub fn used_function() { + 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 { + 14| 1| countdown = 10; + 15| 1| } + ^0 + 16| 1| use_this_lib_crate(); + 17| 1|} + 18| | + 19| |#[inline(always)] + 20| 1|pub fn used_inline_function() { + 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 { + 27| 1| countdown = 10; + 28| 1| } + ^0 + 29| 1| use_this_lib_crate(); + 30| 1|} + 31| | + 32| | + 33| | + 34| | + 35| | + 36| | + 37| | + 38| |#[inline(always)] + 39| 2|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + 40| 2| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + 41| 2|} + ------------------ + | Unexecuted instantiation: used_inline_crate::used_only_from_bin_crate_generic_function::<_> + ------------------ + | used_inline_crate::used_only_from_bin_crate_generic_function::<&alloc::vec::Vec<i32>>: + | 39| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + | 40| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 41| 1|} + ------------------ + | used_inline_crate::used_only_from_bin_crate_generic_function::<&str>: + | 39| 1|pub fn used_only_from_bin_crate_generic_function<T: Debug>(arg: T) { + | 40| 1| println!("used_only_from_bin_crate_generic_function with {:?}", arg); + | 41| 1|} + ------------------ + 42| |// Expect for above function: `Unexecuted instantiation` (see notes in `used_crate.rs`) + 43| | + 44| |#[inline(always)] + 45| 4|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + 46| 4| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + 47| 4|} + ------------------ + | used_inline_crate::used_only_from_this_lib_crate_generic_function::<&str>: + | 45| 2|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + | 46| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | 47| 2|} + ------------------ + | used_inline_crate::used_only_from_this_lib_crate_generic_function::<alloc::vec::Vec<i32>>: + | 45| 2|pub fn used_only_from_this_lib_crate_generic_function<T: Debug>(arg: T) { + | 46| 2| println!("used_only_from_this_lib_crate_generic_function with {:?}", arg); + | 47| 2|} + ------------------ + 48| | + 49| |#[inline(always)] + 50| 3|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + 51| 3| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + 52| 3|} + ------------------ + | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 50| 2|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 51| 2| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 52| 2|} + ------------------ + | used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function::<alloc::vec::Vec<i32>>: + | 50| 1|pub fn used_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 51| 1| println!("used_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 52| 1|} + ------------------ + 53| | + 54| |#[inline(always)] + 55| 3|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + 56| 3| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + 57| 3|} + ------------------ + | used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 55| 1|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 56| 1| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 57| 1|} + ------------------ + | used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function::<&str>: + | 55| 2|pub fn used_with_same_type_from_bin_crate_and_lib_crate_generic_function<T: Debug>(arg: T) { + | 56| 2| println!("used_with_same_type_from_bin_crate_and_lib_crate_generic_function with {:?}", arg); + | 57| 2|} + ------------------ + 58| | + 59| |#[inline(always)] + 60| 0|pub fn unused_generic_function<T: Debug>(arg: T) { + 61| 0| println!("unused_generic_function with {:?}", arg); + 62| 0|} + 63| | + 64| |#[inline(always)] + 65| 0|pub fn unused_function() { + 66| 0| let is_true = std::env::args().len() == 1; + 67| 0| let mut countdown = 2; + 68| 0| if !is_true { + 69| 0| countdown = 20; + 70| 0| } + 71| 0|} + 72| | + 73| |#[inline(always)] + 74| 0|fn unused_private_function() { + 75| 0| let is_true = std::env::args().len() == 1; + 76| 0| let mut countdown = 2; + 77| 0| if !is_true { + 78| 0| countdown = 20; + 79| 0| } + 80| 0|} + 81| | + 82| 2|fn use_this_lib_crate() { + 83| 2| used_from_bin_crate_and_lib_crate_generic_function("used from library used_crate.rs"); + 84| 2| used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + 85| 2| "used from library used_crate.rs", + 86| 2| ); + 87| 2| let some_vec = vec![5, 6, 7, 8]; + 88| 2| used_only_from_this_lib_crate_generic_function(some_vec); + 89| 2| used_only_from_this_lib_crate_generic_function("used ONLY from library used_crate.rs"); + 90| 2|} + +$DIR/uses_inline_crate.rs: + 1| |// FIXME #110395 + 2| |// ignore-linux + 3| | + 4| |// Validates coverage now works with optimizations + 5| |// compile-flags: -C opt-level=3 + 6| | + 7| |#![allow(unused_assignments, unused_variables)] + 8| | + 9| |// aux-build:used_inline_crate.rs + 10| |extern crate used_inline_crate; + 11| | + 12| 1|fn main() { + 13| 1| used_inline_crate::used_function(); + 14| 1| used_inline_crate::used_inline_function(); + 15| 1| let some_vec = vec![1, 2, 3, 4]; + 16| 1| used_inline_crate::used_only_from_bin_crate_generic_function(&some_vec); + 17| 1| used_inline_crate::used_only_from_bin_crate_generic_function("used from bin uses_crate.rs"); + 18| 1| used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function(some_vec); + 19| 1| used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + 20| 1| "interesting?", + 21| 1| ); + 22| 1|} + diff --git a/tests/run-coverage/uses_inline_crate.rs b/tests/run-coverage/uses_inline_crate.rs new file mode 100644 index 000000000..4bd66d2f8 --- /dev/null +++ b/tests/run-coverage/uses_inline_crate.rs @@ -0,0 +1,22 @@ +// FIXME #110395 +// ignore-linux + +// Validates coverage now works with optimizations +// compile-flags: -C opt-level=3 + +#![allow(unused_assignments, unused_variables)] + +// aux-build:used_inline_crate.rs +extern crate used_inline_crate; + +fn main() { + used_inline_crate::used_function(); + used_inline_crate::used_inline_function(); + let some_vec = vec![1, 2, 3, 4]; + used_inline_crate::used_only_from_bin_crate_generic_function(&some_vec); + used_inline_crate::used_only_from_bin_crate_generic_function("used from bin uses_crate.rs"); + used_inline_crate::used_from_bin_crate_and_lib_crate_generic_function(some_vec); + used_inline_crate::used_with_same_type_from_bin_crate_and_lib_crate_generic_function( + "interesting?", + ); +} diff --git a/tests/run-coverage/while.coverage b/tests/run-coverage/while.coverage new file mode 100644 index 000000000..efa7d083f --- /dev/null +++ b/tests/run-coverage/while.coverage @@ -0,0 +1,6 @@ + 1| 1|fn main() { + 2| 1| let num = 9; + 3| 1| while num >= 10 { + 4| 0| } + 5| 1|} + diff --git a/tests/run-coverage/while.rs b/tests/run-coverage/while.rs new file mode 100644 index 000000000..781b90b35 --- /dev/null +++ b/tests/run-coverage/while.rs @@ -0,0 +1,5 @@ +fn main() { + let num = 9; + while num >= 10 { + } +} diff --git a/tests/run-coverage/while_early_ret.coverage b/tests/run-coverage/while_early_ret.coverage new file mode 100644 index 000000000..2ce94e013 --- /dev/null +++ b/tests/run-coverage/while_early_ret.coverage @@ -0,0 +1,43 @@ + 1| |#![allow(unused_assignments)] + 2| |// failure-status: 1 + 3| | + 4| 1|fn main() -> Result<(),u8> { + 5| 1| let mut countdown = 10; + 6| | while + 7| 7| countdown + 8| 7| > + 9| 7| 0 + 10| | { + 11| | if + 12| 7| countdown + 13| 7| < + 14| 7| 5 + 15| | { + 16| | return + 17| | if + 18| 1| countdown + 19| 1| > + 20| 1| 8 + 21| | { + 22| 0| Ok(()) + 23| | } + 24| | else + 25| | { + 26| 1| Err(1) + 27| | } + 28| | ; + 29| 6| } + 30| 6| countdown + 31| 6| -= + 32| 6| 1 + 33| | ; + 34| | } + 35| 0| Ok(()) + 36| 1|} + 37| | + 38| |// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and + 39| |// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux + 40| |// and MacOS. But on Windows (MSVC, at least), the call to `std::process::exit()` exits the program + 41| |// without saving the InstrProf coverage counters. The use of `std::process:exit()` is not critical + 42| |// to the coverage test for early returns, but this is a limitation that should be fixed. + diff --git a/tests/run-coverage/while_early_ret.rs b/tests/run-coverage/while_early_ret.rs new file mode 100644 index 000000000..1c83c8fc7 --- /dev/null +++ b/tests/run-coverage/while_early_ret.rs @@ -0,0 +1,42 @@ +#![allow(unused_assignments)] +// failure-status: 1 + +fn main() -> Result<(),u8> { + let mut countdown = 10; + while + countdown + > + 0 + { + if + countdown + < + 5 + { + return + if + countdown + > + 8 + { + Ok(()) + } + else + { + Err(1) + } + ; + } + countdown + -= + 1 + ; + } + Ok(()) +} + +// ISSUE(77553): Originally, this test had `Err(1)` on line 22 (instead of `Ok(())`) and +// `std::process::exit(2)` on line 26 (instead of `Err(1)`); and this worked as expected on Linux +// and MacOS. But on Windows (MSVC, at least), the call to `std::process::exit()` exits the program +// without saving the InstrProf coverage counters. The use of `std::process:exit()` is not critical +// to the coverage test for early returns, but this is a limitation that should be fixed. diff --git a/tests/run-coverage/yield.coverage b/tests/run-coverage/yield.coverage new file mode 100644 index 000000000..6e2f23ee7 --- /dev/null +++ b/tests/run-coverage/yield.coverage @@ -0,0 +1,38 @@ + 1| |#![feature(generators, generator_trait)] + 2| |#![allow(unused_assignments)] + 3| | + 4| |use std::ops::{Generator, GeneratorState}; + 5| |use std::pin::Pin; + 6| | + 7| 1|fn main() { + 8| 1| let mut generator = || { + 9| 1| yield 1; + 10| 1| return "foo" + 11| 1| }; + 12| | + 13| 1| match Pin::new(&mut generator).resume(()) { + 14| 1| GeneratorState::Yielded(1) => {} + 15| 0| _ => panic!("unexpected value from resume"), + 16| | } + 17| 1| match Pin::new(&mut generator).resume(()) { + 18| 1| GeneratorState::Complete("foo") => {} + 19| 0| _ => panic!("unexpected value from resume"), + 20| | } + 21| | + 22| 1| let mut generator = || { + 23| 1| yield 1; + 24| 1| yield 2; + 25| 0| yield 3; + 26| 0| return "foo" + 27| 0| }; + 28| | + 29| 1| match Pin::new(&mut generator).resume(()) { + 30| 1| GeneratorState::Yielded(1) => {} + 31| 0| _ => panic!("unexpected value from resume"), + 32| | } + 33| 1| match Pin::new(&mut generator).resume(()) { + 34| 1| GeneratorState::Yielded(2) => {} + 35| 0| _ => panic!("unexpected value from resume"), + 36| | } + 37| 1|} + diff --git a/tests/run-coverage/yield.rs b/tests/run-coverage/yield.rs new file mode 100644 index 000000000..ff7616656 --- /dev/null +++ b/tests/run-coverage/yield.rs @@ -0,0 +1,37 @@ +#![feature(generators, generator_trait)] +#![allow(unused_assignments)] + +use std::ops::{Generator, GeneratorState}; +use std::pin::Pin; + +fn main() { + let mut generator = || { + yield 1; + return "foo" + }; + + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(1) => {} + _ => panic!("unexpected value from resume"), + } + match Pin::new(&mut generator).resume(()) { + GeneratorState::Complete("foo") => {} + _ => panic!("unexpected value from resume"), + } + + let mut generator = || { + yield 1; + yield 2; + yield 3; + return "foo" + }; + + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(1) => {} + _ => panic!("unexpected value from resume"), + } + match Pin::new(&mut generator).resume(()) { + GeneratorState::Yielded(2) => {} + _ => panic!("unexpected value from resume"), + } +} |