Skip to content

Commit

Permalink
Parse alternative incorrect uses of await and recover
Browse files Browse the repository at this point in the history
  • Loading branch information
estebank committed May 16, 2019
1 parent 1962ade commit d763faf
Show file tree
Hide file tree
Showing 12 changed files with 428 additions and 37 deletions.
19 changes: 17 additions & 2 deletions src/librustc/hir/lowering.rs
Expand Up @@ -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<hir::HirId>,

catch_scopes: Vec<NodeId>,
loop_scopes: Vec<NodeId>,
is_in_loop_condition: bool,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -3153,6 +3159,7 @@ impl<'a> LoweringContext<'a> {
} else {
lower_fn(decl)
};
this.current_item_id = Some(hir_id);

hir::ItemKind::Fn(
fn_decl,
Expand Down Expand Up @@ -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,
Expand Down
113 changes: 105 additions & 8 deletions src/libsyntax/parse/parser.rs
Expand Up @@ -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? <expr>`
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 <expr>`
let expr = if t == &token::OpenDelim(token::Brace) {
// Handle `await { <expr> }`
// this needs to be handled separatedly from the next arm to avoid
// interpreting `await { <expr> }?` as `<expr>?.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)?;

Expand Down Expand Up @@ -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)?;
Expand Down
Expand Up @@ -22,6 +22,4 @@ macro_rules! await {
() => {}
}

fn main() {
match await { await => () } //~ ERROR expected `!`, found `{`
}
fn main() {}
Expand Up @@ -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

4 changes: 1 addition & 3 deletions src/test/ui/await-keyword/2018-edition-error.rs
Expand Up @@ -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() {}
10 changes: 1 addition & 9 deletions src/test/ui/await-keyword/2018-edition-error.stderr
Expand Up @@ -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

93 changes: 93 additions & 0 deletions 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() {}

0 comments on commit d763faf

Please sign in to comment.