Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Detect errors caused by async block in 2015 edition #71783

Merged
merged 1 commit into from
May 8, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
54 changes: 34 additions & 20 deletions src/librustc_parse/parser/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -1068,8 +1068,8 @@ impl<'a> Parser<'a> {
}

fn parse_path_start_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
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) {
Expand All @@ -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))
Expand Down Expand Up @@ -1895,16 +1895,15 @@ impl<'a> Parser<'a> {

fn maybe_parse_struct_expr(
&mut self,
lo: Span,
path: &ast::Path,
attrs: &AttrVec,
) -> Option<PResult<'a, P<Expr>>> {
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);
}
Expand All @@ -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<Expr>> {
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;
Expand All @@ -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
Expand All @@ -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);
Expand All @@ -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 }`.
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_parse/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
2 changes: 1 addition & 1 deletion src/librustc_parse/parser/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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())
Expand Down
6 changes: 5 additions & 1 deletion src/librustc_resolve/late/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
)
Expand Down
27 changes: 9 additions & 18 deletions src/test/ui/async-await/edition-deny-async-fns-2015.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,89 +2,80 @@ 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

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

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

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

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

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

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

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

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

Expand Down
30 changes: 30 additions & 0 deletions src/test/ui/editions/async-block-2015.rs
Original file line number Diff line number Diff line change
@@ -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() {}
41 changes: 41 additions & 0 deletions src/test/ui/editions/async-block-2015.stderr
Original file line number Diff line number Diff line change
@@ -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`.