diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/locales/en-US.ftl index e76e91fc1b135..72c30662fbdab 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/locales/en-US.ftl @@ -439,6 +439,13 @@ parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by a .label = the visibility .help = you likely meant to define an item, e.g., `{$vis} fn foo() {"{}"}` +parse_visibility_followed_by_let = visibility does not apply to `let` statement + .suggestion = remove pub + +parse_invalid_let_outside_function = `let` is invalid outside of a function + .suggestion_static = consider using `static` instead of `let` + .suggestion_const = consider using `const` instead of `let` + parse_default_not_followed_by_item = `default` is not followed by an item .label = the `default` qualifier .note = only `fn`, `const`, `type`, or `impl` items may be prefixed by `default` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 1662db36d10f9..35ecfc220a1de 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1403,6 +1403,33 @@ pub(crate) struct VisibilityNotFollowedByItem { pub vis: Visibility, } +#[derive(Diagnostic)] +#[diag(parse_visibility_followed_by_let)] +pub(crate) struct VisibilityFollowedByLet { + #[primary_span] + #[suggestion(code = "let", applicability = "machine-applicable")] + pub span: Span, +} + +#[derive(Diagnostic)] +#[diag(parse_invalid_let_outside_function)] +pub(crate) struct InvalidLetOutsideFunction { + #[primary_span] + #[suggestion( + parse_suggestion_static, + style = "verbose", + code = "static", + applicability = "machine-applicable" + )] + #[suggestion( + parse_suggestion_const, + style = "verbose", + code = "const", + applicability = "machine-applicable" + )] + pub span: Span, +} + #[derive(Diagnostic)] #[diag(parse_default_not_followed_by_item)] #[note] diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 9d9ae154ad42e..0b33ff49c1ff4 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -68,16 +68,17 @@ impl<'a> Parser<'a> { } if !self.eat(term) { - let token_str = super::token_descr(&self.token); if !self.maybe_consume_incorrect_semicolon(&items) { - let msg = &format!("expected item, found {token_str}"); - let mut err = self.struct_span_err(self.token.span, msg); - let label = if self.is_kw_followed_by_ident(kw::Let) { - "consider using `const` or `static` instead of `let` for global variables" - } else { - "expected item" - }; - err.span_label(self.token.span, label); + if self.is_kw_followed_by_ident(kw::Let) { + let err = self + .sess + .create_err(errors::InvalidLetOutsideFunction { span: self.token.span }); + return Err(err); + } + let token_str = super::token_descr(&self.token); + let mut err = self + .struct_span_err(self.token.span, &format!("expected item, found {token_str}")); + err.span_label(self.token.span, "expected item"); return Err(err); } } diff --git a/compiler/rustc_parse/src/parser/stmt.rs b/compiler/rustc_parse/src/parser/stmt.rs index 92a22ffc2b07b..bd025048cff29 100644 --- a/compiler/rustc_parse/src/parser/stmt.rs +++ b/compiler/rustc_parse/src/parser/stmt.rs @@ -111,6 +111,11 @@ impl<'a> Parser<'a> { // Do not attempt to parse an expression if we're done here. self.error_outer_attrs(attrs); self.mk_stmt(lo, StmtKind::Empty) + } else if self.prev_token.is_keyword(kw::Pub) && self.token.is_keyword(kw::Let) { + let err = self.sess.create_err(errors::VisibilityFollowedByLet { + span: self.prev_token.span.to(self.token.span), + }); + return Err(err); } else if self.token != token::CloseDelim(Delimiter::Brace) { // Remainder are line-expr stmts. let e = match force_collect { diff --git a/tests/ui/parser/let-outside-function.rs b/tests/ui/parser/let-outside-function.rs new file mode 100644 index 0000000000000..ec2f881ea2c33 --- /dev/null +++ b/tests/ui/parser/let-outside-function.rs @@ -0,0 +1,5 @@ +let a = 1; +//~^ ERROR `let` is invalid outside of a function + +fn main() { +} diff --git a/tests/ui/parser/let-outside-function.stderr b/tests/ui/parser/let-outside-function.stderr new file mode 100644 index 0000000000000..22bb39eebc78b --- /dev/null +++ b/tests/ui/parser/let-outside-function.stderr @@ -0,0 +1,17 @@ +error: `let` is invalid outside of a function + --> $DIR/let-outside-function.rs:1:1 + | +LL | let a = 1; + | ^^^ + | +help: consider using `static` instead of `let` + | +LL | static a = 1; + | ~~~~~~ +help: consider using `const` instead of `let` + | +LL | const a = 1; + | ~~~~~ + +error: aborting due to previous error + diff --git a/tests/ui/parser/pub-let.fixed b/tests/ui/parser/pub-let.fixed new file mode 100644 index 0000000000000..a76d84243bee1 --- /dev/null +++ b/tests/ui/parser/pub-let.fixed @@ -0,0 +1,9 @@ +// run-rustfix + +#![allow(unused_variables)] + +fn main() { + let x = 1; + //~^ ERROR visibility `pub` is not followed by an item + //~^^ ERROR visibility does not apply to `let` statement +} diff --git a/tests/ui/parser/pub-let.rs b/tests/ui/parser/pub-let.rs new file mode 100644 index 0000000000000..9de063405e680 --- /dev/null +++ b/tests/ui/parser/pub-let.rs @@ -0,0 +1,9 @@ +// run-rustfix + +#![allow(unused_variables)] + +fn main() { + pub let x = 1; + //~^ ERROR visibility `pub` is not followed by an item + //~^^ ERROR visibility does not apply to `let` statement +} diff --git a/tests/ui/parser/pub-let.stderr b/tests/ui/parser/pub-let.stderr new file mode 100644 index 0000000000000..ae221d4999aca --- /dev/null +++ b/tests/ui/parser/pub-let.stderr @@ -0,0 +1,16 @@ +error: visibility `pub` is not followed by an item + --> $DIR/pub-let.rs:6:5 + | +LL | pub let x = 1; + | ^^^ the visibility + | + = help: you likely meant to define an item, e.g., `pub fn foo() {}` + +error: visibility does not apply to `let` statement + --> $DIR/pub-let.rs:6:5 + | +LL | pub let x = 1; + | ^^^^^^^ help: remove pub: `let` + +error: aborting due to 2 previous errors + diff --git a/tests/ui/parser/suggest-const-for-global-var.rs b/tests/ui/parser/suggest-const-for-global-var.rs index d6216cb7ac275..432546aa303e7 100644 --- a/tests/ui/parser/suggest-const-for-global-var.rs +++ b/tests/ui/parser/suggest-const-for-global-var.rs @@ -1,5 +1,5 @@ let X: i32 = 12; -//~^ ERROR expected item, found keyword `let` +//~^ ERROR `let` is invalid outside of a function fn main() { println!("{}", X); diff --git a/tests/ui/parser/suggest-const-for-global-var.stderr b/tests/ui/parser/suggest-const-for-global-var.stderr index 94e44ec7f6ce1..dd2a243309ffc 100644 --- a/tests/ui/parser/suggest-const-for-global-var.stderr +++ b/tests/ui/parser/suggest-const-for-global-var.stderr @@ -1,8 +1,17 @@ -error: expected item, found keyword `let` +error: `let` is invalid outside of a function --> $DIR/suggest-const-for-global-var.rs:1:1 | LL | let X: i32 = 12; - | ^^^ consider using `const` or `static` instead of `let` for global variables + | ^^^ + | +help: consider using `static` instead of `let` + | +LL | static X: i32 = 12; + | ~~~~~~ +help: consider using `const` instead of `let` + | +LL | const X: i32 = 12; + | ~~~~~ error: aborting due to previous error