diff --git a/src/libsyntax/parse/parser/stmt.rs b/src/libsyntax/parse/parser/stmt.rs index d54d9c4b8e9fa..f309a1308d924 100644 --- a/src/libsyntax/parse/parser/stmt.rs +++ b/src/libsyntax/parse/parser/stmt.rs @@ -10,8 +10,9 @@ use crate::ast::{self, DUMMY_NODE_ID, Stmt, StmtKind, Local, Block, BlockCheckMo use crate::ast::{Attribute, AttrStyle, VisibilityKind, MacStmtStyle, Mac, MacDelimiter}; use crate::parse::{classify, DirectoryOwnership}; use crate::parse::token; -use crate::source_map::{respan, Span}; +use crate::source_map::respan; use crate::symbol::{kw, sym}; +use syntax_pos::Span; use std::mem; use errors::Applicability; @@ -41,11 +42,29 @@ impl<'a> Parser<'a> { let lo = self.token.span; Ok(Some(if self.eat_keyword(kw::Let) { - Stmt { - id: DUMMY_NODE_ID, - kind: StmtKind::Local(self.parse_local(attrs.into())?), - span: lo.to(self.prev_span), - } + let local = self.parse_local(attrs.into())?; + self.mk_dummy_stmt(lo.to(self.prev_span), local) + + } else if self.is_ident_mut() { + return self.recover_stmt_local( + lo, + attrs, + "missing `let`", + ); + } else if self.is_var_sym() { + self.bump(); + return self.recover_stmt_local( + lo, + attrs, + "to introduce a variable, write `let` instead of `var`", + ); + } else if self.is_auto_for_let() { + self.bump(); + return self.recover_stmt_local( + lo, + attrs, + "to introduce a variable, write `let` instead of `auto`", + ); } else if let Some(macro_def) = self.eat_macro_def( &attrs, &respan(lo, VisibilityKind::Inherited), @@ -473,4 +492,61 @@ impl<'a> Parser<'a> { "this was erroneously allowed and will become a hard error in a future release" }).emit(); } + + fn recover_stmt_local( + &mut self, + span: Span, + attrs: Vec, + msg: &str, + ) -> PResult<'a, Option> { + let local = self.parse_local(attrs.into())?; + self.struct_span_err(span, "invalid variable declaration") + .span_suggestion_short( + span, + msg, + "let".to_string(), + Applicability::MachineApplicable + ).emit(); + return Ok(Some(self.mk_dummy_stmt(span, local))); + } + + fn mk_dummy_stmt(&mut self, span: Span, kind: P) -> Stmt { + Stmt { + id: DUMMY_NODE_ID, + kind: StmtKind::Local(kind), + span, + } + } + + /// Returns `true` if the next token is Brace + fn is_next_brace(&self) -> bool { + self.look_ahead(1, |t| t.eq(&token::OpenDelim(token::Brace))) + } + + /// Returns `true` if the token is kw::Mut and next one is an Ident + fn is_ident_mut(&self) -> bool { + self.token.is_keyword(kw::Mut) && + self.look_ahead(1, |t| t.is_ident()) + } + + /// Returns `true` if the token is sym::var. + fn is_var_sym(&self) -> bool { + self.token.is_non_raw_ident_where(|ident| ident.name == sym::var) && + self.is_malformed_declr() + } + + fn has_equals(&self) -> bool { + self.look_ahead(2, |t| t.eq(&token::Eq)) + } + + fn is_auto_for_let(&self) -> bool { + self.token.is_keyword(kw::Auto) && self.is_malformed_declr() + } + + fn is_malformed_declr(&self) -> bool { + (self.has_equals() && !self.is_next_brace()) || + (self.look_ahead(1, |t| t.is_ident()) && + self.look_ahead(2, |t| t.eq(&token::Semi)) + ) + } } diff --git a/src/libsyntax/parse/token.rs b/src/libsyntax/parse/token.rs index 4a8b25c61079b..c1f0f6f17e38e 100644 --- a/src/libsyntax/parse/token.rs +++ b/src/libsyntax/parse/token.rs @@ -538,7 +538,7 @@ impl Token { } /// Returns `true` if the token is a non-raw identifier for which `pred` holds. - fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool { + pub fn is_non_raw_ident_where(&self, pred: impl FnOnce(ast::Ident) -> bool) -> bool { match self.ident() { Some((id, false)) => pred(id), _ => false, diff --git a/src/libsyntax_pos/symbol.rs b/src/libsyntax_pos/symbol.rs index fa9567fb62c0f..59374b1e7a5e2 100644 --- a/src/libsyntax_pos/symbol.rs +++ b/src/libsyntax_pos/symbol.rs @@ -725,6 +725,7 @@ symbols! { usize, v1, val, + var, vec, Vec, vis, diff --git a/src/test/ui/parser/issue-65257--auto-instead-of-let-recovery.rs b/src/test/ui/parser/issue-65257--auto-instead-of-let-recovery.rs new file mode 100644 index 0000000000000..3e58efb0aca2f --- /dev/null +++ b/src/test/ui/parser/issue-65257--auto-instead-of-let-recovery.rs @@ -0,0 +1,8 @@ +#[allow(dead_code)] +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; +} diff --git a/src/test/ui/parser/issue-65257--auto-instead-of-let-recovery.stderr b/src/test/ui/parser/issue-65257--auto-instead-of-let-recovery.stderr new file mode 100644 index 0000000000000..3bd21f7d101cd --- /dev/null +++ b/src/test/ui/parser/issue-65257--auto-instead-of-let-recovery.stderr @@ -0,0 +1,14 @@ +error: invalid variable declaration + --> $DIR/issue-65257--auto-instead-of-let-recovery.rs:4:5 + | +LL | auto n = 0; + | ^^^^ help: to introduce a variable, write `let` instead of `auto` + +error: invalid variable declaration + --> $DIR/issue-65257--auto-instead-of-let-recovery.rs:6:5 + | +LL | auto m; + | ^^^^ help: to introduce a variable, write `let` instead of `auto` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/parser/issue-65257--var-instead-of-let-recovery.rs b/src/test/ui/parser/issue-65257--var-instead-of-let-recovery.rs new file mode 100644 index 0000000000000..8c3cb551bf399 --- /dev/null +++ b/src/test/ui/parser/issue-65257--var-instead-of-let-recovery.rs @@ -0,0 +1,8 @@ +#[allow(dead_code)] +fn main() { + 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; +} diff --git a/src/test/ui/parser/issue-65257--var-instead-of-let-recovery.stderr b/src/test/ui/parser/issue-65257--var-instead-of-let-recovery.stderr new file mode 100644 index 0000000000000..e0a6fa6949dc5 --- /dev/null +++ b/src/test/ui/parser/issue-65257--var-instead-of-let-recovery.stderr @@ -0,0 +1,14 @@ +error: invalid variable declaration + --> $DIR/issue-65257--var-instead-of-let-recovery.rs:4:5 + | +LL | var n = 0; + | ^^^ help: to introduce a variable, write `let` instead of `var` + +error: invalid variable declaration + --> $DIR/issue-65257--var-instead-of-let-recovery.rs:6:5 + | +LL | var m; + | ^^^ help: to introduce a variable, write `let` instead of `var` + +error: aborting due to 2 previous errors + diff --git a/src/test/ui/parser/issue-65257-mut-instead-of-let-recovery.rs b/src/test/ui/parser/issue-65257-mut-instead-of-let-recovery.rs new file mode 100644 index 0000000000000..d2a9f3abc095f --- /dev/null +++ b/src/test/ui/parser/issue-65257-mut-instead-of-let-recovery.rs @@ -0,0 +1,8 @@ +#[allow(dead_code)] +fn main() { + mut n = 0;//~ ERROR invalid variable declaration + //~^ HELP missing `let` + mut var;//~ ERROR invalid variable declaration + //~^ HELP missing `let` + var = 0; +} diff --git a/src/test/ui/parser/issue-65257-mut-instead-of-let-recovery.stderr b/src/test/ui/parser/issue-65257-mut-instead-of-let-recovery.stderr new file mode 100644 index 0000000000000..ad22a5311186f --- /dev/null +++ b/src/test/ui/parser/issue-65257-mut-instead-of-let-recovery.stderr @@ -0,0 +1,14 @@ +error: invalid variable declaration + --> $DIR/issue-65257-mut-instead-of-let-recovery.rs:4:5 + | +LL | mut n = 0; + | ^^^ help: missing `let` + +error: invalid variable declaration + --> $DIR/issue-65257-mut-instead-of-let-recovery.rs:6:5 + | +LL | mut var; + | ^^^ help: missing `let` + +error: aborting due to 2 previous errors +