From 91674cc56c2ce35097cf44262096c6ecbe067194 Mon Sep 17 00:00:00 2001 From: Michael Goulet Date: Sat, 3 Sep 2022 05:39:46 +0000 Subject: [PATCH] Suggest removing unnecessary prefix let in patterns --- compiler/rustc_ast/src/token.rs | 24 +++++++++++++++++++ .../locales/en-US/parser.ftl | 3 +++ .../rustc_parse/src/parser/diagnostics.rs | 8 +++++++ compiler/rustc_parse/src/parser/pat.rs | 9 ++++++- src/test/ui/parser/unnecessary-let.rs | 11 +++++++++ src/test/ui/parser/unnecessary-let.stderr | 20 ++++++++++++++++ 6 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 src/test/ui/parser/unnecessary-let.rs create mode 100644 src/test/ui/parser/unnecessary-let.stderr diff --git a/compiler/rustc_ast/src/token.rs b/compiler/rustc_ast/src/token.rs index dd98946b4cc..bfc4db32d37 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 3b37a393846..bc17754c56a 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 dd806e2130e..a299ee0a104 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 8b3200d45fc..6c8f3f664e2 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 00000000000..6279109621d --- /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 00000000000..952119cae3e --- /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 +