diff --git a/src/libsyntax/parse/diagnostics.rs b/src/libsyntax/parse/diagnostics.rs index 32e1ee94f0d..61453e35095 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 45081aadfd9..bb0f9fa9502 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