Reland - Report coverage 0 of dead blocks
Fixes: #84018 With `-Z instrument-coverage`, coverage reporting of dead blocks (for example, blocks dropped because a conditional branch is dropped, based on const evaluation) is now supported. Note, this PR relands an earlier, reverted PR that failed when compiling generators. The prior issues with generators has been resolved and a new test was added to prevent future regressions. Check out the resulting changes to test coverage of dead blocks in the test coverage reports in this PR.
This commit is contained in:
parent
7f9ab0300c
commit
f4f76e60b3
24 changed files with 216 additions and 65 deletions
|
|
@ -12,6 +12,7 @@
|
|||
12| 1| if b {
|
||||
13| 1| println!("non_async_func println in block");
|
||||
14| 1| }
|
||||
^0
|
||||
15| 1|}
|
||||
16| |
|
||||
17| |
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
5| 1| if true {
|
||||
6| 1| countdown = 10;
|
||||
7| 1| }
|
||||
^0
|
||||
8| |
|
||||
9| | const B: u32 = 100;
|
||||
10| 1| let x = if countdown > 7 {
|
||||
|
|
@ -24,6 +25,7 @@
|
|||
24| 1| if true {
|
||||
25| 1| countdown = 10;
|
||||
26| 1| }
|
||||
^0
|
||||
27| |
|
||||
28| 1| if countdown > 7 {
|
||||
29| 1| countdown -= 4;
|
||||
|
|
@ -42,6 +44,7 @@
|
|||
41| 1| if true {
|
||||
42| 1| countdown = 10;
|
||||
43| 1| }
|
||||
^0
|
||||
44| |
|
||||
45| 1| if countdown > 7 {
|
||||
46| 1| countdown -= 4;
|
||||
|
|
@ -54,13 +57,14 @@
|
|||
53| | } else {
|
||||
54| 0| return;
|
||||
55| | }
|
||||
56| | } // Note: closing brace shows uncovered (vs. `0` for implicit else) because condition literal
|
||||
57| | // `true` was const-evaluated. The compiler knows the `if` block will be executed.
|
||||
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
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
8| 1|//! assert_eq!(1, 1);
|
||||
9| |//! } else {
|
||||
10| |//! // this is not!
|
||||
11| |//! assert_eq!(1, 2);
|
||||
11| 0|//! assert_eq!(1, 2);
|
||||
12| |//! }
|
||||
13| 1|//! ```
|
||||
14| |//!
|
||||
|
|
@ -84,7 +84,7 @@
|
|||
74| 1| if true {
|
||||
75| 1| assert_eq!(1, 1);
|
||||
76| | } else {
|
||||
77| | assert_eq!(1, 2);
|
||||
77| 0| assert_eq!(1, 2);
|
||||
78| | }
|
||||
79| 1|}
|
||||
80| |
|
||||
|
|
|
|||
|
|
@ -19,11 +19,11 @@
|
|||
19| 1| if true {
|
||||
20| 1| println!("Exiting with error...");
|
||||
21| 1| return Err(1);
|
||||
22| | }
|
||||
23| |
|
||||
24| | let _ = Firework { strength: 1000 };
|
||||
25| |
|
||||
26| | Ok(())
|
||||
22| 0| }
|
||||
23| 0|
|
||||
24| 0| let _ = Firework { strength: 1000 };
|
||||
25| 0|
|
||||
26| 0| Ok(())
|
||||
27| 1|}
|
||||
28| |
|
||||
29| |// Expected program output:
|
||||
|
|
|
|||
|
|
@ -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|}
|
||||
|
||||
|
|
@ -52,15 +52,15 @@
|
|||
30| 1| if true {
|
||||
31| 1| println!("Exiting with error...");
|
||||
32| 1| return Err(1);
|
||||
33| | } // The remaining lines below have no coverage because `if true` (with the constant literal
|
||||
34| | // `true`) is guaranteed to execute the `then` block, which is also guaranteed to `return`.
|
||||
35| | // Thankfully, in the normal case, conditions are not guaranteed ahead of time, and as shown
|
||||
36| | // in other tests, the lines below would have coverage (which would show they had `0`
|
||||
37| | // executions, assuming the condition still evaluated to `true`).
|
||||
38| |
|
||||
39| | let _ = Firework { strength: 1000 };
|
||||
40| |
|
||||
41| | Ok(())
|
||||
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:
|
||||
|
|
|
|||
|
|
@ -9,23 +9,23 @@
|
|||
9| 1| fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||
10| 1| if true {
|
||||
11| 1| if false {
|
||||
12| | while true {
|
||||
13| | }
|
||||
12| 0| while true {
|
||||
13| 0| }
|
||||
14| 1| }
|
||||
15| 1| write!(f, "error")?;
|
||||
^0
|
||||
16| | } else {
|
||||
17| | }
|
||||
15| 1| write!(f, "cool")?;
|
||||
^0
|
||||
16| 0| } else {
|
||||
17| 0| }
|
||||
18| |
|
||||
19| 10| for i in 0..10 {
|
||||
20| 10| if true {
|
||||
21| 10| if false {
|
||||
22| | while true {}
|
||||
22| 0| while true {}
|
||||
23| 10| }
|
||||
24| 10| write!(f, "error")?;
|
||||
^0
|
||||
25| | } else {
|
||||
26| | }
|
||||
24| 10| write!(f, "cool")?;
|
||||
^0
|
||||
25| 0| } else {
|
||||
26| 0| }
|
||||
27| | }
|
||||
28| 1| Ok(())
|
||||
29| 1| }
|
||||
|
|
@ -36,21 +36,21 @@
|
|||
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| | } else {
|
||||
37| 0| } else {
|
||||
38| 1| if false {
|
||||
39| | while true {}
|
||||
39| 0| while true {}
|
||||
40| 1| }
|
||||
41| 1| write!(f, "error")?;
|
||||
^0
|
||||
41| 1| write!(f, "cool")?;
|
||||
^0
|
||||
42| | }
|
||||
43| 10| for i in 0..10 {
|
||||
44| 10| if false {
|
||||
45| | } else {
|
||||
45| 0| } else {
|
||||
46| 10| if false {
|
||||
47| | while true {}
|
||||
47| 0| while true {}
|
||||
48| 10| }
|
||||
49| 10| write!(f, "error")?;
|
||||
^0
|
||||
49| 10| write!(f, "cool")?;
|
||||
^0
|
||||
50| | }
|
||||
51| | }
|
||||
52| 1| Ok(())
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
1| 1|fn main() {
|
||||
2| 1| if false {
|
||||
3| | loop {}
|
||||
3| 0| loop {}
|
||||
4| 1| }
|
||||
5| 1|}
|
||||
|
||||
|
|
|
|||
|
|
@ -53,8 +53,8 @@ fn main() {
|
|||
} else {
|
||||
return;
|
||||
}
|
||||
} // Note: closing brace shows uncovered (vs. `0` for implicit else) because condition literal
|
||||
// `true` was const-evaluated. The compiler knows the `if` block will be executed.
|
||||
}
|
||||
|
||||
|
||||
let mut countdown = 0;
|
||||
if true {
|
||||
|
|
|
|||
30
src/test/run-make-fulldeps/coverage/generator.rs
Normal file
30
src/test/run-make-fulldeps/coverage/generator.rs
Normal file
|
|
@ -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"),
|
||||
}
|
||||
}
|
||||
|
|
@ -30,11 +30,11 @@ fn main() -> Result<(),u8> {
|
|||
if true {
|
||||
println!("Exiting with error...");
|
||||
return Err(1);
|
||||
} // The remaining lines below have no coverage because `if true` (with the constant literal
|
||||
// `true`) is guaranteed to execute the `then` block, which is also guaranteed to `return`.
|
||||
// Thankfully, in the normal case, conditions are not guaranteed ahead of time, and as shown
|
||||
// in other tests, the lines below would have coverage (which would show they had `0`
|
||||
// executions, assuming the condition still evaluated to `true`).
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
let _ = Firework { strength: 1000 };
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ impl std::fmt::Debug for DebugTest {
|
|||
while true {
|
||||
}
|
||||
}
|
||||
write!(f, "error")?;
|
||||
write!(f, "cool")?;
|
||||
} else {
|
||||
}
|
||||
|
||||
|
|
@ -21,7 +21,7 @@ impl std::fmt::Debug for DebugTest {
|
|||
if false {
|
||||
while true {}
|
||||
}
|
||||
write!(f, "error")?;
|
||||
write!(f, "cool")?;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
|
|
@ -38,7 +38,7 @@ impl std::fmt::Display for DisplayTest {
|
|||
if false {
|
||||
while true {}
|
||||
}
|
||||
write!(f, "error")?;
|
||||
write!(f, "cool")?;
|
||||
}
|
||||
for i in 0..10 {
|
||||
if false {
|
||||
|
|
@ -46,7 +46,7 @@ impl std::fmt::Display for DisplayTest {
|
|||
if false {
|
||||
while true {}
|
||||
}
|
||||
write!(f, "error")?;
|
||||
write!(f, "cool")?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue