-
Notifications
You must be signed in to change notification settings - Fork 726
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
attributes: add fake return to improve span on type error (#2270)
## Motivation Return type errors on instrumented async functions are a bit vague, since the type error originates within the macro itself due to the indirection of additional `async {}` blocks generated in the proc-macro (and due to the way that inference propagates around in Rust). This leads to a pretty difficult to understand error. For example: ```rust #[instrument] async fn foo() -> String { "" } ``` results in... ``` error[E0308]: mismatched types --> src/main.rs:1:1 | 1 | #[tracing::instrument] | ^^^^^^^^^^^^^^^^^^^^^^- help: try using a conversion method: `.to_string()` | | | expected struct `String`, found `&str` ``` ## Solution Installs a fake `return` statement as the first thing that happens in the auto-generated block of an instrumented async function. This causes the coercion machinery within rustc to infer the right return type (matching the the outer function) eagerly, as opposed to after the `async {}` block has been type-checked. This will cause us to both be able to point out the return type span correctly, and properly suggest fixes on the expressions that cause the type mismatch. After this change, the example code above compiles to: ``` error[E0308]: mismatched types --> src/main.rs:3:5 | 3 | "" | ^^- help: try using a conversion method: `.to_string()` | | | expected struct `String`, found `&str` | note: return type inferred to be `String` here --> src/main.rs:2:20 | 2 | async fn foo() -> String { | ^^^^^^ ```
- Loading branch information
1 parent
6ce8cfd
commit 39a2068
Showing
5 changed files
with
213 additions
and
20 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
// Only test on nightly, since UI tests are bound to change over time | ||
#[rustversion::stable] | ||
#[test] | ||
fn async_instrument() { | ||
let t = trybuild::TestCases::new(); | ||
t.compile_fail("tests/ui/async_instrument.rs"); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
#![allow(unreachable_code)] | ||
|
||
#[tracing::instrument] | ||
async fn unit() { | ||
"" | ||
} | ||
|
||
#[tracing::instrument] | ||
async fn simple_mismatch() -> String { | ||
"" | ||
} | ||
|
||
// FIXME: this span is still pretty poor | ||
#[tracing::instrument] | ||
async fn opaque_unsatisfied() -> impl std::fmt::Display { | ||
("",) | ||
} | ||
|
||
struct Wrapper<T>(T); | ||
|
||
#[tracing::instrument] | ||
async fn mismatch_with_opaque() -> Wrapper<impl std::fmt::Display> { | ||
"" | ||
} | ||
|
||
#[tracing::instrument] | ||
async fn early_return_unit() { | ||
if true { | ||
return ""; | ||
} | ||
} | ||
|
||
#[tracing::instrument] | ||
async fn early_return() -> String { | ||
if true { | ||
return ""; | ||
} | ||
String::new() | ||
} | ||
|
||
#[tracing::instrument] | ||
async fn extra_semicolon() -> i32 { | ||
1; | ||
} | ||
|
||
fn main() {} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,89 @@ | ||
error[E0308]: mismatched types | ||
--> tests/ui/async_instrument.rs:5:5 | ||
| | ||
5 | "" | ||
| ^^ expected `()`, found `&str` | ||
| | ||
note: return type inferred to be `()` here | ||
--> tests/ui/async_instrument.rs:4:10 | ||
| | ||
4 | async fn unit() { | ||
| ^^^^ | ||
|
||
error[E0308]: mismatched types | ||
--> tests/ui/async_instrument.rs:10:5 | ||
| | ||
10 | "" | ||
| ^^- help: try using a conversion method: `.to_string()` | ||
| | | ||
| expected struct `String`, found `&str` | ||
| | ||
note: return type inferred to be `String` here | ||
--> tests/ui/async_instrument.rs:9:31 | ||
| | ||
9 | async fn simple_mismatch() -> String { | ||
| ^^^^^^ | ||
|
||
error[E0277]: `(&str,)` doesn't implement `std::fmt::Display` | ||
--> tests/ui/async_instrument.rs:14:1 | ||
| | ||
14 | #[tracing::instrument] | ||
| ^^^^^^^^^^^^^^^^^^^^^^ `(&str,)` cannot be formatted with the default formatter | ||
| | ||
= help: the trait `std::fmt::Display` is not implemented for `(&str,)` | ||
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead | ||
= note: this error originates in the attribute macro `tracing::instrument` (in Nightly builds, run with -Z macro-backtrace for more info) | ||
|
||
error[E0308]: mismatched types | ||
--> tests/ui/async_instrument.rs:23:5 | ||
| | ||
23 | "" | ||
| ^^ expected struct `Wrapper`, found `&str` | ||
| | ||
= note: expected struct `Wrapper<_>` | ||
found reference `&'static str` | ||
note: return type inferred to be `Wrapper<_>` here | ||
--> tests/ui/async_instrument.rs:22:36 | ||
| | ||
22 | async fn mismatch_with_opaque() -> Wrapper<impl std::fmt::Display> { | ||
| ^^^^^^^ | ||
help: try wrapping the expression in `Wrapper` | ||
| | ||
23 | Wrapper("") | ||
| ++++++++ + | ||
|
||
error[E0308]: mismatched types | ||
--> tests/ui/async_instrument.rs:29:16 | ||
| | ||
29 | return ""; | ||
| ^^ expected `()`, found `&str` | ||
| | ||
note: return type inferred to be `()` here | ||
--> tests/ui/async_instrument.rs:27:10 | ||
| | ||
27 | async fn early_return_unit() { | ||
| ^^^^^^^^^^^^^^^^^ | ||
|
||
error[E0308]: mismatched types | ||
--> tests/ui/async_instrument.rs:36:16 | ||
| | ||
36 | return ""; | ||
| ^^- help: try using a conversion method: `.to_string()` | ||
| | | ||
| expected struct `String`, found `&str` | ||
| | ||
note: return type inferred to be `String` here | ||
--> tests/ui/async_instrument.rs:34:28 | ||
| | ||
34 | async fn early_return() -> String { | ||
| ^^^^^^ | ||
|
||
error[E0308]: mismatched types | ||
--> tests/ui/async_instrument.rs:42:35 | ||
| | ||
42 | async fn extra_semicolon() -> i32 { | ||
| ___________________________________^ | ||
43 | | 1; | ||
| | - help: remove this semicolon | ||
44 | | } | ||
| |_^ expected `i32`, found `()` |