diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index dd98946b4cc5e..bfc4db32d3751 100644 --- a/compiler/rustc_ast/src/token.rs +++ b/compiler/rustc_ast/src/token.rs @@ -398,6 +398,30 @@ impl Token { } } + /// Returns `true` if the token can appear at the start of an pattern. + /// + /// Shamelessly borrowed from `can_begin_expr`, only used for diagnostics right now. + pub fn can_begin_pattern(&self) -> bool { + match self.uninterpolate().kind { + Ident(name, is_raw) => + ident_can_begin_expr(name, self.span, is_raw), // value name or keyword + | OpenDelim(Delimiter::Bracket | Delimiter::Parenthesis) // tuple or array + | Literal(..) // literal + | BinOp(Minus) // unary minus + | BinOp(And) // reference + | AndAnd // double reference + // DotDotDot is no longer supported + | DotDot | DotDotDot | DotDotEq // ranges + | Lt | BinOp(Shl) // associated path + | ModSep => true, // global path + Interpolated(ref nt) => matches!(**nt, NtLiteral(..) | + NtPat(..) | + NtBlock(..) | + NtPath(..)), + _ => false, + } + } + /// Returns `true` if the token can appear at the start of a type. pub fn can_begin_type(&self) -> bool { match self.uninterpolate().kind { diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 3b37a393846b2..bc17754c56ae3 100644 --- a/compiler/rustc_error_messages/locales/en-US/parser.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl @@ -150,3 +150,6 @@ parser_dotdotdot = unexpected token: `...` parser_left_arrow_operator = unexpected token: `<-` .suggestion = if you meant to write a comparison against a negative value, add a space in between `<` and `-` + +parser_remove_let = expected pattern, found `let` + .suggestion = remove the unnecessary `let` keyword diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 9b5dd09372cc8..ad49227222b2c 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -705,6 +705,14 @@ pub(crate) struct LeftArrowOperator { pub span: Span, } +#[derive(SessionDiagnostic)] +#[diag(parser::remove_let)] +pub(crate) struct RemoveLet { + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = "")] + pub span: Span, +} + // SnapshotParser is used to create a snapshot of the parser // without causing duplicate errors being emitted when the `Parser` // is dropped. diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index 88bd57d37cb65..120a3c267f106 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -1,4 +1,5 @@ use super::{ForceCollect, Parser, PathStyle, TrailingToken}; +use crate::parser::diagnostics::RemoveLet; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor}; use rustc_ast::ptr::P; @@ -320,7 +321,13 @@ impl<'a> Parser<'a> { maybe_recover_from_interpolated_ty_qpath!(self, true); maybe_whole!(self, NtPat, |x| x); - let lo = self.token.span; + let mut lo = self.token.span; + + if self.token.is_keyword(kw::Let) && self.look_ahead(1, |tok| tok.can_begin_pattern()) { + self.bump(); + self.sess.emit_err(RemoveLet { span: lo }); + lo = self.token.span; + } let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd { self.parse_pat_deref(expected)? diff --git a/src/test/ui/parser/unnecessary-let.rs b/src/test/ui/parser/unnecessary-let.rs new file mode 100644 index 0000000000000..6279109621d70 --- /dev/null +++ b/src/test/ui/parser/unnecessary-let.rs @@ -0,0 +1,11 @@ +fn main() { + for let x of [1, 2, 3] {} + //~^ ERROR expected pattern, found `let` + //~| ERROR missing `in` in `for` loop + + match 1 { + let 1 => {} + //~^ ERROR expected pattern, found `let` + _ => {} + } +} diff --git a/src/test/ui/parser/unnecessary-let.stderr b/src/test/ui/parser/unnecessary-let.stderr new file mode 100644 index 0000000000000..952119cae3e27 --- /dev/null +++ b/src/test/ui/parser/unnecessary-let.stderr @@ -0,0 +1,20 @@ +error: expected pattern, found `let` + --> $DIR/unnecessary-let.rs:2:9 + | +LL | for let x of [1, 2, 3] {} + | ^^^ help: remove the unnecessary `let` keyword + +error: missing `in` in `for` loop + --> $DIR/unnecessary-let.rs:2:15 + | +LL | for let x of [1, 2, 3] {} + | ^^ help: try using `in` here instead + +error: expected pattern, found `let` + --> $DIR/unnecessary-let.rs:7:9 + | +LL | let 1 => {} + | ^^^ help: remove the unnecessary `let` keyword + +error: aborting due to 3 previous errors +