diff --git a/compiler/rustc_error_messages/locales/en-US/parser.ftl b/compiler/rustc_error_messages/locales/en-US/parser.ftl index 7e583753618..2d378013dd0 100644 --- a/compiler/rustc_error_messages/locales/en-US/parser.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parser.ftl @@ -32,3 +32,12 @@ parser_incorrect_use_of_await = parser_in_in_typo = expected iterable, found keyword `in` .suggestion = remove the duplicated `in` + +parser_invalid_variable_declaration = + invalid variable declaration + +parser_switch_mut_let_order = + switch the order of `mut` and `let` +parser_missing_let_before_mut = missing keyword +parser_use_let_not_auto = write `let` instead of `auto` to introduce a new variable +parser_use_let_not_var = write `let` instead of `var` to introduce a new variable diff --git a/compiler/rustc_parse/src/parser/diagnostics.rs b/compiler/rustc_parse/src/parser/diagnostics.rs index 93e70e9abda..eeedfd157be 100644 --- a/compiler/rustc_parse/src/parser/diagnostics.rs +++ b/compiler/rustc_parse/src/parser/diagnostics.rs @@ -334,6 +334,35 @@ struct InInTypo { sugg_span: Span, } +#[derive(SessionDiagnostic)] +#[error(parser::invalid_variable_declaration)] +pub struct InvalidVariableDeclaration { + #[primary_span] + pub span: Span, + #[subdiagnostic] + pub sub: InvalidVariableDeclarationSub, +} + +#[derive(SessionSubdiagnostic)] +pub enum InvalidVariableDeclarationSub { + #[suggestion( + parser::switch_mut_let_order, + applicability = "maybe-incorrect", + code = "let mut" + )] + SwitchMutLetOrder(#[primary_span] Span), + #[suggestion( + parser::missing_let_before_mut, + applicability = "machine-applicable", + code = "let mut" + )] + MissingLet(#[primary_span] Span), + #[suggestion(parser::use_let_not_auto, applicability = "machine-applicable", code = "let")] + UseLetNotAuto(#[primary_span] Span), + #[suggestion(parser::use_let_not_var, applicability = "machine-applicable", code = "let")] + UseLetNotVar(#[primary_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/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index ade0f4fbc86..002547c6723 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -1,5 +1,7 @@ use super::attr::DEFAULT_INNER_ATTR_FORBIDDEN; -use super::diagnostics::{AttemptLocalParseRecovery, Error}; +use super::diagnostics::{ + AttemptLocalParseRecovery, Error, InvalidVariableDeclaration, InvalidVariableDeclarationSub, +}; use super::expr::LhsExpr; use super::pat::RecoverComma; use super::path::PathStyle; @@ -58,28 +60,22 @@ pub(crate) fn parse_stmt_without_recovery( if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) { self.bump(); let mut_let_span = lo.to(self.token.span); - self.struct_span_err(mut_let_span, "invalid variable declaration") - .span_suggestion( - mut_let_span, - "switch the order of `mut` and `let`", - "let mut", - Applicability::MaybeIncorrect, - ) - .emit(); + self.sess.emit_err(InvalidVariableDeclaration { + span: mut_let_span, + sub: InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span), + }); } Ok(Some(if self.token.is_keyword(kw::Let) { self.parse_local_mk(lo, attrs, capture_semi, force_collect)? } else if self.is_kw_followed_by_ident(kw::Mut) { - self.recover_stmt_local(lo, attrs, "missing keyword", "let mut")? + self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::MissingLet)? } else if self.is_kw_followed_by_ident(kw::Auto) { self.bump(); // `auto` - let msg = "write `let` instead of `auto` to introduce a new variable"; - self.recover_stmt_local(lo, attrs, msg, "let")? + self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotAuto)? } else if self.is_kw_followed_by_ident(sym::var) { self.bump(); // `var` - let msg = "write `let` instead of `var` to introduce a new variable"; - self.recover_stmt_local(lo, attrs, msg, "let")? + self.recover_stmt_local(lo, attrs, InvalidVariableDeclarationSub::UseLetNotVar)? } else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() { // We have avoided contextual keywords like `union`, items with `crate` visibility, // or `auto trait` items. We aim to parse an arbitrary path `a::b` but not something @@ -217,13 +213,10 @@ fn recover_stmt_local( &mut self, lo: Span, attrs: AttrWrapper, - msg: &str, - sugg: &str, + subdiagnostic: fn(Span) -> InvalidVariableDeclarationSub, ) -> PResult<'a, Stmt> { let stmt = self.recover_local_after_let(lo, attrs)?; - self.struct_span_err(lo, "invalid variable declaration") - .span_suggestion(lo, msg, sugg, Applicability::MachineApplicable) - .emit(); + self.sess.emit_err(InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) }); Ok(stmt) }