Skip to content

Commit

Permalink
macros: improve diagnostics on type mismatch (#3766)
Browse files Browse the repository at this point in the history
  • Loading branch information
taiki-e authored May 9, 2021
1 parent 05c3cf3 commit f9ce18a
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 7 deletions.
8 changes: 8 additions & 0 deletions tests-build/tests/fail/macros_dead_code.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#![deny(dead_code)]

use tests_build::tokio;

#[tokio::main]
async fn f() {}

fn main() {}
11 changes: 11 additions & 0 deletions tests-build/tests/fail/macros_dead_code.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
error: function is never used: `f`
--> $DIR/macros_dead_code.rs:6:10
|
6 | async fn f() {}
| ^
|
note: the lint level is defined here
--> $DIR/macros_dead_code.rs:1:9
|
1 | #![deny(dead_code)]
| ^^^^^^^^^
32 changes: 32 additions & 0 deletions tests-build/tests/fail/macros_type_mismatch.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
use tests_build::tokio;

#[tokio::main]
async fn missing_semicolon_or_return_type() {
Ok(())
}

#[tokio::main]
async fn missing_return_type() {
/* TODO(taiki-e): one of help messages still wrong
help: consider using a semicolon here
|
16 | return Ok(());;
|
*/
return Ok(());
}

#[tokio::main]
async fn extra_semicolon() -> Result<(), ()> {
/* TODO(taiki-e): help message still wrong
help: try using a variant of the expected enum
|
29 | Ok(Ok(());)
|
29 | Err(Ok(());)
|
*/
Ok(());
}

fn main() {}
51 changes: 51 additions & 0 deletions tests-build/tests/fail/macros_type_mismatch.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:5:5
|
5 | Ok(())
| ^^^^^^ expected `()`, found enum `Result`
|
= note: expected unit type `()`
found enum `Result<(), _>`
help: consider using a semicolon here
|
5 | Ok(());
| ^
help: try adding a return type
|
4 | async fn missing_semicolon_or_return_type() -> Result<(), _> {
| ^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:16:5
|
16 | return Ok(());
| ^^^^^^^^^^^^^^ expected `()`, found enum `Result`
|
= note: expected unit type `()`
found enum `Result<(), _>`
help: consider using a semicolon here
|
16 | return Ok(());;
| ^
help: try adding a return type
|
9 | async fn missing_return_type() -> Result<(), _> {
| ^^^^^^^^^^^^^^^^

error[E0308]: mismatched types
--> $DIR/macros_type_mismatch.rs:29:5
|
20 | async fn extra_semicolon() -> Result<(), ()> {
| -------------- expected `Result<(), ()>` because of return type
...
29 | Ok(());
| ^^^^^^^ expected enum `Result`, found `()`
|
= note: expected enum `Result<(), ()>`
found unit type `()`
help: try using a variant of the expected enum
|
29 | Ok(Ok(());)
|
29 | Err(Ok(());)
|
6 changes: 6 additions & 0 deletions tests-build/tests/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ fn compile_fail_full() {
#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_invalid_input.rs");

#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_dead_code.rs");

#[cfg(feature = "full")]
t.compile_fail("tests/fail/macros_type_mismatch.rs");

#[cfg(all(feature = "rt", not(feature = "full")))]
t.compile_fail("tests/fail/macros_core_no_default.rs");

Expand Down
2 changes: 1 addition & 1 deletion tokio-macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ proc-macro = true
[dependencies]
proc-macro2 = "1.0.7"
quote = "1"
syn = { version = "1.0.3", features = ["full"] }
syn = { version = "1.0.56", features = ["full"] }

[dev-dependencies]
tokio = { version = "1.0.0", path = "../tokio", features = ["full"] }
Expand Down
31 changes: 26 additions & 5 deletions tokio-macros/src/entry.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use proc_macro::TokenStream;
use proc_macro2::Span;
use quote::quote;
use quote::{quote, quote_spanned, ToTokens};

#[derive(Clone, Copy, PartialEq)]
enum RuntimeFlavor {
Expand Down Expand Up @@ -278,11 +278,29 @@ fn parse_knobs(

let config = config.build()?;

// If type mismatch occurs, the current rustc points to the last statement.
let (last_stmt_start_span, last_stmt_end_span) = {
let mut last_stmt = input
.block
.stmts
.last()
.map(ToTokens::into_token_stream)
.unwrap_or_default()
.into_iter();
// `Span` on stable Rust has a limitation that only points to the first
// token, not the whole tokens. We can work around this limitation by
// using the first/last span of the tokens like
// `syn::Error::new_spanned` does.
let start = last_stmt.next().map_or_else(Span::call_site, |t| t.span());
let end = last_stmt.last().map_or(start, |t| t.span());
(start, end)
};

let mut rt = match config.flavor {
RuntimeFlavor::CurrentThread => quote! {
RuntimeFlavor::CurrentThread => quote_spanned! {last_stmt_start_span=>
tokio::runtime::Builder::new_current_thread()
},
RuntimeFlavor::Threaded => quote! {
RuntimeFlavor::Threaded => quote_spanned! {last_stmt_start_span=>
tokio::runtime::Builder::new_multi_thread()
},
};
Expand All @@ -302,15 +320,18 @@ fn parse_knobs(
};

let body = &input.block;
input.block = syn::parse_quote! {
let brace_token = input.block.brace_token;
input.block = syn::parse2(quote_spanned! {last_stmt_end_span=>
{
#rt
.enable_all()
.build()
.unwrap()
.block_on(async #body)
}
};
})
.unwrap();
input.block.brace_token = brace_token;

let result = quote! {
#header
Expand Down
15 changes: 14 additions & 1 deletion tokio/tests/macros_test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,20 @@ async fn test_macro_is_resilient_to_shadowing() {
// https://github.com/tokio-rs/tokio/issues/3403
#[rustfmt::skip] // this `rustfmt::skip` is necessary because unused_braces does not warn if the block contains newline.
#[tokio::main]
async fn unused_braces_main() { println!("hello") }
pub async fn unused_braces_main() { println!("hello") }
#[rustfmt::skip] // this `rustfmt::skip` is necessary because unused_braces does not warn if the block contains newline.
#[tokio::test]
async fn unused_braces_test() { assert_eq!(1 + 1, 2) }

// https://github.com/tokio-rs/tokio/pull/3766#issuecomment-835508651
#[std::prelude::v1::test]
fn trait_method() {
trait A {
fn f(self);
}
impl A for () {
#[tokio::main]
async fn f(self) {}
}
().f()
}

1 comment on commit f9ce18a

@github-actions
Copy link

Choose a reason for hiding this comment

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

⚠️ Performance Alert ⚠️

Possible performance regression was detected for benchmark 'sync_mpsc'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 2.

Benchmark suite Current: f9ce18a Previous: 05c3cf3 Ratio
send_large 59165 ns/iter (± 17621) 29481 ns/iter (± 1234) 2.01

This comment was automatically generated by workflow using github-action-benchmark.

CC: @tokio-rs/maintainers

Please sign in to comment.