Skip to content

coverage of async function bodies should match non-async #84323

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 3 commits into from
Apr 20, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions compiler/rustc_mir/src/transform/coverage/spans.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use rustc_middle::mir::{
use rustc_middle::ty::TyCtxt;

use rustc_span::source_map::original_sp;
use rustc_span::{BytePos, Span, SyntaxContext};
use rustc_span::{BytePos, Span};

use std::cmp::Ordering;

Expand Down Expand Up @@ -246,7 +246,7 @@ impl<'a, 'tcx> CoverageSpans<'a, 'tcx> {
) -> Vec<CoverageSpan> {
let mut coverage_spans = CoverageSpans {
mir_body,
fn_sig_span,
fn_sig_span: fn_sig_source_span(fn_sig_span, body_span),
body_span,
basic_coverage_blocks,
sorted_spans_iter: None,
Expand Down Expand Up @@ -731,8 +731,13 @@ pub(super) fn filtered_terminator_span(
}
}

#[inline]
fn fn_sig_source_span(fn_sig_span: Span, body_span: Span) -> Span {
original_sp(fn_sig_span, body_span).with_ctxt(body_span.ctxt())
}

#[inline]
fn function_source_span(span: Span, body_span: Span) -> Span {
let span = original_sp(span, body_span).with_ctxt(SyntaxContext::root());
let span = original_sp(span, body_span).with_ctxt(body_span.ctxt());
if body_span.contains(span) { span } else { body_span }
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
1| |#![allow(unused_assignments, dead_code)]
2| |
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
4| |
5| 1|async fn c(x: u8) -> u8 {
6| 1| if x == 8 {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
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| }
15| 1|}
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| 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| |// 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| 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::<core::future::from_generator::GenFuture<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::<core::future::from_generator::GenFuture<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| |}

2 changes: 1 addition & 1 deletion src/test/run-make-fulldeps/coverage/async.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#![allow(unused_assignments, dead_code)]

// compile-flags: --edition=2018 -C opt-level=1 # fix in rustc_mir/monomorphize/partitioning/mod.rs
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This comment is no longer relevant.

// compile-flags: --edition=2018 -C opt-level=1

async fn c(x: u8) -> u8 {
if x == 8 {
Expand Down
69 changes: 69 additions & 0 deletions src/test/run-make-fulldeps/coverage/async2.rs
Original file line number Diff line number Diff line change
@@ -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");
}
}

// FIXME(#83985): The auto-generated closure in an async function is failing to include
// the println!() and `let` assignment lines in the coverage code region(s), as it does in the
// non-async function above, unless the `println!()` is inside a covered block.
async fn async_func() {
println!("async_func was covered");
let b = true;
if b {
println!("async_func println in block");
}
}

// FIXME(#83985): As above, this async function only has the `println!()` macro call, which is not
// showing coverage, so the entire async closure _appears_ uncovered; but this is not exactly true.
// It's only certain kinds of lines and/or their context that results in missing coverage.
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;
}
}
}
}