diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 55c9f26999be0..b3bb72554e9c4 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -13,7 +13,7 @@ use rustc_ast::util::classify; use rustc_ast::util::literal::LitError; use rustc_ast::util::parser::{prec_let_scrutinee_needs_par, AssocOp, Fixity}; use rustc_ast_pretty::pprust; -use rustc_errors::{Applicability, PResult}; +use rustc_errors::{Applicability, DiagnosticBuilder, PResult}; use rustc_span::source_map::{self, Span, Spanned}; use rustc_span::symbol::{kw, sym, Symbol}; use std::mem; @@ -1068,8 +1068,8 @@ impl<'a> Parser<'a> { } fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P> { - let lo = self.token.span; let path = self.parse_path(PathStyle::Expr)?; + let lo = path.span; // `!`, as an operator, is prefix, so we know this isn't that. let (hi, kind) = if self.eat(&token::Not) { @@ -1081,7 +1081,7 @@ impl<'a> Parser<'a> { }; (self.prev_token.span, ExprKind::MacCall(mac)) } else if self.check(&token::OpenDelim(token::Brace)) { - if let Some(expr) = self.maybe_parse_struct_expr(lo, &path, &attrs) { + if let Some(expr) = self.maybe_parse_struct_expr(&path, &attrs) { return expr; } else { (path.span, ExprKind::Path(None, path)) @@ -1895,16 +1895,15 @@ impl<'a> Parser<'a> { fn maybe_parse_struct_expr( &mut self, - lo: Span, path: &ast::Path, attrs: &AttrVec, ) -> Option>> { let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL); if struct_allowed || self.is_certainly_not_a_block() { // This is a struct literal, but we don't can't accept them here. - let expr = self.parse_struct_expr(lo, path.clone(), attrs.clone()); + let expr = self.parse_struct_expr(path.clone(), attrs.clone()); if let (Ok(expr), false) = (&expr, struct_allowed) { - self.error_struct_lit_not_allowed_here(lo, expr.span); + self.error_struct_lit_not_allowed_here(path.span, expr.span); } return Some(expr); } @@ -1923,17 +1922,23 @@ impl<'a> Parser<'a> { pub(super) fn parse_struct_expr( &mut self, - lo: Span, pth: ast::Path, mut attrs: AttrVec, ) -> PResult<'a, P> { - let struct_sp = lo.to(self.prev_token.span); self.bump(); let mut fields = Vec::new(); let mut base = None; + let mut recover_async = false; attrs.extend(self.parse_inner_attributes()?); + let mut async_block_err = |e: &mut DiagnosticBuilder<'_>, span: Span| { + recover_async = true; + e.span_label(span, "`async` blocks are only allowed in the 2018 edition"); + e.help("set `edition = \"2018\"` in `Cargo.toml`"); + e.note("for more on editions, read https://doc.rust-lang.org/edition-guide"); + }; + while self.token != token::CloseDelim(token::Brace) { if self.eat(&token::DotDot) { let exp_span = self.prev_token.span; @@ -1952,7 +1957,11 @@ impl<'a> Parser<'a> { let parsed_field = match self.parse_field() { Ok(f) => Some(f), Err(mut e) => { - e.span_label(struct_sp, "while parsing this struct"); + if pth == kw::Async { + async_block_err(&mut e, pth.span); + } else { + e.span_label(pth.span, "while parsing this struct"); + } e.emit(); // If the next token is a comma, then try to parse @@ -1976,15 +1985,19 @@ impl<'a> Parser<'a> { } } Err(mut e) => { - e.span_label(struct_sp, "while parsing this struct"); - if let Some(f) = recovery_field { - fields.push(f); - e.span_suggestion( - self.prev_token.span.shrink_to_hi(), - "try adding a comma", - ",".into(), - Applicability::MachineApplicable, - ); + if pth == kw::Async { + async_block_err(&mut e, pth.span); + } else { + e.span_label(pth.span, "while parsing this struct"); + if let Some(f) = recovery_field { + fields.push(f); + e.span_suggestion( + self.prev_token.span.shrink_to_hi(), + "try adding a comma", + ",".into(), + Applicability::MachineApplicable, + ); + } } e.emit(); self.recover_stmt_(SemiColonMode::Comma, BlockMode::Ignore); @@ -1993,9 +2006,10 @@ impl<'a> Parser<'a> { } } - let span = lo.to(self.token.span); + let span = pth.span.to(self.token.span); self.expect(&token::CloseDelim(token::Brace))?; - Ok(self.mk_expr(span, ExprKind::Struct(pth, fields, base), attrs)) + let expr = if recover_async { ExprKind::Err } else { ExprKind::Struct(pth, fields, base) }; + Ok(self.mk_expr(span, expr, attrs)) } /// Use in case of error after field-looking code: `S { foo: () with a }`. diff --git a/src/librustc_parse/parser/item.rs b/src/librustc_parse/parser/item.rs index e9f5f2c0deafc..7fb814973e250 100644 --- a/src/librustc_parse/parser/item.rs +++ b/src/librustc_parse/parser/item.rs @@ -1550,7 +1550,7 @@ impl<'a> Parser<'a> { if span.rust_2015() { let diag = self.diagnostic(); struct_span_err!(diag, span, E0670, "`async fn` is not permitted in the 2015 edition") - .note("to use `async fn`, switch to Rust 2018") + .span_label(span, "to use `async fn`, switch to Rust 2018") .help("set `edition = \"2018\"` in `Cargo.toml`") .note("for more on editions, read https://doc.rust-lang.org/edition-guide") .emit(); diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index e5d0ab247aa46..849193151c335 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -79,7 +79,7 @@ impl<'a> Parser<'a> { } let expr = if self.check(&token::OpenDelim(token::Brace)) { - self.parse_struct_expr(lo, path, AttrVec::new())? + self.parse_struct_expr(path, AttrVec::new())? } else { let hi = self.prev_token.span; self.mk_expr(lo.to(hi), ExprKind::Path(None, path), AttrVec::new()) diff --git a/src/librustc_resolve/late/diagnostics.rs b/src/librustc_resolve/late/diagnostics.rs index f6213189d9443..c1a7cf855fafb 100644 --- a/src/librustc_resolve/late/diagnostics.rs +++ b/src/librustc_resolve/late/diagnostics.rs @@ -151,7 +151,11 @@ impl<'a> LateResolutionVisitor<'a, '_, '_> { }; ( format!("cannot find {} `{}` in {}{}", expected, item_str, mod_prefix, mod_str), - format!("not found in {}", mod_str), + if path_str == "async" && expected.starts_with("struct") { + "`async` blocks are only allowed in the 2018 edition".to_string() + } else { + format!("not found in {}", mod_str) + }, item_span, false, ) diff --git a/src/test/ui/async-await/edition-deny-async-fns-2015.stderr b/src/test/ui/async-await/edition-deny-async-fns-2015.stderr index f3d982801bb99..8bffeb2131dec 100644 --- a/src/test/ui/async-await/edition-deny-async-fns-2015.stderr +++ b/src/test/ui/async-await/edition-deny-async-fns-2015.stderr @@ -2,9 +2,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:3:1 | LL | async fn foo() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -12,9 +11,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:5:12 | LL | fn baz() { async fn foo() {} } - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -22,9 +20,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:7:1 | LL | async fn async_baz() { - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -32,9 +29,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:8:5 | LL | async fn bar() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -42,9 +38,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:14:5 | LL | async fn foo() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -52,9 +47,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:18:5 | LL | async fn foo() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -62,9 +56,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:36:9 | LL | async fn bar() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -72,9 +65,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:26:9 | LL | async fn foo() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide @@ -82,9 +74,8 @@ error[E0670]: `async fn` is not permitted in the 2015 edition --> $DIR/edition-deny-async-fns-2015.rs:31:13 | LL | async fn bar() {} - | ^^^^^ + | ^^^^^ to use `async fn`, switch to Rust 2018 | - = note: to use `async fn`, switch to Rust 2018 = help: set `edition = "2018"` in `Cargo.toml` = note: for more on editions, read https://doc.rust-lang.org/edition-guide diff --git a/src/test/ui/editions/async-block-2015.rs b/src/test/ui/editions/async-block-2015.rs new file mode 100644 index 0000000000000..985606a6f2545 --- /dev/null +++ b/src/test/ui/editions/async-block-2015.rs @@ -0,0 +1,30 @@ +async fn foo() { +//~^ ERROR `async fn` is not permitted in the 2015 edition +//~| NOTE to use `async fn`, switch to Rust 2018 +//~| HELP set `edition = "2018"` in `Cargo.toml` +//~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide + + let x = async {}; + //~^ ERROR cannot find struct, variant or union type `async` in this scope + //~| NOTE `async` blocks are only allowed in the 2018 edition + let y = async { //~ NOTE `async` blocks are only allowed in the 2018 edition + let x = 42; + //~^ ERROR expected identifier, found keyword `let` + //~| NOTE expected identifier, found keyword + //~| HELP set `edition = "2018"` in `Cargo.toml` + //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide + 42 + }; + let z = async { //~ NOTE `async` blocks are only allowed in the 2018 edition + 42 + //~^ ERROR expected identifier, found `42` + //~| NOTE expected identifier + //~| HELP set `edition = "2018"` in `Cargo.toml` + //~| NOTE for more on editions, read https://doc.rust-lang.org/edition-guide + }; + y.await; + z.await; + x +} + +fn main() {} diff --git a/src/test/ui/editions/async-block-2015.stderr b/src/test/ui/editions/async-block-2015.stderr new file mode 100644 index 0000000000000..8e5e5d8bfab9a --- /dev/null +++ b/src/test/ui/editions/async-block-2015.stderr @@ -0,0 +1,41 @@ +error[E0670]: `async fn` is not permitted in the 2015 edition + --> $DIR/async-block-2015.rs:1:1 + | +LL | async fn foo() { + | ^^^^^ to use `async fn`, switch to Rust 2018 + | + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error: expected identifier, found keyword `let` + --> $DIR/async-block-2015.rs:11:9 + | +LL | let y = async { + | ----- `async` blocks are only allowed in the 2018 edition +LL | let x = 42; + | ^^^ expected identifier, found keyword + | + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error: expected identifier, found `42` + --> $DIR/async-block-2015.rs:19:9 + | +LL | let z = async { + | ----- `async` blocks are only allowed in the 2018 edition +LL | 42 + | ^^ expected identifier + | + = help: set `edition = "2018"` in `Cargo.toml` + = note: for more on editions, read https://doc.rust-lang.org/edition-guide + +error[E0422]: cannot find struct, variant or union type `async` in this scope + --> $DIR/async-block-2015.rs:7:13 + | +LL | let x = async {}; + | ^^^^^ `async` blocks are only allowed in the 2018 edition + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0422, E0670. +For more information about an error, try `rustc --explain E0422`.