From f270bb15f16caee67c33aad8d1fba5f1bd6fca6f Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 3 Mar 2023 19:21:01 +0300 Subject: [PATCH 1/4] Recover from `pub let` inside function --- compiler/rustc_parse/locales/en-US.ftl | 3 +++ compiler/rustc_parse/src/errors.rs | 8 ++++++++ compiler/rustc_parse/src/parser/stmt.rs | 5 +++++ 3 files changed, 16 insertions(+) diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/locales/en-US.ftl index e76e91fc1b135..36baa372f17af 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/locales/en-US.ftl @@ -439,6 +439,9 @@ 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_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..1dc0c1cf67914 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1403,6 +1403,14 @@ 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_default_not_followed_by_item)] #[note] 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 { From 35bb73e22f0faad9069a0894a76f4d416d5f74c5 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 3 Mar 2023 19:21:35 +0300 Subject: [PATCH 2/4] Add test for `pub let` inside function recovery --- tests/ui/parser/pub-let.fixed | 9 +++++++++ tests/ui/parser/pub-let.rs | 9 +++++++++ tests/ui/parser/pub-let.stderr | 16 ++++++++++++++++ 3 files changed, 34 insertions(+) create mode 100644 tests/ui/parser/pub-let.fixed create mode 100644 tests/ui/parser/pub-let.rs create mode 100644 tests/ui/parser/pub-let.stderr 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 + From 4edbda5533f2827e5986b39c1abc9a792601cdba Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 3 Mar 2023 20:59:45 +0300 Subject: [PATCH 3/4] Recover from `let` outside function --- compiler/rustc_parse/locales/en-US.ftl | 4 ++++ compiler/rustc_parse/src/errors.rs | 19 +++++++++++++++++++ compiler/rustc_parse/src/parser/item.rs | 19 ++++++++++--------- 3 files changed, 33 insertions(+), 9 deletions(-) diff --git a/compiler/rustc_parse/locales/en-US.ftl b/compiler/rustc_parse/locales/en-US.ftl index 36baa372f17af..72c30662fbdab 100644 --- a/compiler/rustc_parse/locales/en-US.ftl +++ b/compiler/rustc_parse/locales/en-US.ftl @@ -442,6 +442,10 @@ parse_visibility_not_followed_by_item = visibility `{$vis}` is not followed by a 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 1dc0c1cf67914..35ecfc220a1de 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1411,6 +1411,25 @@ pub(crate) struct VisibilityFollowedByLet { 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); } } From 695078630273d4de07161becf3f5abf9f0068055 Mon Sep 17 00:00:00 2001 From: Obei Sideg Date: Fri, 3 Mar 2023 21:00:45 +0300 Subject: [PATCH 4/4] Add test for `let` outside function recovery --- tests/ui/parser/let-outside-function.rs | 5 +++++ tests/ui/parser/let-outside-function.stderr | 17 +++++++++++++++++ tests/ui/parser/suggest-const-for-global-var.rs | 2 +- .../parser/suggest-const-for-global-var.stderr | 13 +++++++++++-- 4 files changed, 34 insertions(+), 3 deletions(-) create mode 100644 tests/ui/parser/let-outside-function.rs create mode 100644 tests/ui/parser/let-outside-function.stderr 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/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