From 0b7908c550a2db5358a6d82a5bbc93a5fff4cec5 Mon Sep 17 00:00:00 2001 From: A C Date: Mon, 16 Sep 2019 21:45:13 +0100 Subject: Add a UI test for correct parsing --- src/test/ui/parser/stmt_expr_attrs_placement.rs | 22 ++++++++++++++++++++++ .../ui/parser/stmt_expr_attrs_placement.stderr | 10 ++++++++++ 2 files changed, 32 insertions(+) create mode 100644 src/test/ui/parser/stmt_expr_attrs_placement.rs create mode 100644 src/test/ui/parser/stmt_expr_attrs_placement.stderr (limited to 'src/test/ui/parser') diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.rs b/src/test/ui/parser/stmt_expr_attrs_placement.rs new file mode 100644 index 00000000000..b8a794f4b92 --- /dev/null +++ b/src/test/ui/parser/stmt_expr_attrs_placement.rs @@ -0,0 +1,22 @@ +#![feature(stmt_expr_attributes)] + +// Test that various placements of the inner attribute are parsed correctly, +// or not. + +fn main() { + let a = #![allow(warnings)] (1, 2); + //~^ ERROR an inner attribute is not permitted in this context + + let b = (#![allow(warnings)] 1, 2); + + let c = { + #![allow(warnings)] + (#![allow(warnings)] 1, 2) + }; + + let d = { + #![allow(warnings)] + let e = (#![allow(warnings)] 1, 2); + e + }; +} diff --git a/src/test/ui/parser/stmt_expr_attrs_placement.stderr b/src/test/ui/parser/stmt_expr_attrs_placement.stderr new file mode 100644 index 00000000000..1886a0f9ba0 --- /dev/null +++ b/src/test/ui/parser/stmt_expr_attrs_placement.stderr @@ -0,0 +1,10 @@ +error: an inner attribute is not permitted in this context + --> $DIR/stmt_expr_attrs_placement.rs:7:13 + | +LL | let a = #![allow(warnings)] (1, 2); + | ^^^^^^^^^^^^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: aborting due to previous error + -- cgit 1.4.1-3-g733a5 From 4e01b709640d9760758a19ef0dc3732991c14d30 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 3 Dec 2019 13:11:34 +0100 Subject: add recovery to parse_labeled_expr --- src/librustc_parse/parser/expr.rs | 8 +++++--- src/test/ui/parser/recover-labeled-non-block-expr.rs | 5 +++++ .../ui/parser/recover-labeled-non-block-expr.stderr | 17 +++++++++++++++++ 3 files changed, 27 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/parser/recover-labeled-non-block-expr.rs create mode 100644 src/test/ui/parser/recover-labeled-non-block-expr.stderr (limited to 'src/test/ui/parser') diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 923e5d378c5..bfb10dd0f3a 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -1096,9 +1096,11 @@ impl<'a> Parser<'a> { } let msg = "expected `while`, `for`, `loop` or `{` after a label"; - let mut err = self.fatal(msg); - err.span_label(self.token.span, msg); - return Err(err); + self.struct_span_err(self.token.span, msg) + .span_label(self.token.span, msg) + .emit(); + // Continue as an expression in an effort to recover on `'label: non_block_expr`. + self.parse_expr() } /// Returns a string literal if the next token is a string literal. diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.rs b/src/test/ui/parser/recover-labeled-non-block-expr.rs new file mode 100644 index 00000000000..be92170acf0 --- /dev/null +++ b/src/test/ui/parser/recover-labeled-non-block-expr.rs @@ -0,0 +1,5 @@ +fn main() { + 'label: 1 + 1; //~ ERROR expected `while`, `for`, `loop` or `{` after a label + + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/recover-labeled-non-block-expr.stderr b/src/test/ui/parser/recover-labeled-non-block-expr.stderr new file mode 100644 index 00000000000..771a915288c --- /dev/null +++ b/src/test/ui/parser/recover-labeled-non-block-expr.stderr @@ -0,0 +1,17 @@ +error: expected `while`, `for`, `loop` or `{` after a label + --> $DIR/recover-labeled-non-block-expr.rs:2:13 + | +LL | 'label: 1 + 1; + | ^ expected `while`, `for`, `loop` or `{` after a label + +error[E0308]: mismatched types + --> $DIR/recover-labeled-non-block-expr.rs:4:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 2 previous errors + +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 327641e35c10624e7c728fce269885c6e4f6a602 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 3 Dec 2019 13:35:05 +0100 Subject: recover on 'do catch { .. }' --- src/librustc_parse/parser/expr.rs | 44 ++++++++++++++++--------- src/test/ui/parser/do-catch-suggests-try.rs | 7 +++- src/test/ui/parser/do-catch-suggests-try.stderr | 17 +++++++--- 3 files changed, 47 insertions(+), 21 deletions(-) (limited to 'src/test/ui/parser') diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index b4d828ff849..292d277a678 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -876,14 +876,11 @@ impl<'a> Parser<'a> { return self.parse_labeled_expr(label, attrs); } if self.eat_keyword(kw::Loop) { - let lo = self.prev_span; - return self.parse_loop_expr(None, lo, attrs); + return self.parse_loop_expr(None, self.prev_span, attrs); } if self.eat_keyword(kw::Continue) { - let label = self.eat_label(); - let ex = ExprKind::Continue(label); - let hi = self.prev_span; - return Ok(self.mk_expr(lo.to(hi), ex, attrs)); + let kind = ExprKind::Continue(self.eat_label()); + return Ok(self.mk_expr(lo.to(self.prev_span), kind, attrs)); } if self.eat_keyword(kw::Match) { let match_sp = self.prev_span; @@ -893,20 +890,14 @@ impl<'a> Parser<'a> { }); } if self.eat_keyword(kw::Unsafe) { - return self.parse_block_expr( - None, - lo, - BlockCheckMode::Unsafe(ast::UserProvided), - attrs); + let mode = BlockCheckMode::Unsafe(ast::UserProvided); + return self.parse_block_expr(None, lo, mode, attrs); } if self.is_do_catch_block() { - let mut db = self.fatal("found removed `do catch` syntax"); - db.help("following RFC #2388, the new non-placeholder syntax is `try`"); - return Err(db); + return self.recover_do_catch(attrs); } if self.is_try_block() { - let lo = self.token.span; - assert!(self.eat_keyword(kw::Try)); + self.expect_keyword(kw::Try)?; return self.parse_try_block(lo, attrs); } @@ -1104,6 +1095,27 @@ impl<'a> Parser<'a> { self.parse_expr() } + /// Recover on the syntax `do catch { ... }` suggesting `try { ... }` instead. + fn recover_do_catch(&mut self, attrs: ThinVec) -> PResult<'a, P> { + let lo = self.token.span; + + self.bump(); // `do` + self.bump(); // `catch` + + let span_dc = lo.to(self.prev_span); + self.struct_span_err(span_dc, "found removed `do catch` syntax") + .span_suggestion( + span_dc, + "replace with the new syntax", + "try".to_string(), + Applicability::MachineApplicable, + ) + .note("following RFC #2388, the new non-placeholder syntax is `try`") + .emit(); + + self.parse_try_block(lo, attrs) + } + /// Returns a string literal if the next token is a string literal. /// In case of error returns `Some(lit)` if the next token is a literal with a wrong kind, /// and returns `None` if the next token is not literal at all. diff --git a/src/test/ui/parser/do-catch-suggests-try.rs b/src/test/ui/parser/do-catch-suggests-try.rs index d805ab75882..f64568d06e9 100644 --- a/src/test/ui/parser/do-catch-suggests-try.rs +++ b/src/test/ui/parser/do-catch-suggests-try.rs @@ -1,5 +1,10 @@ +#![feature(try_blocks)] + fn main() { let _: Option<()> = do catch {}; //~^ ERROR found removed `do catch` syntax - //~^^ HELP following RFC #2388, the new non-placeholder syntax is `try` + //~| replace with the new syntax + //~| following RFC #2388, the new non-placeholder syntax is `try` + + let _recovery_witness: () = 1; //~ ERROR mismatched types } diff --git a/src/test/ui/parser/do-catch-suggests-try.stderr b/src/test/ui/parser/do-catch-suggests-try.stderr index e151d4cf8a6..cd8907b7eac 100644 --- a/src/test/ui/parser/do-catch-suggests-try.stderr +++ b/src/test/ui/parser/do-catch-suggests-try.stderr @@ -1,10 +1,19 @@ error: found removed `do catch` syntax - --> $DIR/do-catch-suggests-try.rs:2:25 + --> $DIR/do-catch-suggests-try.rs:4:25 | LL | let _: Option<()> = do catch {}; - | ^^ + | ^^^^^^^^ help: replace with the new syntax: `try` | - = help: following RFC #2388, the new non-placeholder syntax is `try` + = note: following RFC #2388, the new non-placeholder syntax is `try` -error: aborting due to previous error +error[E0308]: mismatched types + --> $DIR/do-catch-suggests-try.rs:9:33 + | +LL | let _recovery_witness: () = 1; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 2 previous errors +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From c9e1f13f6eb9d21224c083eb07d894adffc7ec96 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Tue, 3 Dec 2019 18:08:19 +0100 Subject: recover on 'mut', 'var', 'auto' --- src/librustc_parse/parser/stmt.rs | 42 +++++++++++++-- src/libsyntax_pos/symbol.rs | 1 + .../issue-65257-invalid-var-decl-recovery.rs | 21 ++++++++ .../issue-65257-invalid-var-decl-recovery.stderr | 59 ++++++++++++++++++++++ 4 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs create mode 100644 src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr (limited to 'src/test/ui/parser') diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index a0808063116..abee24e2b09 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -14,7 +14,7 @@ use syntax::ast::{AttrVec, Attribute, AttrStyle, VisibilityKind, MacStmtStyle, M use syntax::util::classify; use syntax::token; use syntax_pos::source_map::{respan, Span}; -use syntax_pos::symbol::kw; +use syntax_pos::symbol::{kw, sym, Symbol}; use std::mem; @@ -39,8 +39,20 @@ impl<'a> Parser<'a> { let lo = self.token.span; if self.eat_keyword(kw::Let) { - let local = self.parse_local(attrs.into())?; - return Ok(Some(self.mk_stmt(lo.to(self.prev_span), StmtKind::Local(local)))); + return self.parse_local_mk(lo, attrs.into()).map(Some) + } + if self.is_kw_followed_by_ident(kw::Mut) { + return self.recover_stmt_local(lo, attrs.into(), "missing `let`", "let mut"); + } + if self.is_kw_followed_by_ident(kw::Auto) { + self.bump(); // `auto` + let msg = "to introduce a variable, write `let` instead of `auto`"; + return self.recover_stmt_local(lo, attrs.into(), msg, "let"); + } + if self.is_kw_followed_by_ident(sym::var) { + self.bump(); // `var` + let msg = "to introduce a variable, write `let` instead of `var`"; + return self.recover_stmt_local(lo, attrs.into(), msg, "let"); } let mac_vis = respan(lo, VisibilityKind::Inherited); @@ -189,6 +201,30 @@ impl<'a> Parser<'a> { } } + fn is_kw_followed_by_ident(&self, kw: Symbol) -> bool { + self.token.is_keyword(kw) + && self.look_ahead(1, |t| t.is_ident() && !t.is_reserved_ident()) + } + + fn recover_stmt_local( + &mut self, + span: Span, + attrs: AttrVec, + msg: &str, + sugg: &str, + ) -> PResult<'a, Option> { + let stmt = self.parse_local_mk(span, attrs)?; + self.struct_span_err(stmt.span, "invalid variable declaration") + .span_suggestion_short(span, msg, sugg.to_string(), Applicability::MachineApplicable) + .emit(); + Ok(Some(stmt)) + } + + fn parse_local_mk(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, Stmt> { + let local = self.parse_local(attrs.into())?; + Ok(self.mk_stmt(lo.to(self.prev_span), StmtKind::Local(local))) + } + /// Parses a local variable declaration. fn parse_local(&mut self, attrs: AttrVec) -> PResult<'a, P> { let lo = self.prev_span; diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index ae34064c926..d3e80fc4fdd 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -773,6 +773,7 @@ symbols! { usize, v1, val, + var, vec, Vec, vis, diff --git a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs new file mode 100644 index 00000000000..7efc4174874 --- /dev/null +++ b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs @@ -0,0 +1,21 @@ +fn main() { + auto n = 0;//~ ERROR invalid variable declaration + //~^ HELP to introduce a variable, write `let` instead of `auto` + auto m;//~ ERROR invalid variable declaration + //~^ HELP to introduce a variable, write `let` instead of `auto` + m = 0; + + var n = 0;//~ ERROR invalid variable declaration + //~^ HELP to introduce a variable, write `let` instead of `var` + var m;//~ ERROR invalid variable declaration + //~^ HELP to introduce a variable, write `let` instead of `var` + m = 0; + + mut n = 0;//~ ERROR invalid variable declaration + //~^ HELP missing `let` + mut var;//~ ERROR invalid variable declaration + //~^ HELP missing `let` + var = 0; + + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr new file mode 100644 index 00000000000..429c12265bd --- /dev/null +++ b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr @@ -0,0 +1,59 @@ +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:2:5 + | +LL | auto n = 0; + | ----^^^^^^ + | | + | help: to introduce a variable, write `let` instead of `auto` + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:4:5 + | +LL | auto m; + | ----^^ + | | + | help: to introduce a variable, write `let` instead of `auto` + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:8:5 + | +LL | var n = 0; + | ---^^^^^^ + | | + | help: to introduce a variable, write `let` instead of `var` + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:10:5 + | +LL | var m; + | ---^^ + | | + | help: to introduce a variable, write `let` instead of `var` + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:14:5 + | +LL | mut n = 0; + | ---^^^^^^ + | | + | help: missing `let` + +error: invalid variable declaration + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:16:5 + | +LL | mut var; + | ---^^^^ + | | + | help: missing `let` + +error[E0308]: mismatched types + --> $DIR/issue-65257-invalid-var-decl-recovery.rs:20:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 7 previous errors + +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 66470d3217f27b5950c38a3af4a99e0ef12fa2c8 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 4 Dec 2019 08:10:41 +0100 Subject: recover `#[attr] if expr {}` --- src/librustc_parse/lib.rs | 1 + src/librustc_parse/parser/expr.rs | 17 +++++++------ src/test/ui/parser/recovery-attr-on-if.rs | 9 +++++++ src/test/ui/parser/recovery-attr-on-if.stderr | 35 +++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 8 deletions(-) create mode 100644 src/test/ui/parser/recovery-attr-on-if.rs create mode 100644 src/test/ui/parser/recovery-attr-on-if.stderr (limited to 'src/test/ui/parser') diff --git a/src/librustc_parse/lib.rs b/src/librustc_parse/lib.rs index 58c36524380..3de7f888724 100644 --- a/src/librustc_parse/lib.rs +++ b/src/librustc_parse/lib.rs @@ -2,6 +2,7 @@ #![feature(bool_to_option)] #![feature(crate_visibility_modifier)] +#![feature(slice_patterns)] use syntax::ast; use syntax::print::pprust; diff --git a/src/librustc_parse/parser/expr.rs b/src/librustc_parse/parser/expr.rs index 7afa3d665d6..68af34a36bb 100644 --- a/src/librustc_parse/parser/expr.rs +++ b/src/librustc_parse/parser/expr.rs @@ -668,19 +668,20 @@ impl<'a> Parser<'a> { expr.map(|mut expr| { attrs.extend::>(expr.attrs.into()); expr.attrs = attrs; - match expr.kind { - ExprKind::If(..) if !expr.attrs.is_empty() => { - // Just point to the first attribute in there... - let span = expr.attrs[0].span; - self.span_err(span, "attributes are not yet allowed on `if` expressions"); - } - _ => {} - } + self.error_attr_on_if_expr(&expr); expr }) ) } + fn error_attr_on_if_expr(&self, expr: &Expr) { + if let (ExprKind::If(..), [a0, ..]) = (&expr.kind, &*expr.attrs) { + // Just point to the first attribute in there... + self.struct_span_err(a0.span, "attributes are not yet allowed on `if` expressions") + .emit(); + } + } + fn parse_dot_or_call_expr_with_(&mut self, e0: P, lo: Span) -> PResult<'a, P> { let mut e = e0; let mut hi; diff --git a/src/test/ui/parser/recovery-attr-on-if.rs b/src/test/ui/parser/recovery-attr-on-if.rs new file mode 100644 index 00000000000..0d1f5be7b49 --- /dev/null +++ b/src/test/ui/parser/recovery-attr-on-if.rs @@ -0,0 +1,9 @@ +fn main() { + #[attr] if true {}; + //~^ ERROR cannot find attribute + //~| ERROR attributes are not yet allowed on `if` expressions + #[attr] if true {}; + //~^ ERROR cannot find attribute + //~| ERROR attributes are not yet allowed on `if` expressions + let _recovery_witness: () = 0; //~ ERROR mismatched types +} diff --git a/src/test/ui/parser/recovery-attr-on-if.stderr b/src/test/ui/parser/recovery-attr-on-if.stderr new file mode 100644 index 00000000000..a02846827c9 --- /dev/null +++ b/src/test/ui/parser/recovery-attr-on-if.stderr @@ -0,0 +1,35 @@ +error: attributes are not yet allowed on `if` expressions + --> $DIR/recovery-attr-on-if.rs:2:5 + | +LL | #[attr] if true {}; + | ^^^^^^^ + +error: attributes are not yet allowed on `if` expressions + --> $DIR/recovery-attr-on-if.rs:5:5 + | +LL | #[attr] if true {}; + | ^^^^^^^ + +error: cannot find attribute `attr` in this scope + --> $DIR/recovery-attr-on-if.rs:5:7 + | +LL | #[attr] if true {}; + | ^^^^ + +error: cannot find attribute `attr` in this scope + --> $DIR/recovery-attr-on-if.rs:2:7 + | +LL | #[attr] if true {}; + | ^^^^ + +error[E0308]: mismatched types + --> $DIR/recovery-attr-on-if.rs:8:33 + | +LL | let _recovery_witness: () = 0; + | -- ^ expected `()`, found integer + | | + | expected due to this + +error: aborting due to 5 previous errors + +For more information about this error, try `rustc --explain E0308`. -- cgit 1.4.1-3-g733a5 From 19db2d2fed4de5a480bca028458f616d21db92a9 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 4 Dec 2019 11:16:12 +0100 Subject: ast_stmt_expr_attr -> pretty & ui tests --- src/test/pretty/ast-stmt-expr-attr.rs | 175 +++++++++ src/test/ui-fulldeps/ast_stmt_expr_attr.rs | 311 ---------------- src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs | 2 + .../ui/parser/attr-stmt-expr-attr-bad-2.stderr | 8 + src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs | 2 + .../ui/parser/attr-stmt-expr-attr-bad-3.stderr | 8 + src/test/ui/parser/attr-stmt-expr-attr-bad.rs | 107 ++++++ src/test/ui/parser/attr-stmt-expr-attr-bad.stderr | 390 +++++++++++++++++++++ 8 files changed, 692 insertions(+), 311 deletions(-) create mode 100644 src/test/pretty/ast-stmt-expr-attr.rs delete mode 100644 src/test/ui-fulldeps/ast_stmt_expr_attr.rs create mode 100644 src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs create mode 100644 src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr create mode 100644 src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs create mode 100644 src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr create mode 100644 src/test/ui/parser/attr-stmt-expr-attr-bad.rs create mode 100644 src/test/ui/parser/attr-stmt-expr-attr-bad.stderr (limited to 'src/test/ui/parser') diff --git a/src/test/pretty/ast-stmt-expr-attr.rs b/src/test/pretty/ast-stmt-expr-attr.rs new file mode 100644 index 00000000000..5b975424512 --- /dev/null +++ b/src/test/pretty/ast-stmt-expr-attr.rs @@ -0,0 +1,175 @@ +// pp-exact + +fn main() { } + +#[cfg(FALSE)] +fn syntax() { + let _ = #[attr] box 0; + let _ = #[attr] [#![attr] ]; + let _ = #[attr] [#![attr] 0]; + let _ = #[attr] [#![attr] 0; 0]; + let _ = #[attr] [#![attr] 0, 0, 0]; + let _ = #[attr] foo(); + let _ = #[attr] x.foo(); + let _ = #[attr] (#![attr] ); + let _ = #[attr] (#![attr] #[attr] 0,); + let _ = #[attr] (#![attr] #[attr] 0, 0); + let _ = #[attr] 0 + #[attr] 0; + let _ = #[attr] 0 / #[attr] 0; + let _ = #[attr] 0 & #[attr] 0; + let _ = #[attr] 0 % #[attr] 0; + let _ = #[attr] (0 + 0); + let _ = #[attr] !0; + let _ = #[attr] -0; + let _ = #[attr] false; + let _ = #[attr] 0; + let _ = #[attr] 'c'; + let _ = #[attr] x as Y; + let _ = #[attr] (x as Y); + let _ = + #[attr] while true { + #![attr] + }; + let _ = + #[attr] while let Some(false) = true { + #![attr] + }; + let _ = + #[attr] for x in y { + #![attr] + }; + let _ = + #[attr] loop { + #![attr] + }; + let _ = + #[attr] match true { + #![attr] + #[attr] + _ => false, + }; + let _ = #[attr] || #[attr] foo; + let _ = #[attr] move || #[attr] foo; + let _ = + #[attr] || + #[attr] { + #![attr] + foo + }; + let _ = + #[attr] move || + #[attr] { + #![attr] + foo + }; + let _ = + #[attr] || + { + #![attr] + foo + }; + let _ = + #[attr] move || + { + #![attr] + foo + }; + let _ = + #[attr] { + #![attr] + }; + let _ = + #[attr] { + #![attr] + let _ = (); + }; + let _ = + #[attr] { + #![attr] + let _ = (); + foo + }; + let _ = #[attr] x = y; + let _ = #[attr] (x = y); + let _ = #[attr] x += y; + let _ = #[attr] (x += y); + let _ = #[attr] foo.bar; + let _ = (#[attr] foo).bar; + let _ = #[attr] foo.0; + let _ = (#[attr] foo).0; + let _ = #[attr] foo[bar]; + let _ = (#[attr] foo)[bar]; + let _ = #[attr] 0..#[attr] 0; + let _ = #[attr] 0..; + let _ = #[attr] (0..0); + let _ = #[attr] (0..); + let _ = #[attr] (..0); + let _ = #[attr] (..); + let _ = #[attr] foo::bar::baz; + let _ = #[attr] &0; + let _ = #[attr] &mut 0; + let _ = #[attr] &#[attr] 0; + let _ = #[attr] &mut #[attr] 0; + let _ = #[attr] break ; + let _ = #[attr] continue ; + let _ = #[attr] return; + let _ = #[attr] foo!(); + let _ = #[attr] foo!(# ! [attr]); + let _ = #[attr] foo![]; + let _ = #[attr] foo![# ! [attr]]; + let _ = #[attr] foo! { }; + let _ = #[attr] foo! { # ! [attr] }; + let _ = #[attr] Foo{#![attr] bar: baz,}; + let _ = #[attr] Foo{#![attr] ..foo}; + let _ = #[attr] Foo{#![attr] bar: baz, ..foo}; + let _ = #[attr] (#![attr] 0); + + { + #[attr] + let _ = 0; + + #[attr] + 0; + + #[attr] + foo!(); + + #[attr] + foo! { } + + #[attr] + foo![]; + } + + { + #[attr] + let _ = 0; + } + { + + #[attr] + 0 + } + { + + #[attr] + { + #![attr] + } + } + { + + #[attr] + foo!() + } + { + + #[attr] + foo![] + } + { + + #[attr] + foo! { } + } +} diff --git a/src/test/ui-fulldeps/ast_stmt_expr_attr.rs b/src/test/ui-fulldeps/ast_stmt_expr_attr.rs deleted file mode 100644 index d6d49df63ef..00000000000 --- a/src/test/ui-fulldeps/ast_stmt_expr_attr.rs +++ /dev/null @@ -1,311 +0,0 @@ -// run-pass - -#![allow(unused_imports)] -// ignore-cross-compile - -#![feature(rustc_private)] - -extern crate syntax; -extern crate syntax_expand; -extern crate rustc_parse; -extern crate rustc_errors; - -use rustc_errors::PResult; -use rustc_parse::parser::attr::*; -use rustc_parse::new_parser_from_source_str; -use rustc_parse::parser::Parser; -use syntax::ast::*; -use syntax::attr::*; -use syntax::ast; -use syntax::sess::ParseSess; -use syntax::source_map::{FilePathMapping, FileName}; -use syntax::ptr::P; -use syntax::print::pprust; -use syntax::token; -use std::fmt; - -// Copied out of syntax::util::parser_testing - -pub fn string_to_parser<'a>(ps: &'a ParseSess, source_str: String) -> Parser<'a> { - new_parser_from_source_str(ps, FileName::Custom(source_str.clone()), source_str) -} - -fn with_error_checking_parse<'a, T, F>(s: String, ps: &'a ParseSess, f: F) -> PResult<'a, T> where - F: FnOnce(&mut Parser<'a>) -> PResult<'a, T>, -{ - let mut p = string_to_parser(&ps, s); - let x = f(&mut p); - - if ps.span_diagnostic.has_errors() || p.token != token::Eof { - if let Err(mut e) = x { - e.cancel(); - } - return Err(p.fatal("parse error")); - } - - x -} - -fn expr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, P> { - with_error_checking_parse(s.to_string(), ps, |p| { - p.parse_expr() - }) -} - -fn stmt<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Stmt> { - with_error_checking_parse(s.to_string(), ps, |p| { - p.parse_stmt().map(|s| s.unwrap()) - }) -} - -fn attr<'a>(s: &str, ps: &'a ParseSess) -> PResult<'a, ast::Attribute> { - with_error_checking_parse(s.to_string(), ps, |p| { - p.parse_attribute(true) - }) -} - -fn str_compare String>(e: &str, expected: &[T], actual: &[T], f: F) { - let expected: Vec<_> = expected.iter().map(|e| f(e)).collect(); - let actual: Vec<_> = actual.iter().map(|e| f(e)).collect(); - - if expected != actual { - panic!("parsed `{}` as {:?}, expected {:?}", e, actual, expected); - } -} - -fn sess() -> ParseSess { - ParseSess::new(FilePathMapping::empty()) -} - -fn check_expr_attrs(es: &str, expected: &[&str]) { - let ps = sess(); - let e = expr(es, &ps).expect("parse error"); - let actual = &e.attrs; - str_compare(es, - &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::>(), - &actual, - pprust::attribute_to_string); -} - -fn check_stmt_attrs(es: &str, expected: &[&str]) { - let ps = sess(); - let e = stmt(es, &ps).expect("parse error"); - let actual = e.kind.attrs(); - str_compare(es, - &expected.iter().map(|r| attr(r, &ps).unwrap()).collect::>(), - actual, - pprust::attribute_to_string); -} - -fn reject_expr_parse(es: &str) { - let ps = sess(); - match expr(es, &ps) { - Ok(_) => panic!("parser did not reject `{}`", es), - Err(mut e) => e.cancel(), - }; -} - -fn reject_stmt_parse(es: &str) { - let ps = sess(); - match stmt(es, &ps) { - Ok(_) => panic!("parser did not reject `{}`", es), - Err(mut e) => e.cancel(), - }; -} - -fn main() { - syntax::with_default_globals(|| run()); -} - -fn run() { - let both = &["#[attr]", "#![attr]"]; - let outer = &["#[attr]"]; - let none = &[]; - - check_expr_attrs("#[attr] box 0", outer); - reject_expr_parse("box #![attr] 0"); - - check_expr_attrs("#[attr] [#![attr]]", both); - check_expr_attrs("#[attr] [#![attr] 0]", both); - check_expr_attrs("#[attr] [#![attr] 0; 0]", both); - check_expr_attrs("#[attr] [#![attr] 0, 0, 0]", both); - reject_expr_parse("[#[attr]]"); - - check_expr_attrs("#[attr] foo()", outer); - check_expr_attrs("#[attr] x.foo()", outer); - reject_expr_parse("foo#[attr]()"); - reject_expr_parse("foo(#![attr])"); - reject_expr_parse("x.foo(#![attr])"); - reject_expr_parse("x.#[attr]foo()"); - reject_expr_parse("x.#![attr]foo()"); - - check_expr_attrs("#[attr] (#![attr])", both); - check_expr_attrs("#[attr] (#![attr] #[attr] 0,)", both); - check_expr_attrs("#[attr] (#![attr] #[attr] 0, 0)", both); - - check_expr_attrs("#[attr] 0 + #[attr] 0", none); - check_expr_attrs("#[attr] 0 / #[attr] 0", none); - check_expr_attrs("#[attr] 0 & #[attr] 0", none); - check_expr_attrs("#[attr] 0 % #[attr] 0", none); - check_expr_attrs("#[attr] (0 + 0)", outer); - reject_expr_parse("0 + #![attr] 0"); - - check_expr_attrs("#[attr] !0", outer); - check_expr_attrs("#[attr] -0", outer); - reject_expr_parse("!#![attr] 0"); - reject_expr_parse("-#![attr] 0"); - - check_expr_attrs("#[attr] false", outer); - check_expr_attrs("#[attr] 0", outer); - check_expr_attrs("#[attr] 'c'", outer); - - check_expr_attrs("#[attr] x as Y", none); - check_expr_attrs("#[attr] (x as Y)", outer); - reject_expr_parse("x #![attr] as Y"); - - reject_expr_parse("#[attr] if false {}"); - reject_expr_parse("if false #[attr] {}"); - reject_expr_parse("if false {#![attr]}"); - reject_expr_parse("if false {} #[attr] else {}"); - reject_expr_parse("if false {} else #[attr] {}"); - reject_expr_parse("if false {} else {#![attr]}"); - reject_expr_parse("if false {} else #[attr] if true {}"); - reject_expr_parse("if false {} else if true #[attr] {}"); - reject_expr_parse("if false {} else if true {#![attr]}"); - - reject_expr_parse("#[attr] if let Some(false) = false {}"); - reject_expr_parse("if let Some(false) = false #[attr] {}"); - reject_expr_parse("if let Some(false) = false {#![attr]}"); - reject_expr_parse("if let Some(false) = false {} #[attr] else {}"); - reject_expr_parse("if let Some(false) = false {} else #[attr] {}"); - reject_expr_parse("if let Some(false) = false {} else {#![attr]}"); - reject_expr_parse("if let Some(false) = false {} else #[attr] if let Some(false) = true {}"); - reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true #[attr] {}"); - reject_expr_parse("if let Some(false) = false {} else if let Some(false) = true {#![attr]}"); - - check_expr_attrs("#[attr] while true {#![attr]}", both); - - check_expr_attrs("#[attr] while let Some(false) = true {#![attr]}", both); - - check_expr_attrs("#[attr] for x in y {#![attr]}", both); - - check_expr_attrs("#[attr] loop {#![attr]}", both); - - check_expr_attrs("#[attr] match true {#![attr] #[attr] _ => false}", both); - - check_expr_attrs("#[attr] || #[attr] foo", outer); - check_expr_attrs("#[attr] move || #[attr] foo", outer); - check_expr_attrs("#[attr] || #[attr] { #![attr] foo }", outer); - check_expr_attrs("#[attr] move || #[attr] { #![attr] foo }", outer); - check_expr_attrs("#[attr] || { #![attr] foo }", outer); - check_expr_attrs("#[attr] move || { #![attr] foo }", outer); - reject_expr_parse("|| #![attr] foo"); - reject_expr_parse("move || #![attr] foo"); - reject_expr_parse("|| #![attr] {foo}"); - reject_expr_parse("move || #![attr] {foo}"); - - check_expr_attrs("#[attr] { #![attr] }", both); - check_expr_attrs("#[attr] { #![attr] let _ = (); }", both); - check_expr_attrs("#[attr] { #![attr] let _ = (); foo }", both); - - check_expr_attrs("#[attr] x = y", none); - check_expr_attrs("#[attr] (x = y)", outer); - - check_expr_attrs("#[attr] x += y", none); - check_expr_attrs("#[attr] (x += y)", outer); - - check_expr_attrs("#[attr] foo.bar", outer); - check_expr_attrs("(#[attr] foo).bar", none); - - check_expr_attrs("#[attr] foo.0", outer); - check_expr_attrs("(#[attr] foo).0", none); - - check_expr_attrs("#[attr] foo[bar]", outer); - check_expr_attrs("(#[attr] foo)[bar]", none); - - check_expr_attrs("#[attr] 0..#[attr] 0", none); - check_expr_attrs("#[attr] 0..", none); - reject_expr_parse("#[attr] ..#[attr] 0"); - reject_expr_parse("#[attr] .."); - - check_expr_attrs("#[attr] (0..0)", outer); - check_expr_attrs("#[attr] (0..)", outer); - check_expr_attrs("#[attr] (..0)", outer); - check_expr_attrs("#[attr] (..)", outer); - - check_expr_attrs("#[attr] foo::bar::baz", outer); - - check_expr_attrs("#[attr] &0", outer); - check_expr_attrs("#[attr] &mut 0", outer); - check_expr_attrs("#[attr] & #[attr] 0", outer); - check_expr_attrs("#[attr] &mut #[attr] 0", outer); - reject_expr_parse("#[attr] &#![attr] 0"); - reject_expr_parse("#[attr] &mut #![attr] 0"); - - check_expr_attrs("#[attr] break", outer); - check_expr_attrs("#[attr] continue", outer); - check_expr_attrs("#[attr] return", outer); - - check_expr_attrs("#[attr] foo!()", outer); - check_expr_attrs("#[attr] foo!(#![attr])", outer); - check_expr_attrs("#[attr] foo![]", outer); - check_expr_attrs("#[attr] foo![#![attr]]", outer); - check_expr_attrs("#[attr] foo!{}", outer); - check_expr_attrs("#[attr] foo!{#![attr]}", outer); - - check_expr_attrs("#[attr] Foo { #![attr] bar: baz }", both); - check_expr_attrs("#[attr] Foo { #![attr] ..foo }", both); - check_expr_attrs("#[attr] Foo { #![attr] bar: baz, ..foo }", both); - - check_expr_attrs("#[attr] (#![attr] 0)", both); - - // Look at statements in their natural habitat... - check_expr_attrs("{ - #[attr] let _ = 0; - #[attr] 0; - #[attr] foo!(); - #[attr] foo!{} - #[attr] foo![]; - }", none); - - check_stmt_attrs("#[attr] let _ = 0", outer); - check_stmt_attrs("#[attr] 0", outer); - check_stmt_attrs("#[attr] {#![attr]}", both); - check_stmt_attrs("#[attr] foo!()", outer); - check_stmt_attrs("#[attr] foo![]", outer); - check_stmt_attrs("#[attr] foo!{}", outer); - - reject_stmt_parse("#[attr] #![attr] let _ = 0"); - reject_stmt_parse("#[attr] #![attr] 0"); - reject_stmt_parse("#[attr] #![attr] foo!()"); - reject_stmt_parse("#[attr] #![attr] foo![]"); - reject_stmt_parse("#[attr] #![attr] foo!{}"); - - // FIXME: Allow attributes in pattern constexprs? - // note: requires parens in patterns to allow disambiguation - - reject_expr_parse("match 0 { - 0..=#[attr] 10 => () - }"); - reject_expr_parse("match 0 { - 0..=#[attr] -10 => () - }"); - reject_expr_parse("match 0 { - 0..=-#[attr] 10 => () - }"); - reject_expr_parse("match 0 { - 0..=#[attr] FOO => () - }"); - - // make sure we don't catch this bug again... - reject_expr_parse("{ - fn foo() { - #[attr]; - } - }"); - reject_expr_parse("{ - fn foo() { - #[attr] - } - }"); -} diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs new file mode 100644 index 00000000000..e5ac59ae463 --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.rs @@ -0,0 +1,2 @@ +#[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } +//~^ ERROR unexpected token: `#` diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr new file mode 100644 index 00000000000..ca1043250ba --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-2.stderr @@ -0,0 +1,8 @@ +error: unexpected token: `#` + --> $DIR/attr-stmt-expr-attr-bad-2.rs:1:34 + | +LL | #[cfg(FALSE)] fn e() { let _ = x.#![attr]foo(); } + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs new file mode 100644 index 00000000000..7dc71af52f4 --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.rs @@ -0,0 +1,2 @@ +#[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } +//~^ ERROR unexpected token: `#` diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr new file mode 100644 index 00000000000..ab9366d042a --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad-3.stderr @@ -0,0 +1,8 @@ +error: unexpected token: `#` + --> $DIR/attr-stmt-expr-attr-bad-3.rs:1:34 + | +LL | #[cfg(FALSE)] fn e() { let _ = x.#[attr]foo(); } + | ^ + +error: aborting due to previous error + diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.rs b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs new file mode 100644 index 00000000000..ef10010ed0e --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.rs @@ -0,0 +1,107 @@ +fn main() {} + +#[cfg(FALSE)] fn e() { let _ = box #![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = [#[attr]]; } +//~^ ERROR expected expression, found `]` +#[cfg(FALSE)] fn e() { let _ = foo#[attr](); } +//~^ ERROR expected one of +#[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } +//~^ ERROR an inner attribute is not permitted in this context +//~| ERROR expected expression, found `)` +#[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } +//~^ ERROR an inner attribute is not permitted in this context +//~| ERROR expected expression, found `)` +#[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; } +//~^ ERROR expected one of +#[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } +//~^ ERROR expected expression, found `..` +#[cfg(FALSE)] fn e() { let _ = #[attr] ..; } +//~^ ERROR expected expression, found `..` +#[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; } +//~^ ERROR attributes are not yet allowed on `if` expressions +#[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } +//~^ ERROR expected one of +#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } +//~^ ERROR attributes are not yet allowed on `if` expressions +//~| ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; } +//~^ ERROR attributes are not yet allowed on `if` expressions +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } +//~^ ERROR expected one of +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } +//~^ ERROR attributes are not yet allowed on `if` expressions +//~| ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } +//~^ ERROR expected `{`, found `#` +#[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } +//~^ ERROR an inner attribute is not permitted in this context + +#[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } +//~^ ERROR an inner attribute is not permitted following an outer attribute +#[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } +//~^ ERROR an inner attribute is not permitted following an outer attribute +#[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } +//~^ ERROR an inner attribute is not permitted following an outer attribute +#[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } +//~^ ERROR an inner attribute is not permitted following an outer attribute +#[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } +//~^ ERROR an inner attribute is not permitted following an outer attribute + +// FIXME: Allow attributes in pattern constexprs? +// note: requires parens in patterns to allow disambiguation + +#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } +//~^ ERROR `X..=` range patterns are not supported +//~| ERROR expected one of `=>`, `if`, or `|`, found `#` +#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } +//~^ ERROR `X..=` range patterns are not supported +//~| ERROR expected one of `=>`, `if`, or `|`, found `#` +#[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } } +//~^ ERROR unexpected token: `#` +#[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } +//~^ ERROR `X..=` range patterns are not supported +//~| ERROR expected one of `=>`, `if`, or `|`, found `#` + +// make sure we don't catch this bug again... +#[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } } +//~^ ERROR expected statement after outer attribute +#[cfg(FALSE)] fn e() { { fn foo() { #[attr] } } } diff --git a/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr new file mode 100644 index 00000000000..9a0d3176714 --- /dev/null +++ b/src/test/ui/parser/attr-stmt-expr-attr-bad.stderr @@ -0,0 +1,390 @@ +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:3:36 + | +LL | #[cfg(FALSE)] fn e() { let _ = box #![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected expression, found `]` + --> $DIR/attr-stmt-expr-attr-bad.rs:5:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = [#[attr]]; } + | ^ expected expression + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:7:35 + | +LL | #[cfg(FALSE)] fn e() { let _ = foo#[attr](); } + | ^ expected one of 7 possible tokens + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:9:36 + | +LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected expression, found `)` + --> $DIR/attr-stmt-expr-attr-bad.rs:9:44 + | +LL | #[cfg(FALSE)] fn e() { let _ = foo(#![attr]); } + | ^ expected expression + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:12:38 + | +LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected expression, found `)` + --> $DIR/attr-stmt-expr-attr-bad.rs:12:46 + | +LL | #[cfg(FALSE)] fn e() { let _ = x.foo(#![attr]); } + | ^ expected expression + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:15:36 + | +LL | #[cfg(FALSE)] fn e() { let _ = 0 + #![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:17:33 + | +LL | #[cfg(FALSE)] fn e() { let _ = !#![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:19:33 + | +LL | #[cfg(FALSE)] fn e() { let _ = -#![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected one of `!`, `.`, `::`, `;`, `?`, `{`, or an operator, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:21:34 + | +LL | #[cfg(FALSE)] fn e() { let _ = x #![attr] as Y; } + | ^ expected one of 7 possible tokens + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:23:35 + | +LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] foo; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:25:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] foo; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:27:35 + | +LL | #[cfg(FALSE)] fn e() { let _ = || #![attr] {foo}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:29:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = move || #![attr] {foo}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected expression, found `..` + --> $DIR/attr-stmt-expr-attr-bad.rs:31:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..#[attr] 0; } + | ^^ expected expression + +error: expected expression, found `..` + --> $DIR/attr-stmt-expr-attr-bad.rs:33:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] ..; } + | ^^ expected expression + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:35:41 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &#![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:37:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] &mut #![attr] 0; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: attributes are not yet allowed on `if` expressions + --> $DIR/attr-stmt-expr-attr-bad.rs:39:32 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if 0 {}; } + | ^^^^^^^ + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:41:37 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 #[attr] {}; } + | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | | | + | | expected `{` + | this `if` statement has a condition, but no block + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:43:38 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:45:40 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} #[attr] else {}; } + | ^ expected one of `.`, `;`, `?`, `else`, or an operator + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:47:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] {}; } + | ^ --- help: try placing this code inside a block: `{ {}; }` + | | + | expected `{` + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:49:46 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: attributes are not yet allowed on `if` expressions + --> $DIR/attr-stmt-expr-attr-bad.rs:51:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } + | ^^^^^^^ + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:51:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else #[attr] if 0 {}; } + | ^ -------- help: try placing this code inside a block: `{ if 0 {}; }` + | | + | expected `{` + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:54:50 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 #[attr] {}; } + | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | | | + | | expected `{` + | this `if` statement has a condition, but no block + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:56:51 + | +LL | #[cfg(FALSE)] fn e() { let _ = if 0 {} else if 0 {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: attributes are not yet allowed on `if` expressions + --> $DIR/attr-stmt-expr-attr-bad.rs:58:32 + | +LL | #[cfg(FALSE)] fn e() { let _ = #[attr] if let _ = 0 {}; } + | ^^^^^^^ + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:60:45 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 #[attr] {}; } + | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | | | + | | expected `{` + | this `if` statement has a condition, but no block + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:62:46 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: expected one of `.`, `;`, `?`, `else`, or an operator, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:64:48 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} #[attr] else {}; } + | ^ expected one of `.`, `;`, `?`, `else`, or an operator + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:66:53 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] {}; } + | ^ --- help: try placing this code inside a block: `{ {}; }` + | | + | expected `{` + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:68:54 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: attributes are not yet allowed on `if` expressions + --> $DIR/attr-stmt-expr-attr-bad.rs:70:53 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } + | ^^^^^^^ + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:70:53 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else #[attr] if let _ = 0 {}; } + | ^ ---------------- help: try placing this code inside a block: `{ if let _ = 0 {}; }` + | | + | expected `{` + +error: expected `{`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:73:66 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 #[attr] {}; } + | -- ^ --- help: try placing this code inside a block: `{ {}; }` + | | | + | | expected `{` + | this `if` statement has a condition, but no block + +error: an inner attribute is not permitted in this context + --> $DIR/attr-stmt-expr-attr-bad.rs:75:67 + | +LL | #[cfg(FALSE)] fn e() { let _ = if let _ = 0 {} else if let _ = 0 {#![attr]}; } + | ^^^^^^^^ + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:78:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] let _ = 0; } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:80:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] 0; } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:82:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!(); } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:84:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo![]; } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: an inner attribute is not permitted following an outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:86:32 + | +LL | #[cfg(FALSE)] fn s() { #[attr] #![attr] foo!{}; } + | ------- ^^^^^^^^ not permitted following an outer attibute + | | + | previous outer attribute + | + = note: inner attributes, like `#![no_std]`, annotate the item enclosing them, and are usually found at the beginning of source files. Outer attributes, like `#[test]`, annotate the item following them. + +error: `X..=` range patterns are not supported + --> $DIR/attr-stmt-expr-attr-bad.rs:92:34 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } + | ^^^^ help: try using the maximum value for the type: `0..=MAX` + +error: expected one of `=>`, `if`, or `|`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:92:38 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] 10 => () } } + | ^ expected one of `=>`, `if`, or `|` + +error: `X..=` range patterns are not supported + --> $DIR/attr-stmt-expr-attr-bad.rs:95:34 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } + | ^^^^ help: try using the maximum value for the type: `0..=MAX` + +error: expected one of `=>`, `if`, or `|`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:95:38 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] -10 => () } } + | ^ expected one of `=>`, `if`, or `|` + +error: unexpected token: `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:98:39 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=-#[attr] 10 => () } } + | ^ + +error: `X..=` range patterns are not supported + --> $DIR/attr-stmt-expr-attr-bad.rs:100:34 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } + | ^^^^ help: try using the maximum value for the type: `0..=MAX` + +error: expected one of `=>`, `if`, or `|`, found `#` + --> $DIR/attr-stmt-expr-attr-bad.rs:100:38 + | +LL | #[cfg(FALSE)] fn e() { match 0 { 0..=#[attr] FOO => () } } + | ^ expected one of `=>`, `if`, or `|` + +error: expected statement after outer attribute + --> $DIR/attr-stmt-expr-attr-bad.rs:105:44 + | +LL | #[cfg(FALSE)] fn e() { { fn foo() { #[attr]; } } } + | ^ + +error: aborting due to 52 previous errors + -- cgit 1.4.1-3-g733a5 From 621661f8a63f2118f3add5c3d686d9a2b6f62e5e Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Wed, 4 Dec 2019 12:15:01 +0100 Subject: tweak var/auto/mut recovery --- src/librustc_parse/parser/stmt.rs | 14 +++---- .../issue-65257-invalid-var-decl-recovery.rs | 12 +++--- .../issue-65257-invalid-var-decl-recovery.stderr | 44 +++++++++++++--------- 3 files changed, 39 insertions(+), 31 deletions(-) (limited to 'src/test/ui/parser') diff --git a/src/librustc_parse/parser/stmt.rs b/src/librustc_parse/parser/stmt.rs index abee24e2b09..42d85e96aef 100644 --- a/src/librustc_parse/parser/stmt.rs +++ b/src/librustc_parse/parser/stmt.rs @@ -42,16 +42,16 @@ impl<'a> Parser<'a> { return self.parse_local_mk(lo, attrs.into()).map(Some) } if self.is_kw_followed_by_ident(kw::Mut) { - return self.recover_stmt_local(lo, attrs.into(), "missing `let`", "let mut"); + return self.recover_stmt_local(lo, attrs.into(), "missing keyword", "let mut"); } if self.is_kw_followed_by_ident(kw::Auto) { self.bump(); // `auto` - let msg = "to introduce a variable, write `let` instead of `auto`"; + let msg = "write `let` instead of `auto` to introduce a new variable"; return self.recover_stmt_local(lo, attrs.into(), msg, "let"); } if self.is_kw_followed_by_ident(sym::var) { self.bump(); // `var` - let msg = "to introduce a variable, write `let` instead of `var`"; + let msg = "write `let` instead of `var` to introduce a new variable"; return self.recover_stmt_local(lo, attrs.into(), msg, "let"); } @@ -208,14 +208,14 @@ impl<'a> Parser<'a> { fn recover_stmt_local( &mut self, - span: Span, + lo: Span, attrs: AttrVec, msg: &str, sugg: &str, ) -> PResult<'a, Option> { - let stmt = self.parse_local_mk(span, attrs)?; - self.struct_span_err(stmt.span, "invalid variable declaration") - .span_suggestion_short(span, msg, sugg.to_string(), Applicability::MachineApplicable) + let stmt = self.parse_local_mk(lo, attrs)?; + self.struct_span_err(lo, "invalid variable declaration") + .span_suggestion(lo, msg, sugg.to_string(), Applicability::MachineApplicable) .emit(); Ok(Some(stmt)) } diff --git a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs index 7efc4174874..c1826f8caae 100644 --- a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs +++ b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.rs @@ -1,20 +1,20 @@ fn main() { auto n = 0;//~ ERROR invalid variable declaration - //~^ HELP to introduce a variable, write `let` instead of `auto` + //~^ HELP write `let` instead of `auto` to introduce a new variable auto m;//~ ERROR invalid variable declaration - //~^ HELP to introduce a variable, write `let` instead of `auto` + //~^ HELP write `let` instead of `auto` to introduce a new variable m = 0; var n = 0;//~ ERROR invalid variable declaration - //~^ HELP to introduce a variable, write `let` instead of `var` + //~^ HELP write `let` instead of `var` to introduce a new variable var m;//~ ERROR invalid variable declaration - //~^ HELP to introduce a variable, write `let` instead of `var` + //~^ HELP write `let` instead of `var` to introduce a new variable m = 0; mut n = 0;//~ ERROR invalid variable declaration - //~^ HELP missing `let` + //~^ HELP missing keyword mut var;//~ ERROR invalid variable declaration - //~^ HELP missing `let` + //~^ HELP missing keyword var = 0; let _recovery_witness: () = 0; //~ ERROR mismatched types diff --git a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr index 429c12265bd..ad72dd30542 100644 --- a/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr +++ b/src/test/ui/parser/issue-65257-invalid-var-decl-recovery.stderr @@ -2,49 +2,57 @@ error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:2:5 | LL | auto n = 0; - | ----^^^^^^ - | | - | help: to introduce a variable, write `let` instead of `auto` + | ^^^^ + | +help: write `let` instead of `auto` to introduce a new variable + | +LL | let n = 0; + | ^^^ error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:4:5 | LL | auto m; - | ----^^ - | | - | help: to introduce a variable, write `let` instead of `auto` + | ^^^^ + | +help: write `let` instead of `auto` to introduce a new variable + | +LL | let m; + | ^^^ error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:8:5 | LL | var n = 0; - | ---^^^^^^ - | | - | help: to introduce a variable, write `let` instead of `var` + | ^^^ + | +help: write `let` instead of `var` to introduce a new variable + | +LL | let n = 0; + | ^^^ error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:10:5 | LL | var m; - | ---^^ - | | - | help: to introduce a variable, write `let` instead of `var` + | ^^^ + | +help: write `let` instead of `var` to introduce a new variable + | +LL | let m; + | ^^^ error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:14:5 | LL | mut n = 0; - | ---^^^^^^ - | | - | help: missing `let` + | ^^^ help: missing keyword: `let mut` error: invalid variable declaration --> $DIR/issue-65257-invalid-var-decl-recovery.rs:16:5 | LL | mut var; - | ---^^^^ - | | - | help: missing `let` + | ^^^ help: missing keyword: `let mut` error[E0308]: mismatched types --> $DIR/issue-65257-invalid-var-decl-recovery.rs:20:33 -- cgit 1.4.1-3-g733a5