From d763faf92198e3f0c851388c71d2bb8a2f04afd9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 May 2019 19:47:18 -0700 Subject: [PATCH 01/10] Parse alternative incorrect uses of await and recover --- src/librustc/hir/lowering.rs | 19 +- src/libsyntax/parse/parser.rs | 113 +++++++++- ...018-edition-error-in-non-macro-position.rs | 4 +- ...edition-error-in-non-macro-position.stderr | 10 +- .../ui/await-keyword/2018-edition-error.rs | 4 +- .../await-keyword/2018-edition-error.stderr | 10 +- .../incorrect-syntax-suggestions.rs | 93 +++++++++ .../incorrect-syntax-suggestions.stderr | 196 ++++++++++++++++++ .../await-keyword/post_expansion_error.stderr | 4 +- src/test/ui/issues/issue-51719.rs | 2 + src/test/ui/issues/issue-51719.stderr | 5 +- src/test/ui/issues/issue-51751.stderr | 5 +- 12 files changed, 428 insertions(+), 37 deletions(-) create mode 100644 src/test/ui/await-keyword/incorrect-syntax-suggestions.rs create mode 100644 src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index dd0d13d8f5a6a..12ccc79e4ab41 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -97,6 +97,10 @@ pub struct LoweringContext<'a> { is_generator: bool, is_async_body: bool, + /// Used to get the current `fn`'s def span to point to when using `await` + /// outside of an `async fn`. + current_item_id: Option, + catch_scopes: Vec, loop_scopes: Vec, is_in_loop_condition: bool, @@ -250,6 +254,7 @@ pub fn lower_crate( node_id_to_hir_id: IndexVec::new(), is_generator: false, is_async_body: false, + current_item_id: None, is_in_trait_impl: false, lifetimes_to_define: Vec::new(), is_collecting_in_band_lifetimes: false, @@ -3115,6 +3120,7 @@ impl<'a> LoweringContext<'a> { } ItemKind::Fn(ref decl, ref header, ref generics, ref body) => { let fn_def_id = self.resolver.definitions().local_def_id(id); + let hir_id = self.lower_node_id(id); self.with_new_scopes(|this| { let mut lower_fn = |decl: &FnDecl| { // Note: we don't need to change the return type from `T` to @@ -3153,6 +3159,7 @@ impl<'a> LoweringContext<'a> { } else { lower_fn(decl) }; + this.current_item_id = Some(hir_id); hir::ItemKind::Fn( fn_decl, @@ -5551,13 +5558,21 @@ impl<'a> LoweringContext<'a> { // } // } if !self.is_async_body { - span_err!( + let mut err = struct_span_err!( self.sess, await_span, E0728, "`await` is only allowed inside `async` functions and blocks" ); - self.sess.abort_if_errors(); + err.span_label(await_span, "only allowed inside `async` functions and blocks"); + if let Some(item_id) = self.current_item_id { + err.span_label( + self.sess.source_map().def_span(self.items[&item_id].span), + "this function is not `async`", + ); + } + err.emit(); + return hir::ExprKind::Err; } let span = self.sess.source_map().mark_span_with_reason( CompilerDesugaringKind::Await, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 99e8db9d8e6d2..55e13ef00dcc9 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2629,14 +2629,94 @@ impl<'a> Parser<'a> { db.note("variable declaration using `let` is a statement"); return Err(db); } else if self.span.rust_2018() && self.eat_keyword(keywords::Await) { - // FIXME: remove this branch when `await!` is no longer supported - // https://github.com/rust-lang/rust/issues/60610 - self.expect(&token::Not)?; - self.expect(&token::OpenDelim(token::Paren))?; - let expr = self.parse_expr()?; - self.expect(&token::CloseDelim(token::Paren))?; - hi = self.prev_span; - ex = ExprKind::Await(ast::AwaitOrigin::MacroLike, expr); + let await_sp = self.prev_span; + match self.token { + token::Not => { + // FIXME: make this an error when `await!` is no longer supported + // https://github.com/rust-lang/rust/issues/60610 + self.expect(&token::Not)?; + self.expect(&token::OpenDelim(token::Paren))?; + let expr = self.parse_expr().map_err(|mut err| { + err.span_label( + await_sp, + "while parsing this await macro call", + ); + err + })?; + self.expect(&token::CloseDelim(token::Paren))?; + ex = ExprKind::Await(ast::AwaitOrigin::MacroLike, expr); + } + token::Question => { + // Handle `await? ` + self.bump(); // `?` + let expr = self.parse_expr().map_err(|mut err| { + err.span_label( + await_sp, + "while parsing this incorrect await statement", + ); + err + })?; + let sp = lo.to(expr.span); + let expr_str = self.sess.source_map().span_to_snippet(expr.span) + .unwrap_or_else(|_| pprust::expr_to_string(&expr)); + let expr = self.mk_expr( + sp, + ExprKind::Await(ast::AwaitOrigin::FieldLike, expr), + ThinVec::new(), + ); + hi = sp; + ex = ExprKind::Try(expr); + let mut err = self.struct_span_err( + await_sp, + "incorrect use of `await`", + ); + err.span_suggestion( + sp, + "`await` is not a statement", + format!("{}.await?", expr_str), + Applicability::MachineApplicable, + ); + err.emit(); + } + ref t => { + // Handle `await ` + let expr = if t == &token::OpenDelim(token::Brace) { + // Handle `await { }` + // this needs to be handled separatedly from the next arm to avoid + // interpreting `await { }?` as `?.await` + self.parse_block_expr( + None, + self.span, + BlockCheckMode::Default, + ThinVec::new(), + ) + } else { + self.parse_expr() + }.map_err(|mut err| { + err.span_label( + await_sp, + "while parsing this incorrect await statement", + ); + err + })?; + let expr_str = self.sess.source_map().span_to_snippet(expr.span) + .unwrap_or_else(|_| pprust::expr_to_string(&expr)); + let sp = lo.to(expr.span); + hi = sp; + ex = ExprKind::Await(ast::AwaitOrigin::FieldLike, expr); + let mut err = self.struct_span_err( + await_sp, + "incorrect use of `await`", + ); + err.span_suggestion( + sp, + "`await` is not a statement", + format!("{}.await", expr_str), + Applicability::MachineApplicable, + ); + err.emit(); + } + } } else if self.token.is_path_start() { let path = self.parse_path(PathStyle::Expr)?; @@ -2913,6 +2993,23 @@ impl<'a> Parser<'a> { ExprKind::Await(ast::AwaitOrigin::FieldLike, self_arg), ThinVec::new(), ); + if self.token == token::OpenDelim(token::Paren) && + self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren)) + { + // future.await() + let lo = self.span; + self.bump(); // ( + let sp = lo.to(self.span); + self.bump(); // ) + let mut err = self.struct_span_err(span, "incorrect use of `await`"); + err.span_suggestion( + sp, + "`await` is not a method call, remove the parentheses", + String::new(), + Applicability::MachineApplicable, + ); + err.emit() + } return Ok(await_expr); } let segment = self.parse_path_segment(PathStyle::Expr)?; diff --git a/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.rs b/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.rs index b2e8e4be17244..f59f1160e703e 100644 --- a/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.rs +++ b/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.rs @@ -22,6 +22,4 @@ macro_rules! await { () => {} } -fn main() { - match await { await => () } //~ ERROR expected `!`, found `{` -} +fn main() {} diff --git a/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.stderr b/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.stderr index 076a31bd9ced6..c4b82b29f0270 100644 --- a/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.stderr +++ b/src/test/ui/await-keyword/2018-edition-error-in-non-macro-position.stderr @@ -68,13 +68,5 @@ help: you can escape reserved keywords to use them as identifiers LL | macro_rules! r#await { | ^^^^^^^ -error: expected `!`, found `{` - --> $DIR/2018-edition-error-in-non-macro-position.rs:26:17 - | -LL | match await { await => () } - | ----- ^ expected `!` - | | - | while parsing this match expression - -error: aborting due to 8 previous errors +error: aborting due to 7 previous errors diff --git a/src/test/ui/await-keyword/2018-edition-error.rs b/src/test/ui/await-keyword/2018-edition-error.rs index e0b2962ce9791..d856869684266 100644 --- a/src/test/ui/await-keyword/2018-edition-error.rs +++ b/src/test/ui/await-keyword/2018-edition-error.rs @@ -9,6 +9,4 @@ mod outer_mod { use self::outer_mod::await::await; //~ ERROR expected identifier //~^ ERROR expected identifier, found reserved keyword `await` -fn main() { - match await { await => () } //~ ERROR expected `!`, found `{` -} +fn main() {} diff --git a/src/test/ui/await-keyword/2018-edition-error.stderr b/src/test/ui/await-keyword/2018-edition-error.stderr index c8bf9b42ca545..8afe5c1a36b36 100644 --- a/src/test/ui/await-keyword/2018-edition-error.stderr +++ b/src/test/ui/await-keyword/2018-edition-error.stderr @@ -38,13 +38,5 @@ help: you can escape reserved keywords to use them as identifiers LL | use self::outer_mod::await::r#await; | ^^^^^^^ -error: expected `!`, found `{` - --> $DIR/2018-edition-error.rs:13:17 - | -LL | match await { await => () } - | ----- ^ expected `!` - | | - | while parsing this match expression - -error: aborting due to 5 previous errors +error: aborting due to 4 previous errors diff --git a/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs b/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs new file mode 100644 index 0000000000000..ca3654d3c8785 --- /dev/null +++ b/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs @@ -0,0 +1,93 @@ +// edition:2018 + +#![feature(async_await)] + +async fn bar() -> Result<(), ()> { + Ok(()) +} + +async fn foo1() -> Result<(), ()> { + let _ = await bar(); //~ ERROR incorrect use of `await` + Ok(()) +} +async fn foo2() -> Result<(), ()> { + let _ = await? bar(); //~ ERROR incorrect use of `await` + Ok(()) +} +async fn foo3() -> Result<(), ()> { + let _ = await bar()?; //~ ERROR incorrect use of `await` + //~^ ERROR the `?` operator can only be applied to values that implement `std::ops::Try` + Ok(()) +} +async fn foo21() -> Result<(), ()> { + let _ = await { bar() }; //~ ERROR incorrect use of `await` + Ok(()) +} +async fn foo22() -> Result<(), ()> { + let _ = await(bar()); //~ ERROR incorrect use of `await` + Ok(()) +} +async fn foo23() -> Result<(), ()> { + let _ = await { bar() }?; //~ ERROR incorrect use of `await` + Ok(()) +} +async fn foo4() -> Result<(), ()> { + let _ = (await bar())?; //~ ERROR incorrect use of `await` + Ok(()) +} +async fn foo5() -> Result<(), ()> { + let _ = bar().await(); //~ ERROR incorrect use of `await` + Ok(()) +} +async fn foo6() -> Result<(), ()> { + let _ = bar().await()?; //~ ERROR incorrect use of `await` + Ok(()) +} +async fn foo7() -> Result<(), ()> { + let _ = bar().await; // OK + Ok(()) +} +async fn foo8() -> Result<(), ()> { + let _ = bar().await?; // OK + Ok(()) +} +fn foo9() -> Result<(), ()> { + let _ = await bar(); //~ ERROR `await` is only allowed inside `async` functions and blocks + //~^ ERROR incorrect use of `await` + Ok(()) +} +fn foo10() -> Result<(), ()> { + let _ = await? bar(); //~ ERROR `await` is only allowed inside `async` functions and blocks + //~^ ERROR incorrect use of `await` + Ok(()) +} +fn foo11() -> Result<(), ()> { + let _ = await bar()?; //~ ERROR `await` is only allowed inside `async` functions and blocks + //~^ ERROR incorrect use of `await` + Ok(()) +} +fn foo12() -> Result<(), ()> { + let _ = (await bar())?; //~ ERROR `await` is only allowed inside `async` functions and blocks + //~^ ERROR incorrect use of `await` + Ok(()) +} +fn foo13() -> Result<(), ()> { + let _ = bar().await(); //~ ERROR `await` is only allowed inside `async` functions and blocks + //~^ ERROR incorrect use of `await` + Ok(()) +} +fn foo14() -> Result<(), ()> { + let _ = bar().await()?; //~ ERROR `await` is only allowed inside `async` functions and blocks + //~^ ERROR incorrect use of `await` + Ok(()) +} +fn foo15() -> Result<(), ()> { + let _ = bar().await; //~ ERROR `await` is only allowed inside `async` functions and blocks + Ok(()) +} +fn foo16() -> Result<(), ()> { + let _ = bar().await?; //~ ERROR `await` is only allowed inside `async` functions and blocks + Ok(()) +} + +fn main() {} diff --git a/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr new file mode 100644 index 0000000000000..f39ccfcc95c89 --- /dev/null +++ b/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr @@ -0,0 +1,196 @@ +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:10:13 + | +LL | let _ = await bar(); + | ^^^^^------ + | | + | help: `await` is not a statement: `bar().await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:14:13 + | +LL | let _ = await? bar(); + | ^^^^^------- + | | + | help: `await` is not a statement: `bar().await?` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:18:13 + | +LL | let _ = await bar()?; + | ^^^^^------- + | | + | help: `await` is not a statement: `bar()?.await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:23:13 + | +LL | let _ = await { bar() }; + | ^^^^^---------- + | | + | help: `await` is not a statement: `{ bar() }.await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:27:13 + | +LL | let _ = await(bar()); + | ^^^^^------- + | | + | help: `await` is not a statement: `(bar()).await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:31:13 + | +LL | let _ = await { bar() }?; + | ^^^^^---------- + | | + | help: `await` is not a statement: `{ bar() }.await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:35:14 + | +LL | let _ = (await bar())?; + | ^^^^^------ + | | + | help: `await` is not a statement: `bar().await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:39:13 + | +LL | let _ = bar().await(); + | ^^^^^^^^^^^-- help: `await` is not a method call, remove the parentheses + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:43:13 + | +LL | let _ = bar().await()?; + | ^^^^^^^^^^^-- help: `await` is not a method call, remove the parentheses + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:55:13 + | +LL | let _ = await bar(); + | ^^^^^------ + | | + | help: `await` is not a statement: `bar().await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:60:13 + | +LL | let _ = await? bar(); + | ^^^^^------- + | | + | help: `await` is not a statement: `bar().await?` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:65:13 + | +LL | let _ = await bar()?; + | ^^^^^------- + | | + | help: `await` is not a statement: `bar()?.await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:70:14 + | +LL | let _ = (await bar())?; + | ^^^^^------ + | | + | help: `await` is not a statement: `bar().await` + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:75:13 + | +LL | let _ = bar().await(); + | ^^^^^^^^^^^-- help: `await` is not a method call, remove the parentheses + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:80:13 + | +LL | let _ = bar().await()?; + | ^^^^^^^^^^^-- help: `await` is not a method call, remove the parentheses + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:55:13 + | +LL | async fn foo8() -> Result<(), ()> { + | --------------------------------- this function is not `async` +... +LL | let _ = await bar(); + | ^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:60:13 + | +LL | fn foo9() -> Result<(), ()> { + | --------------------------- this function is not `async` +... +LL | let _ = await? bar(); + | ^^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:65:13 + | +LL | fn foo10() -> Result<(), ()> { + | ---------------------------- this function is not `async` +... +LL | let _ = await bar()?; + | ^^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:70:14 + | +LL | fn foo11() -> Result<(), ()> { + | ---------------------------- this function is not `async` +... +LL | let _ = (await bar())?; + | ^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:75:13 + | +LL | fn foo12() -> Result<(), ()> { + | ---------------------------- this function is not `async` +... +LL | let _ = bar().await(); + | ^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:80:13 + | +LL | fn foo13() -> Result<(), ()> { + | ---------------------------- this function is not `async` +... +LL | let _ = bar().await()?; + | ^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:85:13 + | +LL | fn foo14() -> Result<(), ()> { + | ---------------------------- this function is not `async` +... +LL | let _ = bar().await; + | ^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:89:13 + | +LL | fn foo15() -> Result<(), ()> { + | ---------------------------- this function is not `async` +... +LL | let _ = bar().await?; + | ^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` + --> $DIR/incorrect-syntax-suggestions.rs:18:19 + | +LL | let _ = await bar()?; + | ^^^^^^ the `?` operator cannot be applied to type `impl std::future::Future` + | + = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` + = note: required by `std::ops::Try::into_result` + +error: aborting due to 24 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/await-keyword/post_expansion_error.stderr b/src/test/ui/await-keyword/post_expansion_error.stderr index 0996c38b3b6c6..4e525974c2c6f 100644 --- a/src/test/ui/await-keyword/post_expansion_error.stderr +++ b/src/test/ui/await-keyword/post_expansion_error.stderr @@ -2,7 +2,9 @@ error: expected expression, found `)` --> $DIR/post_expansion_error.rs:8:12 | LL | await!() - | ^ expected expression + | ----- ^ expected expression + | | + | while parsing this await macro call error: aborting due to previous error diff --git a/src/test/ui/issues/issue-51719.rs b/src/test/ui/issues/issue-51719.rs index 2c02ac01142bb..b2dbadebc7c1e 100644 --- a/src/test/ui/issues/issue-51719.rs +++ b/src/test/ui/issues/issue-51719.rs @@ -9,3 +9,5 @@ async fn foo() {} fn make_generator() { let _gen = || foo.await; //~ ERROR `await` is only allowed inside `async` functions and blocks } + +fn main() {} \ No newline at end of file diff --git a/src/test/ui/issues/issue-51719.stderr b/src/test/ui/issues/issue-51719.stderr index 768909b66ec77..cb4a5e9037086 100644 --- a/src/test/ui/issues/issue-51719.stderr +++ b/src/test/ui/issues/issue-51719.stderr @@ -1,8 +1,11 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-51719.rs:10:19 | +LL | async fn foo() {} + | -------------- this function is not `async` +... LL | let _gen = || foo.await; - | ^^^^^^^^^ + | ^^^^^^^^^ only allowed inside `async` functions and blocks error: aborting due to previous error diff --git a/src/test/ui/issues/issue-51751.stderr b/src/test/ui/issues/issue-51751.stderr index 0c4cb034a9381..842b99f16f256 100644 --- a/src/test/ui/issues/issue-51751.stderr +++ b/src/test/ui/issues/issue-51751.stderr @@ -1,8 +1,11 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-51751.rs:11:20 | +LL | async fn inc(limit: i64) -> i64 { + | ------------------------------- this function is not `async` +... LL | let finished = result.await; - | ^^^^^^^^^^^^ + | ^^^^^^^^^^^^ only allowed inside `async` functions and blocks error: aborting due to previous error From ee02661474abad0c91b8031c920904ab14f30c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 May 2019 19:59:56 -0700 Subject: [PATCH 02/10] Split parser logic to its own method --- src/libsyntax/parse/parser.rs | 183 ++++++++++++++++++---------------- 1 file changed, 96 insertions(+), 87 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 55e13ef00dcc9..83bfd93d0664a 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2630,93 +2630,9 @@ impl<'a> Parser<'a> { return Err(db); } else if self.span.rust_2018() && self.eat_keyword(keywords::Await) { let await_sp = self.prev_span; - match self.token { - token::Not => { - // FIXME: make this an error when `await!` is no longer supported - // https://github.com/rust-lang/rust/issues/60610 - self.expect(&token::Not)?; - self.expect(&token::OpenDelim(token::Paren))?; - let expr = self.parse_expr().map_err(|mut err| { - err.span_label( - await_sp, - "while parsing this await macro call", - ); - err - })?; - self.expect(&token::CloseDelim(token::Paren))?; - ex = ExprKind::Await(ast::AwaitOrigin::MacroLike, expr); - } - token::Question => { - // Handle `await? ` - self.bump(); // `?` - let expr = self.parse_expr().map_err(|mut err| { - err.span_label( - await_sp, - "while parsing this incorrect await statement", - ); - err - })?; - let sp = lo.to(expr.span); - let expr_str = self.sess.source_map().span_to_snippet(expr.span) - .unwrap_or_else(|_| pprust::expr_to_string(&expr)); - let expr = self.mk_expr( - sp, - ExprKind::Await(ast::AwaitOrigin::FieldLike, expr), - ThinVec::new(), - ); - hi = sp; - ex = ExprKind::Try(expr); - let mut err = self.struct_span_err( - await_sp, - "incorrect use of `await`", - ); - err.span_suggestion( - sp, - "`await` is not a statement", - format!("{}.await?", expr_str), - Applicability::MachineApplicable, - ); - err.emit(); - } - ref t => { - // Handle `await ` - let expr = if t == &token::OpenDelim(token::Brace) { - // Handle `await { }` - // this needs to be handled separatedly from the next arm to avoid - // interpreting `await { }?` as `?.await` - self.parse_block_expr( - None, - self.span, - BlockCheckMode::Default, - ThinVec::new(), - ) - } else { - self.parse_expr() - }.map_err(|mut err| { - err.span_label( - await_sp, - "while parsing this incorrect await statement", - ); - err - })?; - let expr_str = self.sess.source_map().span_to_snippet(expr.span) - .unwrap_or_else(|_| pprust::expr_to_string(&expr)); - let sp = lo.to(expr.span); - hi = sp; - ex = ExprKind::Await(ast::AwaitOrigin::FieldLike, expr); - let mut err = self.struct_span_err( - await_sp, - "incorrect use of `await`", - ); - err.span_suggestion( - sp, - "`await` is not a statement", - format!("{}.await", expr_str), - Applicability::MachineApplicable, - ); - err.emit(); - } - } + let e = self.parse_async_macro_or_stmt(lo, await_sp)?; + hi = e.0; + ex = e.1; } else if self.token.is_path_start() { let path = self.parse_path(PathStyle::Expr)?; @@ -2781,6 +2697,99 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr, true) } + fn parse_async_macro_or_stmt( + &mut self, + lo: Span, + await_sp: Span, + ) -> PResult<'a, (Span, ExprKind)> { + Ok(match self.token { + token::Not => { + // Handle correct `await!()` + // FIXME: make this an error when `await!` is no longer supported + // https://github.com/rust-lang/rust/issues/60610 + self.expect(&token::Not)?; + self.expect(&token::OpenDelim(token::Paren))?; + let expr = self.parse_expr().map_err(|mut err| { + err.span_label( + await_sp, + "while parsing this await macro call", + ); + err + })?; + self.expect(&token::CloseDelim(token::Paren))?; + (expr.span, ExprKind::Await(ast::AwaitOrigin::MacroLike, expr)) + } + token::Question => { + // Handle `await? ` + self.bump(); // `?` + let expr = self.parse_expr().map_err(|mut err| { + err.span_label( + await_sp, + "while parsing this incorrect await statement", + ); + err + })?; + let sp = lo.to(expr.span); + let expr_str = self.sess.source_map().span_to_snippet(expr.span) + .unwrap_or_else(|_| pprust::expr_to_string(&expr)); + let expr = self.mk_expr( + sp, + ExprKind::Await(ast::AwaitOrigin::FieldLike, expr), + ThinVec::new(), + ); + let mut err = self.struct_span_err( + await_sp, + "incorrect use of `await`", + ); + err.span_suggestion( + sp, + "`await` is not a statement", + format!("{}.await?", expr_str), + Applicability::MachineApplicable, + ); + err.emit(); + (sp, ExprKind::Try(expr)) + } + ref t => { + // Handle `await ` + let expr = if t == &token::OpenDelim(token::Brace) { + // Handle `await { }` + // this needs to be handled separatedly from the next arm to avoid + // interpreting `await { }?` as `?.await` + self.parse_block_expr( + None, + self.span, + BlockCheckMode::Default, + ThinVec::new(), + ) + } else { + self.parse_expr() + }.map_err(|mut err| { + err.span_label( + await_sp, + "while parsing this incorrect await statement", + ); + err + })?; + let expr_str = self.sess.source_map().span_to_snippet(expr.span) + .unwrap_or_else(|_| pprust::expr_to_string(&expr)); + let sp = lo.to(expr.span); + let mut err = self.struct_span_err( + await_sp, + "incorrect use of `await`", + ); + err.span_suggestion( + sp, + "`await` is not a statement", + format!("{}.await", expr_str), + Applicability::MachineApplicable, + ); + err.emit(); + (sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr)) + } + }) + } + fn maybe_parse_struct_expr( &mut self, lo: Span, From 01c6689604d671c5fa51671940f69833222194ef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 May 2019 20:06:15 -0700 Subject: [PATCH 03/10] Simplify span usage for incorrect await --- src/libsyntax/parse/parser.rs | 6 +- .../incorrect-syntax-suggestions.stderr | 60 ++++++------------- src/test/ui/feature-gate/await-macro.stderr | 2 +- 3 files changed, 23 insertions(+), 45 deletions(-) diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 83bfd93d0664a..45081aadfd97b 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2738,7 +2738,7 @@ impl<'a> Parser<'a> { ThinVec::new(), ); let mut err = self.struct_span_err( - await_sp, + sp, "incorrect use of `await`", ); err.span_suggestion( @@ -2775,7 +2775,7 @@ impl<'a> Parser<'a> { .unwrap_or_else(|_| pprust::expr_to_string(&expr)); let sp = lo.to(expr.span); let mut err = self.struct_span_err( - await_sp, + sp, "incorrect use of `await`", ); err.span_suggestion( @@ -3010,7 +3010,7 @@ impl<'a> Parser<'a> { self.bump(); // ( let sp = lo.to(self.span); self.bump(); // ) - let mut err = self.struct_span_err(span, "incorrect use of `await`"); + let mut err = self.struct_span_err(sp, "incorrect use of `await`"); err.span_suggestion( sp, "`await` is not a method call, remove the parentheses", diff --git a/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr index f39ccfcc95c89..2e2efedcf00a3 100644 --- a/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr +++ b/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr @@ -2,113 +2,91 @@ error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:10:13 | LL | let _ = await bar(); - | ^^^^^------ - | | - | help: `await` is not a statement: `bar().await` + | ^^^^^^^^^^^ help: `await` is not a statement: `bar().await` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:14:13 | LL | let _ = await? bar(); - | ^^^^^------- - | | - | help: `await` is not a statement: `bar().await?` + | ^^^^^^^^^^^^ help: `await` is not a statement: `bar().await?` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:18:13 | LL | let _ = await bar()?; - | ^^^^^------- - | | - | help: `await` is not a statement: `bar()?.await` + | ^^^^^^^^^^^^ help: `await` is not a statement: `bar()?.await` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:23:13 | LL | let _ = await { bar() }; - | ^^^^^---------- - | | - | help: `await` is not a statement: `{ bar() }.await` + | ^^^^^^^^^^^^^^^ help: `await` is not a statement: `{ bar() }.await` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:27:13 | LL | let _ = await(bar()); - | ^^^^^------- - | | - | help: `await` is not a statement: `(bar()).await` + | ^^^^^^^^^^^^ help: `await` is not a statement: `(bar()).await` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:31:13 | LL | let _ = await { bar() }?; - | ^^^^^---------- - | | - | help: `await` is not a statement: `{ bar() }.await` + | ^^^^^^^^^^^^^^^ help: `await` is not a statement: `{ bar() }.await` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:35:14 | LL | let _ = (await bar())?; - | ^^^^^------ - | | - | help: `await` is not a statement: `bar().await` + | ^^^^^^^^^^^ help: `await` is not a statement: `bar().await` error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:39:13 + --> $DIR/incorrect-syntax-suggestions.rs:39:24 | LL | let _ = bar().await(); - | ^^^^^^^^^^^-- help: `await` is not a method call, remove the parentheses + | ^^ help: `await` is not a method call, remove the parentheses error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:43:13 + --> $DIR/incorrect-syntax-suggestions.rs:43:24 | LL | let _ = bar().await()?; - | ^^^^^^^^^^^-- help: `await` is not a method call, remove the parentheses + | ^^ help: `await` is not a method call, remove the parentheses error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:55:13 | LL | let _ = await bar(); - | ^^^^^------ - | | - | help: `await` is not a statement: `bar().await` + | ^^^^^^^^^^^ help: `await` is not a statement: `bar().await` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:60:13 | LL | let _ = await? bar(); - | ^^^^^------- - | | - | help: `await` is not a statement: `bar().await?` + | ^^^^^^^^^^^^ help: `await` is not a statement: `bar().await?` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:65:13 | LL | let _ = await bar()?; - | ^^^^^------- - | | - | help: `await` is not a statement: `bar()?.await` + | ^^^^^^^^^^^^ help: `await` is not a statement: `bar()?.await` error: incorrect use of `await` --> $DIR/incorrect-syntax-suggestions.rs:70:14 | LL | let _ = (await bar())?; - | ^^^^^------ - | | - | help: `await` is not a statement: `bar().await` + | ^^^^^^^^^^^ help: `await` is not a statement: `bar().await` error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:75:13 + --> $DIR/incorrect-syntax-suggestions.rs:75:24 | LL | let _ = bar().await(); - | ^^^^^^^^^^^-- help: `await` is not a method call, remove the parentheses + | ^^ help: `await` is not a method call, remove the parentheses error: incorrect use of `await` - --> $DIR/incorrect-syntax-suggestions.rs:80:13 + --> $DIR/incorrect-syntax-suggestions.rs:80:24 | LL | let _ = bar().await()?; - | ^^^^^^^^^^^-- help: `await` is not a method call, remove the parentheses + | ^^ help: `await` is not a method call, remove the parentheses error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:55:13 diff --git a/src/test/ui/feature-gate/await-macro.stderr b/src/test/ui/feature-gate/await-macro.stderr index 699a7a8886e89..57aab6800f7ab 100644 --- a/src/test/ui/feature-gate/await-macro.stderr +++ b/src/test/ui/feature-gate/await-macro.stderr @@ -2,7 +2,7 @@ error[E0658]: `await!()` macro syntax is unstable, and will soon be remove --> $DIR/await-macro.rs:9:5 | LL | await!(bar()); - | ^^^^^^^^^^^^^ + | ^^^^^^^^^^^^ | = note: for more information, see https://github.com/rust-lang/rust/issues/50547 = help: add #![feature(await_macro)] to the crate attributes to enable From 91c36c40bdd68d928f9202077309ae48f575d187 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Wed, 15 May 2019 20:37:50 -0700 Subject: [PATCH 04/10] tidy fix --- src/test/ui/issues/issue-51719.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/ui/issues/issue-51719.rs b/src/test/ui/issues/issue-51719.rs index b2dbadebc7c1e..5966edd0bf098 100644 --- a/src/test/ui/issues/issue-51719.rs +++ b/src/test/ui/issues/issue-51719.rs @@ -10,4 +10,4 @@ fn make_generator() { let _gen = || foo.await; //~ ERROR `await` is only allowed inside `async` functions and blocks } -fn main() {} \ No newline at end of file +fn main() {} From c61660500555c81d6049b7e7b5b502d1bd9df80f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 16 May 2019 13:17:40 -0700 Subject: [PATCH 05/10] Point at enclosing fn/closure when it's not async --- src/librustc/hir/lowering.rs | 16 +++-- .../incorrect-syntax-suggestions.rs | 14 +++++ .../incorrect-syntax-suggestions.stderr | 58 +++++++++++-------- src/test/ui/issues/issue-51719.stderr | 7 +-- src/test/ui/issues/issue-51751.stderr | 6 +- 5 files changed, 60 insertions(+), 41 deletions(-) diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 12ccc79e4ab41..3a8b139236cca 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -99,7 +99,7 @@ pub struct LoweringContext<'a> { /// Used to get the current `fn`'s def span to point to when using `await` /// outside of an `async fn`. - current_item_id: Option, + current_item: Option, catch_scopes: Vec, loop_scopes: Vec, @@ -254,7 +254,7 @@ pub fn lower_crate( node_id_to_hir_id: IndexVec::new(), is_generator: false, is_async_body: false, - current_item_id: None, + current_item: None, is_in_trait_impl: false, lifetimes_to_define: Vec::new(), is_collecting_in_band_lifetimes: false, @@ -3120,8 +3120,8 @@ impl<'a> LoweringContext<'a> { } ItemKind::Fn(ref decl, ref header, ref generics, ref body) => { let fn_def_id = self.resolver.definitions().local_def_id(id); - let hir_id = self.lower_node_id(id); self.with_new_scopes(|this| { + this.current_item = Some(ident.span); let mut lower_fn = |decl: &FnDecl| { // Note: we don't need to change the return type from `T` to // `impl Future` here because lower_body @@ -3159,7 +3159,6 @@ impl<'a> LoweringContext<'a> { } else { lower_fn(decl) }; - this.current_item_id = Some(hir_id); hir::ItemKind::Fn( fn_decl, @@ -3661,6 +3660,7 @@ impl<'a> LoweringContext<'a> { } else { lower_method(sig) }; + self.current_item = Some(i.span); (generics, hir::ImplItemKind::Method(sig, body_id)) } @@ -4277,6 +4277,7 @@ impl<'a> LoweringContext<'a> { let fn_decl = self.lower_fn_decl(decl, None, false, None); self.with_new_scopes(|this| { + this.current_item = Some(fn_decl_span); let mut is_generator = false; let body_id = this.lower_body(Some(decl), |this| { let e = this.lower_expr(body); @@ -5565,11 +5566,8 @@ impl<'a> LoweringContext<'a> { "`await` is only allowed inside `async` functions and blocks" ); err.span_label(await_span, "only allowed inside `async` functions and blocks"); - if let Some(item_id) = self.current_item_id { - err.span_label( - self.sess.source_map().def_span(self.items[&item_id].span), - "this function is not `async`", - ); + if let Some(item_sp) = self.current_item { + err.span_label(item_sp, "this is not `async`"); } err.emit(); return hir::ExprKind::Err; diff --git a/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs b/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs index ca3654d3c8785..6b615cc9ff98f 100644 --- a/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs +++ b/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs @@ -89,5 +89,19 @@ fn foo16() -> Result<(), ()> { let _ = bar().await?; //~ ERROR `await` is only allowed inside `async` functions and blocks Ok(()) } +fn foo24() -> Result<(), ()> { + fn foo() -> Result<(), ()> { + let _ = bar().await?; //~ ERROR `await` is only allowed inside `async` functions and blocks + Ok(()) + } + foo() +} +fn foo25() -> Result<(), ()> { + let foo = || { + let _ = bar().await?; //~ ERROR `await` is only allowed inside `async` functions and blocks + Ok(()) + }; + foo() +} fn main() {} diff --git a/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr index 2e2efedcf00a3..5ea59d4bcee17 100644 --- a/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr +++ b/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr @@ -91,75 +91,83 @@ LL | let _ = bar().await()?; error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:55:13 | -LL | async fn foo8() -> Result<(), ()> { - | --------------------------------- this function is not `async` -... +LL | fn foo9() -> Result<(), ()> { + | ---- this is not `async` LL | let _ = await bar(); | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:60:13 | -LL | fn foo9() -> Result<(), ()> { - | --------------------------- this function is not `async` -... +LL | fn foo10() -> Result<(), ()> { + | ----- this is not `async` LL | let _ = await? bar(); | ^^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:65:13 | -LL | fn foo10() -> Result<(), ()> { - | ---------------------------- this function is not `async` -... +LL | fn foo11() -> Result<(), ()> { + | ----- this is not `async` LL | let _ = await bar()?; | ^^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:70:14 | -LL | fn foo11() -> Result<(), ()> { - | ---------------------------- this function is not `async` -... +LL | fn foo12() -> Result<(), ()> { + | ----- this is not `async` LL | let _ = (await bar())?; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:75:13 | -LL | fn foo12() -> Result<(), ()> { - | ---------------------------- this function is not `async` -... +LL | fn foo13() -> Result<(), ()> { + | ----- this is not `async` LL | let _ = bar().await(); | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:80:13 | -LL | fn foo13() -> Result<(), ()> { - | ---------------------------- this function is not `async` -... +LL | fn foo14() -> Result<(), ()> { + | ----- this is not `async` LL | let _ = bar().await()?; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:85:13 | -LL | fn foo14() -> Result<(), ()> { - | ---------------------------- this function is not `async` -... +LL | fn foo15() -> Result<(), ()> { + | ----- this is not `async` LL | let _ = bar().await; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:89:13 | -LL | fn foo15() -> Result<(), ()> { - | ---------------------------- this function is not `async` -... +LL | fn foo16() -> Result<(), ()> { + | ----- this is not `async` LL | let _ = bar().await?; | ^^^^^^^^^^^ only allowed inside `async` functions and blocks +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:94:17 + | +LL | fn foo() -> Result<(), ()> { + | --- this is not `async` +LL | let _ = bar().await?; + | ^^^^^^^^^^^ only allowed inside `async` functions and blocks + +error[E0728]: `await` is only allowed inside `async` functions and blocks + --> $DIR/incorrect-syntax-suggestions.rs:101:17 + | +LL | let foo = || { + | -- this is not `async` +LL | let _ = bar().await?; + | ^^^^^^^^^^^ only allowed inside `async` functions and blocks + error[E0277]: the `?` operator can only be applied to values that implement `std::ops::Try` --> $DIR/incorrect-syntax-suggestions.rs:18:19 | @@ -169,6 +177,6 @@ LL | let _ = await bar()?; = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` = note: required by `std::ops::Try::into_result` -error: aborting due to 24 previous errors +error: aborting due to 26 previous errors For more information about this error, try `rustc --explain E0277`. diff --git a/src/test/ui/issues/issue-51719.stderr b/src/test/ui/issues/issue-51719.stderr index cb4a5e9037086..c06165b24468f 100644 --- a/src/test/ui/issues/issue-51719.stderr +++ b/src/test/ui/issues/issue-51719.stderr @@ -1,11 +1,10 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-51719.rs:10:19 | -LL | async fn foo() {} - | -------------- this function is not `async` -... LL | let _gen = || foo.await; - | ^^^^^^^^^ only allowed inside `async` functions and blocks + | -- ^^^^^^^^^ only allowed inside `async` functions and blocks + | | + | this is not `async` error: aborting due to previous error diff --git a/src/test/ui/issues/issue-51751.stderr b/src/test/ui/issues/issue-51751.stderr index 842b99f16f256..97b63d1590ec6 100644 --- a/src/test/ui/issues/issue-51751.stderr +++ b/src/test/ui/issues/issue-51751.stderr @@ -1,9 +1,9 @@ error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/issue-51751.rs:11:20 | -LL | async fn inc(limit: i64) -> i64 { - | ------------------------------- this function is not `async` -... +LL | fn main() { + | ---- this is not `async` +LL | let result = inc(10000); LL | let finished = result.await; | ^^^^^^^^^^^^ only allowed inside `async` functions and blocks From 0183a575f686003586ca3308db8a55d6224f4789 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 16 May 2019 13:20:06 -0700 Subject: [PATCH 06/10] readd match await test case --- .../incorrect-syntax-suggestions.rs | 6 ++++- .../incorrect-syntax-suggestions.stderr | 27 ++++++++++++++++++- 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs b/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs index 6b615cc9ff98f..e1e5bdd3d1b92 100644 --- a/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs +++ b/src/test/ui/await-keyword/incorrect-syntax-suggestions.rs @@ -104,4 +104,8 @@ fn foo25() -> Result<(), ()> { foo() } -fn main() {} +fn main() { + match await { await => () } + //~^ ERROR expected expression, found `=>` + //~| ERROR incorrect use of `await` +} //~ ERROR expected one of `.`, `?`, `{`, or an operator, found `}` diff --git a/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr b/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr index 5ea59d4bcee17..bc7bd77479e40 100644 --- a/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr +++ b/src/test/ui/await-keyword/incorrect-syntax-suggestions.stderr @@ -88,6 +88,31 @@ error: incorrect use of `await` LL | let _ = bar().await()?; | ^^ help: `await` is not a method call, remove the parentheses +error: expected expression, found `=>` + --> $DIR/incorrect-syntax-suggestions.rs:108:25 + | +LL | match await { await => () } + | ----- ^^ expected expression + | | + | while parsing this incorrect await statement + +error: incorrect use of `await` + --> $DIR/incorrect-syntax-suggestions.rs:108:11 + | +LL | match await { await => () } + | ^^^^^^^^^^^^^^^^^^^^^ help: `await` is not a statement: `{ await => () }.await` + +error: expected one of `.`, `?`, `{`, or an operator, found `}` + --> $DIR/incorrect-syntax-suggestions.rs:111:1 + | +LL | match await { await => () } + | ----- - expected one of `.`, `?`, `{`, or an operator here + | | + | while parsing this match expression +... +LL | } + | ^ unexpected token + error[E0728]: `await` is only allowed inside `async` functions and blocks --> $DIR/incorrect-syntax-suggestions.rs:55:13 | @@ -177,6 +202,6 @@ LL | let _ = await bar()?; = help: the trait `std::ops::Try` is not implemented for `impl std::future::Future` = note: required by `std::ops::Try::into_result` -error: aborting due to 26 previous errors +error: aborting due to 29 previous errors For more information about this error, try `rustc --explain E0277`. From b9d6fe3ae96a7f1f478dc6baf29b8e4cff5ab865 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Esteban=20K=C3=BCber?= Date: Thu, 16 May 2019 13:33:26 -0700 Subject: [PATCH 07/10] Review comments - Change wording of suggestion - Move recovery logic to `diagnostics.rs` - Reduce ammount of code duplication --- src/libsyntax/parse/diagnostics.rs | 40 +++++- src/libsyntax/parse/parser.rs | 124 ++++-------------- .../incorrect-syntax-suggestions.stderr | 26 ++-- 3 files changed, 81 insertions(+), 109 deletions(-) diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index 32e1ee94f0dfb..61453e35095be 100644 --- a/src/libsyntax/parse/diagnostics.rs +++ b/src/libsyntax/parse/diagnostics.rs @@ -1,5 +1,5 @@ use crate::ast; -use crate::ast::{Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind}; +use crate::ast::{BlockCheckMode, Expr, ExprKind, Item, ItemKind, Pat, PatKind, QSelf, Ty, TyKind}; use crate::parse::parser::PathStyle; use crate::parse::token; use crate::parse::PResult; @@ -223,4 +223,42 @@ impl<'a> Parser<'a> { false } } + + /// Consume alternative await syntaxes like `await `, `await? `, `await()` + /// and `await { }`. + crate fn parse_incorrect_await_syntax( + &mut self, + lo: Span, + await_sp: Span, + ) -> PResult<'a, (Span, ExprKind)> { + let is_question = self.eat(&token::Question); // Handle `await? `. + let expr = if self.token == token::OpenDelim(token::Brace) { + // Handle `await { }`. + // This needs to be handled separatedly from the next arm to avoid + // interpreting `await { }?` as `?.await`. + self.parse_block_expr( + None, + self.span, + BlockCheckMode::Default, + ThinVec::new(), + ) + } else { + self.parse_expr() + }.map_err(|mut err| { + err.span_label(await_sp, "while parsing this incorrect await expression"); + err + })?; + let expr_str = self.sess.source_map().span_to_snippet(expr.span) + .unwrap_or_else(|_| pprust::expr_to_string(&expr)); + let suggestion = format!("{}.await{}", expr_str, if is_question { "?" } else { "" }); + let sp = lo.to(expr.span); + let app = match expr.node { + ExprKind::Try(_) => Applicability::MaybeIncorrect, // `await ?` + _ => Applicability::MachineApplicable, + }; + self.struct_span_err(sp, "incorrect use of `await`") + .span_suggestion(sp, "`await` is a postfix operation", suggestion, app) + .emit(); + Ok((sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr))) + } } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index 45081aadfd97b..bb0f9fa9502a0 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2629,10 +2629,9 @@ impl<'a> Parser<'a> { db.note("variable declaration using `let` is a statement"); return Err(db); } else if self.span.rust_2018() && self.eat_keyword(keywords::Await) { - let await_sp = self.prev_span; - let e = self.parse_async_macro_or_stmt(lo, await_sp)?; - hi = e.0; - ex = e.1; + let (await_hi, e_kind) = self.parse_await_macro_or_alt(lo, self.prev_span)?; + hi = await_hi; + ex = e_kind; } else if self.token.is_path_start() { let path = self.parse_path(PathStyle::Expr)?; @@ -2697,97 +2696,29 @@ impl<'a> Parser<'a> { self.maybe_recover_from_bad_qpath(expr, true) } - fn parse_async_macro_or_stmt( + /// Parse `await!()` calls, or alternatively recover from incorrect but reasonable + /// alternative syntaxes `await `, `await? `, `await()` and + /// `await { }`. + fn parse_await_macro_or_alt( &mut self, lo: Span, await_sp: Span, ) -> PResult<'a, (Span, ExprKind)> { - Ok(match self.token { - token::Not => { - // Handle correct `await!()` - // FIXME: make this an error when `await!` is no longer supported - // https://github.com/rust-lang/rust/issues/60610 - self.expect(&token::Not)?; - self.expect(&token::OpenDelim(token::Paren))?; - let expr = self.parse_expr().map_err(|mut err| { - err.span_label( - await_sp, - "while parsing this await macro call", - ); - err - })?; - self.expect(&token::CloseDelim(token::Paren))?; - (expr.span, ExprKind::Await(ast::AwaitOrigin::MacroLike, expr)) - } - token::Question => { - // Handle `await? ` - self.bump(); // `?` - let expr = self.parse_expr().map_err(|mut err| { - err.span_label( - await_sp, - "while parsing this incorrect await statement", - ); - err - })?; - let sp = lo.to(expr.span); - let expr_str = self.sess.source_map().span_to_snippet(expr.span) - .unwrap_or_else(|_| pprust::expr_to_string(&expr)); - let expr = self.mk_expr( - sp, - ExprKind::Await(ast::AwaitOrigin::FieldLike, expr), - ThinVec::new(), - ); - let mut err = self.struct_span_err( - sp, - "incorrect use of `await`", - ); - err.span_suggestion( - sp, - "`await` is not a statement", - format!("{}.await?", expr_str), - Applicability::MachineApplicable, - ); - err.emit(); - (sp, ExprKind::Try(expr)) - } - ref t => { - // Handle `await ` - let expr = if t == &token::OpenDelim(token::Brace) { - // Handle `await { }` - // this needs to be handled separatedly from the next arm to avoid - // interpreting `await { }?` as `?.await` - self.parse_block_expr( - None, - self.span, - BlockCheckMode::Default, - ThinVec::new(), - ) - } else { - self.parse_expr() - }.map_err(|mut err| { - err.span_label( - await_sp, - "while parsing this incorrect await statement", - ); - err - })?; - let expr_str = self.sess.source_map().span_to_snippet(expr.span) - .unwrap_or_else(|_| pprust::expr_to_string(&expr)); - let sp = lo.to(expr.span); - let mut err = self.struct_span_err( - sp, - "incorrect use of `await`", - ); - err.span_suggestion( - sp, - "`await` is not a statement", - format!("{}.await", expr_str), - Applicability::MachineApplicable, - ); - err.emit(); - (sp, ExprKind::Await(ast::AwaitOrigin::FieldLike, expr)) - } - }) + if self.token == token::Not { + // Handle correct `await!()`. + // FIXME: make this an error when `await!` is no longer supported + // https://github.com/rust-lang/rust/issues/60610 + self.expect(&token::Not)?; + self.expect(&token::OpenDelim(token::Paren))?; + let expr = self.parse_expr().map_err(|mut err| { + err.span_label(await_sp, "while parsing this await macro call"); + err + })?; + self.expect(&token::CloseDelim(token::Paren))?; + Ok((expr.span, ExprKind::Await(ast::AwaitOrigin::MacroLike, expr))) + } else { // Handle `await `. + self.parse_incorrect_await_syntax(lo, await_sp) + } } fn maybe_parse_struct_expr( @@ -2938,10 +2869,13 @@ impl<'a> Parser<'a> { } /// Parses a block or unsafe block. - fn parse_block_expr(&mut self, opt_label: Option