Skip to content

Commit 6af1e63

Browse files
committed
Auto merge of #84323 - richkadel:uncovered-functions, r=tmandry
coverage of async function bodies should match non-async This fixes some missing coverage within async function bodies. Commit 1 demonstrates the problem in the fixed issue, and commit 2 corrects it. Fixes: #83985
2 parents a70fbf6 + 5d8d67f commit 6af1e63

File tree

5 files changed

+194
-5
lines changed

5 files changed

+194
-5
lines changed

compiler/rustc_mir/src/transform/coverage/spans.rs

+8-3
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use rustc_middle::mir::{
1111
use rustc_middle::ty::TyCtxt;
1212

1313
use rustc_span::source_map::original_sp;
14-
use rustc_span::{BytePos, Span, SyntaxContext};
14+
use rustc_span::{BytePos, Span};
1515

1616
use std::cmp::Ordering;
1717

@@ -246,7 +246,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
246246
) -> Vec<CoverageSpan> {
247247
let mut coverage_spans = CoverageSpans {
248248
mir_body,
249-
fn_sig_span,
249+
fn_sig_span: fn_sig_source_span(fn_sig_span, body_span),
250250
body_span,
251251
basic_coverage_blocks,
252252
sorted_spans_iter: None,
@@ -731,8 +731,13 @@ pub(super) fn filtered_terminator_span(
731731
}
732732
}
733733

734+
#[inline]
735+
fn fn_sig_source_span(fn_sig_span: Span, body_span: Span) -> Span {
736+
original_sp(fn_sig_span, body_span).with_ctxt(body_span.ctxt())
737+
}
738+
734739
#[inline]
735740
fn function_source_span(span: Span, body_span: Span) -> Span {
736-
let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root());
741+
let span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
737742
if body_span.contains(span) { span } else { body_span }
738743
}

src/test/run-make-fulldeps/coverage-reports/expected_show_coverage.async.txt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
1| |#![allow(unused_assignments, dead_code)]
22
2| |
3-
3| |// compile-flags: --edition=2018 -C opt-level=1 # fix in rustc_mir/monomorphize/partitioning/mod.rs
3+
3| |// compile-flags: --edition=2018 -C opt-level=1
44
4| |
55
5| 1|async fn c(x: u8) -> u8 {
66
6| 1| if x == 8 {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
1| |// compile-flags: --edition=2018
2+
2| |
3+
3| |use core::{
4+
4| | future::Future,
5+
5| | marker::Send,
6+
6| | pin::Pin,
7+
7| |};
8+
8| |
9+
9| 1|fn non_async_func() {
10+
10| 1| println!("non_async_func was covered");
11+
11| 1| let b = true;
12+
12| 1| if b {
13+
13| 1| println!("non_async_func println in block");
14+
14| 1| }
15+
15| 1|}
16+
16| |
17+
17| |// FIXME(#83985): The auto-generated closure in an async function is failing to include
18+
18| |// the println!() and `let` assignment lines in the coverage code region(s), as it does in the
19+
19| |// non-async function above, unless the `println!()` is inside a covered block.
20+
20| 1|async fn async_func() {
21+
21| 1| println!("async_func was covered");
22+
22| 1| let b = true;
23+
23| 1| if b {
24+
24| 1| println!("async_func println in block");
25+
25| 1| }
26+
^0
27+
26| 1|}
28+
27| |
29+
28| |// FIXME(#83985): As above, this async function only has the `println!()` macro call, which is not
30+
29| |// showing coverage, so the entire async closure _appears_ uncovered; but this is not exactly true.
31+
30| |// It's only certain kinds of lines and/or their context that results in missing coverage.
32+
31| 1|async fn async_func_just_println() {
33+
32| 1| println!("async_func_just_println was covered");
34+
33| 1|}
35+
34| |
36+
35| 1|fn main() {
37+
36| 1| println!("codecovsample::main");
38+
37| 1|
39+
38| 1| non_async_func();
40+
39| 1|
41+
40| 1| executor::block_on(async_func());
42+
41| 1| executor::block_on(async_func_just_println());
43+
42| 1|}
44+
43| |
45+
44| |mod executor {
46+
45| | use core::{
47+
46| | future::Future,
48+
47| | pin::Pin,
49+
48| | task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
50+
49| | };
51+
50| |
52+
51| 2| pub fn block_on<F: Future>(mut future: F) -> F::Output {
53+
52| 2| let mut future = unsafe { Pin::new_unchecked(&mut future) };
54+
53| 2| use std::hint::unreachable_unchecked;
55+
54| 2| static VTABLE: RawWakerVTable = RawWakerVTable::new(
56+
55| 2| |_| unsafe { unreachable_unchecked() }, // clone
57+
^0
58+
56| 2| |_| unsafe { unreachable_unchecked() }, // wake
59+
^0
60+
57| 2| |_| unsafe { unreachable_unchecked() }, // wake_by_ref
61+
^0
62+
58| 2| |_| (),
63+
59| 2| );
64+
60| 2| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
65+
61| 2| let mut context = Context::from_waker(&waker);
66+
62| |
67+
63| | loop {
68+
64| 2| if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
69+
65| 2| break val;
70+
66| 0| }
71+
67| | }
72+
68| 2| }
73+
------------------
74+
| async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func::{closure#0}>>:
75+
| 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output {
76+
| 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) };
77+
| 53| 1| use std::hint::unreachable_unchecked;
78+
| 54| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new(
79+
| 55| 1| |_| unsafe { unreachable_unchecked() }, // clone
80+
| 56| 1| |_| unsafe { unreachable_unchecked() }, // wake
81+
| 57| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref
82+
| 58| 1| |_| (),
83+
| 59| 1| );
84+
| 60| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
85+
| 61| 1| let mut context = Context::from_waker(&waker);
86+
| 62| |
87+
| 63| | loop {
88+
| 64| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
89+
| 65| 1| break val;
90+
| 66| 0| }
91+
| 67| | }
92+
| 68| 1| }
93+
------------------
94+
| async2::executor::block_on::<core::future::from_generator::GenFuture<async2::async_func_just_println::{closure#0}>>:
95+
| 51| 1| pub fn block_on<F: Future>(mut future: F) -> F::Output {
96+
| 52| 1| let mut future = unsafe { Pin::new_unchecked(&mut future) };
97+
| 53| 1| use std::hint::unreachable_unchecked;
98+
| 54| 1| static VTABLE: RawWakerVTable = RawWakerVTable::new(
99+
| 55| 1| |_| unsafe { unreachable_unchecked() }, // clone
100+
| 56| 1| |_| unsafe { unreachable_unchecked() }, // wake
101+
| 57| 1| |_| unsafe { unreachable_unchecked() }, // wake_by_ref
102+
| 58| 1| |_| (),
103+
| 59| 1| );
104+
| 60| 1| let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
105+
| 61| 1| let mut context = Context::from_waker(&waker);
106+
| 62| |
107+
| 63| | loop {
108+
| 64| 1| if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
109+
| 65| 1| break val;
110+
| 66| 0| }
111+
| 67| | }
112+
| 68| 1| }
113+
------------------
114+
69| |}
115+

src/test/run-make-fulldeps/coverage/async.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#![allow(unused_assignments, dead_code)]
22

3-
// compile-flags: --edition=2018 -C opt-level=1 # fix in rustc_mir/monomorphize/partitioning/mod.rs
3+
// compile-flags: --edition=2018 -C opt-level=1
44

55
async fn c(x: u8) -> u8 {
66
if x == 8 {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// compile-flags: --edition=2018
2+
3+
use core::{
4+
future::Future,
5+
marker::Send,
6+
pin::Pin,
7+
};
8+
9+
fn non_async_func() {
10+
println!("non_async_func was covered");
11+
let b = true;
12+
if b {
13+
println!("non_async_func println in block");
14+
}
15+
}
16+
17+
// FIXME(#83985): The auto-generated closure in an async function is failing to include
18+
// the println!() and `let` assignment lines in the coverage code region(s), as it does in the
19+
// non-async function above, unless the `println!()` is inside a covered block.
20+
async fn async_func() {
21+
println!("async_func was covered");
22+
let b = true;
23+
if b {
24+
println!("async_func println in block");
25+
}
26+
}
27+
28+
// FIXME(#83985): As above, this async function only has the `println!()` macro call, which is not
29+
// showing coverage, so the entire async closure _appears_ uncovered; but this is not exactly true.
30+
// It's only certain kinds of lines and/or their context that results in missing coverage.
31+
async fn async_func_just_println() {
32+
println!("async_func_just_println was covered");
33+
}
34+
35+
fn main() {
36+
println!("codecovsample::main");
37+
38+
non_async_func();
39+
40+
executor::block_on(async_func());
41+
executor::block_on(async_func_just_println());
42+
}
43+
44+
mod executor {
45+
use core::{
46+
future::Future,
47+
pin::Pin,
48+
task::{Context, Poll, RawWaker, RawWakerVTable, Waker},
49+
};
50+
51+
pub fn block_on<F: Future>(mut future: F) -> F::Output {
52+
let mut future = unsafe { Pin::new_unchecked(&mut future) };
53+
use std::hint::unreachable_unchecked;
54+
static VTABLE: RawWakerVTable = RawWakerVTable::new(
55+
|_| unsafe { unreachable_unchecked() }, // clone
56+
|_| unsafe { unreachable_unchecked() }, // wake
57+
|_| unsafe { unreachable_unchecked() }, // wake_by_ref
58+
|_| (),
59+
);
60+
let waker = unsafe { Waker::from_raw(RawWaker::new(core::ptr::null(), &VTABLE)) };
61+
let mut context = Context::from_waker(&waker);
62+
63+
loop {
64+
if let Poll::Ready(val) = future.as_mut().poll(&mut context) {
65+
break val;
66+
}
67+
}
68+
}
69+
}

0 commit comments

Comments
 (0)