about summary refs log tree commit diff
diff options
context:
space:
mode:
authortrevyn <230691+trevyn@users.noreply.github.com>2024-07-06 13:14:53 +0300
committertrevyn <230691+trevyn@users.noreply.github.com>2024-07-08 10:04:03 +0400
commitb40adc9d3b56f230f295011ff78b0dfd378683ef (patch)
treee488528d8378b67898faa6b30dd25a5f6ac88caf
parent51917e2e69702e5752bce6a4f3bfd285d0f4ae39 (diff)
downloadrust-b40adc9d3b56f230f295011ff78b0dfd378683ef.tar.gz
rust-b40adc9d3b56f230f295011ff78b0dfd378683ef.zip
Add suggestions for possible missing `fn`, `struct`, or `enum` keywords
-rw-r--r--compiler/rustc_parse/messages.ftl19
-rw-r--r--compiler/rustc_parse/src/errors.rs31
-rw-r--r--compiler/rustc_parse/src/parser/item.rs112
-rw-r--r--tests/ui/did_you_mean/issue-40006.rs4
-rw-r--r--tests/ui/did_you_mean/issue-40006.stderr28
-rw-r--r--tests/ui/mismatched_types/recovered-block.rs2
-rw-r--r--tests/ui/mismatched_types/recovered-block.stderr12
-rw-r--r--tests/ui/parser/extern-no-fn.rs4
-rw-r--r--tests/ui/parser/extern-no-fn.stderr12
-rw-r--r--tests/ui/parser/missing-enum-issue-125446.rs6
-rw-r--r--tests/ui/parser/missing-enum-issue-125446.stderr13
-rw-r--r--tests/ui/parser/missing-enum-or-struct-issue-125446.rs2
-rw-r--r--tests/ui/parser/missing-enum-or-struct-issue-125446.stderr8
-rw-r--r--tests/ui/parser/missing-fn-issue-125446.rs7
-rw-r--r--tests/ui/parser/missing-fn-issue-125446.stderr13
-rw-r--r--tests/ui/parser/missing-fn-issue-65381-1.rs4
-rw-r--r--tests/ui/parser/missing-fn-issue-65381-1.stderr13
-rw-r--r--tests/ui/parser/missing-fn-issue-65381-2.rs3
-rw-r--r--tests/ui/parser/missing-fn-issue-65381-2.stderr13
-rw-r--r--tests/ui/parser/missing-fn-issue-65381-3.rs4
-rw-r--r--tests/ui/parser/missing-fn-issue-65381-3.stderr13
-rw-r--r--tests/ui/parser/missing-struct-issue-125446.rs5
-rw-r--r--tests/ui/parser/missing-struct-issue-125446.stderr13
-rw-r--r--tests/ui/pub/pub-ident-fn-2.stderr6
-rw-r--r--tests/ui/pub/pub-ident-fn-or-struct.stderr9
-rw-r--r--tests/ui/pub/pub-ident-fn-with-lifetime-2.rs2
-rw-r--r--tests/ui/pub/pub-ident-fn-with-lifetime-2.stderr8
-rw-r--r--tests/ui/pub/pub-ident-fn-with-lifetime.rs2
-rw-r--r--tests/ui/pub/pub-ident-fn-with-lifetime.stderr8
-rw-r--r--tests/ui/pub/pub-ident-fn.rs2
-rw-r--r--tests/ui/pub/pub-ident-fn.stderr8
-rw-r--r--tests/ui/pub/pub-ident-struct-2.stderr6
-rw-r--r--tests/ui/pub/pub-ident-struct-3.stderr6
-rw-r--r--tests/ui/pub/pub-ident-struct-4.stderr6
-rw-r--r--tests/ui/pub/pub-ident-struct-with-lifetime.rs2
-rw-r--r--tests/ui/pub/pub-ident-struct-with-lifetime.stderr11
-rw-r--r--tests/ui/pub/pub-ident-struct.fixed6
-rw-r--r--tests/ui/pub/pub-ident-struct.rs4
-rw-r--r--tests/ui/pub/pub-ident-struct.stderr11
-rw-r--r--tests/ui/pub/pub-ident-with-lifetime-incomplete.rs2
-rw-r--r--tests/ui/pub/pub-ident-with-lifetime-incomplete.stderr6
-rw-r--r--tests/ui/track-diagnostics/track4.stderr10
42 files changed, 309 insertions, 147 deletions
diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl
index e4c75ac1145..02c3c87313b 100644
--- a/compiler/rustc_parse/messages.ftl
+++ b/compiler/rustc_parse/messages.ftl
@@ -1,9 +1,5 @@
 parse_add_paren = try adding parentheses
 
-parse_ambiguous_missing_keyword_for_item_definition = missing `fn` or `struct` for function or struct definition
-    .suggestion = if you meant to call a macro, try
-    .help = if you meant to call a macro, remove the `pub` and add a trailing `!` after the identifier
-
 parse_ambiguous_range_pattern = the range pattern here has ambiguous interpretation
     .suggestion = add parentheses to clarify the precedence
 
@@ -528,14 +524,23 @@ parse_missing_comma_after_match_arm = expected `,` following `match` arm
 parse_missing_const_type = missing type for `{$kind}` item
     .suggestion = provide a type for the item
 
+parse_missing_enum_for_enum_definition = missing `enum` for enum definition
+    .suggestion = add `enum` here to parse `{$ident}` as an enum
+
+parse_missing_enum_or_struct_for_item_definition = missing `enum` or `struct` for enum or struct definition
+
 parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop
     .suggestion = try adding an expression to the `for` loop
 
 parse_missing_fn_for_function_definition = missing `fn` for function definition
-    .suggestion = add `fn` here to parse `{$ident}` as a public function
+    .suggestion = add `fn` here to parse `{$ident}` as a function
 
 parse_missing_fn_for_method_definition = missing `fn` for method definition
-    .suggestion = add `fn` here to parse `{$ident}` as a public method
+    .suggestion = add `fn` here to parse `{$ident}` as a method
+
+parse_missing_fn_or_struct_for_item_definition = missing `fn` or `struct` for function or struct definition
+    .suggestion = if you meant to call a macro, try
+    .help = if you meant to call a macro, remove the `pub` and add a trailing `!` after the identifier
 
 parse_missing_fn_params = missing parameters for function definition
     .suggestion = add a parameter list
@@ -555,7 +560,7 @@ parse_missing_semicolon_before_array = expected `;`, found `[`
     .suggestion = consider adding `;` here
 
 parse_missing_struct_for_struct_definition = missing `struct` for struct definition
-    .suggestion = add `struct` here to parse `{$ident}` as a public struct
+    .suggestion = add `struct` here to parse `{$ident}` as a struct
 
 parse_missing_trait_in_trait_impl = missing trait in a trait impl
     .suggestion_add_trait = add a trait here
diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs
index 6894f470d88..3ae9b6dad99 100644
--- a/compiler/rustc_parse/src/errors.rs
+++ b/compiler/rustc_parse/src/errors.rs
@@ -1612,28 +1612,44 @@ pub(crate) struct DefaultNotFollowedByItem {
 
 #[derive(Diagnostic)]
 pub(crate) enum MissingKeywordForItemDefinition {
+    #[diag(parse_missing_enum_for_enum_definition)]
+    Enum {
+        #[primary_span]
+        span: Span,
+        #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "enum ")]
+        insert_span: Span,
+        ident: Ident,
+    },
+    #[diag(parse_missing_enum_or_struct_for_item_definition)]
+    EnumOrStruct {
+        #[primary_span]
+        span: Span,
+    },
     #[diag(parse_missing_struct_for_struct_definition)]
     Struct {
         #[primary_span]
-        #[suggestion(style = "short", applicability = "maybe-incorrect", code = " struct ")]
         span: Span,
+        #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "struct ")]
+        insert_span: Span,
         ident: Ident,
     },
     #[diag(parse_missing_fn_for_function_definition)]
     Function {
         #[primary_span]
-        #[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")]
         span: Span,
+        #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "fn ")]
+        insert_span: Span,
         ident: Ident,
     },
     #[diag(parse_missing_fn_for_method_definition)]
     Method {
         #[primary_span]
-        #[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")]
         span: Span,
+        #[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "fn ")]
+        insert_span: Span,
         ident: Ident,
     },
-    #[diag(parse_ambiguous_missing_keyword_for_item_definition)]
+    #[diag(parse_missing_fn_or_struct_for_item_definition)]
     Ambiguous {
         #[primary_span]
         span: Span,
@@ -1644,7 +1660,12 @@ pub(crate) enum MissingKeywordForItemDefinition {
 
 #[derive(Subdiagnostic)]
 pub(crate) enum AmbiguousMissingKwForItemSub {
-    #[suggestion(parse_suggestion, applicability = "maybe-incorrect", code = "{snippet}!")]
+    #[suggestion(
+        parse_suggestion,
+        style = "verbose",
+        applicability = "maybe-incorrect",
+        code = "{snippet}!"
+    )]
     SuggestMacro {
         #[primary_span]
         span: Span,
diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs
index abb6b51cebd..f31e634f55c 100644
--- a/compiler/rustc_parse/src/parser/item.rs
+++ b/compiler/rustc_parse/src/parser/item.rs
@@ -239,6 +239,7 @@ impl<'a> Parser<'a> {
                 self.recover_const_impl(const_span, attrs, def_())?
             } else {
                 self.recover_const_mut(const_span);
+                self.recover_missing_kw_before_item()?;
                 let (ident, generics, ty, expr) = self.parse_const_item()?;
                 (
                     ident,
@@ -311,6 +312,9 @@ impl<'a> Parser<'a> {
                 Case::Insensitive,
             );
         } else if macros_allowed && self.check_path() {
+            if self.isnt_macro_invocation() {
+                self.recover_missing_kw_before_item()?;
+            }
             // MACRO INVOCATION ITEM
             (Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?)))
         } else {
@@ -374,25 +378,25 @@ impl<'a> Parser<'a> {
         self.check_ident() && self.look_ahead(1, |t| *t != token::Not && *t != token::PathSep)
     }
 
-    /// Recover on encountering a struct or method definition where the user
-    /// forgot to add the `struct` or `fn` keyword after writing `pub`: `pub S {}`.
+    /// Recover on encountering a struct, enum, or method definition where the user
+    /// forgot to add the `struct`, `enum`, or `fn` keyword
     fn recover_missing_kw_before_item(&mut self) -> PResult<'a, ()> {
-        // Space between `pub` keyword and the identifier
-        //
-        //     pub   S {}
-        //        ^^^ `sp` points here
-        let sp = self.prev_token.span.between(self.token.span);
-        let full_sp = self.prev_token.span.to(self.token.span);
-        let ident_sp = self.token.span;
-
-        let ident = if self.look_ahead(1, |t| {
-            [
-                token::Lt,
-                token::OpenDelim(Delimiter::Brace),
-                token::OpenDelim(Delimiter::Parenthesis),
-            ]
-            .contains(&t.kind)
-        }) {
+        let is_pub = self.prev_token.is_keyword(kw::Pub);
+        let is_const = self.prev_token.is_keyword(kw::Const);
+        let ident_span = self.token.span;
+        let span = if is_pub { self.prev_token.span.to(ident_span) } else { ident_span };
+        let insert_span = ident_span.shrink_to_lo();
+
+        let ident = if (!is_const
+            || self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Parenthesis)))
+            && self.look_ahead(1, |t| {
+                [
+                    token::Lt,
+                    token::OpenDelim(Delimiter::Brace),
+                    token::OpenDelim(Delimiter::Parenthesis),
+                ]
+                .contains(&t.kind)
+            }) {
             self.parse_ident().unwrap()
         } else {
             return Ok(());
@@ -406,46 +410,56 @@ impl<'a> Parser<'a> {
         }
 
         let err = if self.check(&token::OpenDelim(Delimiter::Brace)) {
-            // possible public struct definition where `struct` was forgotten
-            Some(errors::MissingKeywordForItemDefinition::Struct { span: sp, ident })
+            // possible struct or enum definition where `struct` or `enum` was forgotten
+            if self.look_ahead(1, |t| *t == token::CloseDelim(Delimiter::Brace)) {
+                // `S {}` could be unit enum or struct
+                Some(errors::MissingKeywordForItemDefinition::EnumOrStruct { span })
+            } else if self.look_ahead(2, |t| *t == token::Colon)
+                || self.look_ahead(3, |t| *t == token::Colon)
+            {
+                // `S { f:` or `S { pub f:`
+                Some(errors::MissingKeywordForItemDefinition::Struct { span, insert_span, ident })
+            } else {
+                Some(errors::MissingKeywordForItemDefinition::Enum { span, insert_span, ident })
+            }
         } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
-            // possible public function or tuple struct definition where `fn`/`struct` was
-            // forgotten
+            // possible function or tuple struct definition where `fn` or `struct` was forgotten
             self.bump(); // `(`
             let is_method = self.recover_self_param();
 
             self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes);
 
-            let err =
-                if self.check(&token::RArrow) || self.check(&token::OpenDelim(Delimiter::Brace)) {
-                    self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
-                    self.bump(); // `{`
-                    self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
-                    if is_method {
-                        errors::MissingKeywordForItemDefinition::Method { span: sp, ident }
-                    } else {
-                        errors::MissingKeywordForItemDefinition::Function { span: sp, ident }
-                    }
-                } else if self.check(&token::Semi) {
-                    errors::MissingKeywordForItemDefinition::Struct { span: sp, ident }
+            let err = if self.check(&token::RArrow)
+                || self.check(&token::OpenDelim(Delimiter::Brace))
+            {
+                self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
+                self.bump(); // `{`
+                self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
+                if is_method {
+                    errors::MissingKeywordForItemDefinition::Method { span, insert_span, ident }
                 } else {
-                    errors::MissingKeywordForItemDefinition::Ambiguous {
-                        span: sp,
-                        subdiag: if found_generics {
-                            None
-                        } else if let Ok(snippet) = self.span_to_snippet(ident_sp) {
-                            Some(errors::AmbiguousMissingKwForItemSub::SuggestMacro {
-                                span: full_sp,
-                                snippet,
-                            })
-                        } else {
-                            Some(errors::AmbiguousMissingKwForItemSub::HelpMacro)
-                        },
-                    }
-                };
+                    errors::MissingKeywordForItemDefinition::Function { span, insert_span, ident }
+                }
+            } else if is_pub && self.check(&token::Semi) {
+                errors::MissingKeywordForItemDefinition::Struct { span, insert_span, ident }
+            } else {
+                errors::MissingKeywordForItemDefinition::Ambiguous {
+                    span,
+                    subdiag: if found_generics {
+                        None
+                    } else if let Ok(snippet) = self.span_to_snippet(ident_span) {
+                        Some(errors::AmbiguousMissingKwForItemSub::SuggestMacro {
+                            span: ident_span,
+                            snippet,
+                        })
+                    } else {
+                        Some(errors::AmbiguousMissingKwForItemSub::HelpMacro)
+                    },
+                }
+            };
             Some(err)
         } else if found_generics {
-            Some(errors::MissingKeywordForItemDefinition::Ambiguous { span: sp, subdiag: None })
+            Some(errors::MissingKeywordForItemDefinition::Ambiguous { span, subdiag: None })
         } else {
             None
         };
diff --git a/tests/ui/did_you_mean/issue-40006.rs b/tests/ui/did_you_mean/issue-40006.rs
index 74f304d81a0..fff31bfc85e 100644
--- a/tests/ui/did_you_mean/issue-40006.rs
+++ b/tests/ui/did_you_mean/issue-40006.rs
@@ -5,7 +5,7 @@ impl dyn A {
 struct S;
 
 trait X {
-    X() {} //~ ERROR expected one of `!` or `::`, found `(`
+    X() {} //~ ERROR missing `fn` for function definition
     fn xxx() { ### }
     L = M;
     Z = { 2 + 3 };
@@ -13,7 +13,7 @@ trait X {
 }
 
 trait A {
-    X() {} //~ ERROR expected one of `!` or `::`, found `(`
+    X() {} //~ ERROR missing `fn` for function definition
 }
 trait B {
     fn xxx() { ### } //~ ERROR expected
diff --git a/tests/ui/did_you_mean/issue-40006.stderr b/tests/ui/did_you_mean/issue-40006.stderr
index bdbfa4dd713..303806a14de 100644
--- a/tests/ui/did_you_mean/issue-40006.stderr
+++ b/tests/ui/did_you_mean/issue-40006.stderr
@@ -11,26 +11,36 @@ LL | }
    | unexpected token
    | the item list ends here
 
-error: expected one of `!` or `::`, found `(`
-  --> $DIR/issue-40006.rs:8:6
+error: missing `fn` for function definition
+  --> $DIR/issue-40006.rs:8:5
    |
 LL | trait X {
    |         - while parsing this item list starting here
 LL |     X() {}
-   |      ^ expected one of `!` or `::`
+   |     ^
 ...
 LL | }
    | - the item list ends here
+   |
+help: add `fn` here to parse `X` as a function
+   |
+LL |     fn X() {}
+   |     ++
 
-error: expected one of `!` or `::`, found `(`
-  --> $DIR/issue-40006.rs:16:6
+error: missing `fn` for function definition
+  --> $DIR/issue-40006.rs:16:5
    |
 LL | trait A {
    |         - while parsing this item list starting here
 LL |     X() {}
-   |      ^ expected one of `!` or `::`
+   |     ^
 LL | }
    | - the item list ends here
+   |
+help: add `fn` here to parse `X` as a function
+   |
+LL |     fn X() {}
+   |     ++
 
 error: expected one of `!` or `[`, found `#`
   --> $DIR/issue-40006.rs:19:17
@@ -69,17 +79,17 @@ LL | }
    | - the item list ends here
 
 error: missing `fn` for method definition
-  --> $DIR/issue-40006.rs:32:8
+  --> $DIR/issue-40006.rs:32:5
    |
 LL | impl S {
    |        - while parsing this item list starting here
 LL |     pub hello_method(&self) {
-   |        ^
+   |     ^^^^^^^^^^^^^^^^
 ...
 LL | }
    | - the item list ends here
    |
-help: add `fn` here to parse `hello_method` as a public method
+help: add `fn` here to parse `hello_method` as a method
    |
 LL |     pub fn hello_method(&self) {
    |         ++
diff --git a/tests/ui/mismatched_types/recovered-block.rs b/tests/ui/mismatched_types/recovered-block.rs
index a91bbe7083b..f3078f46489 100644
--- a/tests/ui/mismatched_types/recovered-block.rs
+++ b/tests/ui/mismatched_types/recovered-block.rs
@@ -10,6 +10,6 @@ pub fn foo() -> Foo {
 
     pub Foo { text }
 }
-//~^^ ERROR missing `struct` for struct definition
+//~^^ ERROR missing `enum` for enum definition
 
 fn main() {}
diff --git a/tests/ui/mismatched_types/recovered-block.stderr b/tests/ui/mismatched_types/recovered-block.stderr
index 51b5f3b1af2..35ce134ab61 100644
--- a/tests/ui/mismatched_types/recovered-block.stderr
+++ b/tests/ui/mismatched_types/recovered-block.stderr
@@ -1,13 +1,13 @@
-error: missing `struct` for struct definition
-  --> $DIR/recovered-block.rs:11:8
+error: missing `enum` for enum definition
+  --> $DIR/recovered-block.rs:11:5
    |
 LL |     pub Foo { text }
-   |        ^
+   |     ^^^^^^^
    |
-help: add `struct` here to parse `Foo` as a public struct
+help: add `enum` here to parse `Foo` as an enum
    |
-LL |     pub struct Foo { text }
-   |         ++++++
+LL |     pub enum Foo { text }
+   |         ++++
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/extern-no-fn.rs b/tests/ui/parser/extern-no-fn.rs
index 73568609cdf..e965b390f07 100644
--- a/tests/ui/parser/extern-no-fn.rs
+++ b/tests/ui/parser/extern-no-fn.rs
@@ -1,5 +1,7 @@
 extern "C" {
-    f(); //~ ERROR expected one of `!` or `::`, found `(`
+    f();
+    //~^ ERROR missing `fn` or `struct` for function or struct definition
+    //~| HELP if you meant to call a macro, try
 }
 
 fn main() {
diff --git a/tests/ui/parser/extern-no-fn.stderr b/tests/ui/parser/extern-no-fn.stderr
index 2e434afb218..03826e4a93b 100644
--- a/tests/ui/parser/extern-no-fn.stderr
+++ b/tests/ui/parser/extern-no-fn.stderr
@@ -1,12 +1,18 @@
-error: expected one of `!` or `::`, found `(`
-  --> $DIR/extern-no-fn.rs:2:6
+error: missing `fn` or `struct` for function or struct definition
+  --> $DIR/extern-no-fn.rs:2:5
    |
 LL | extern "C" {
    |            - while parsing this item list starting here
 LL |     f();
-   |      ^ expected one of `!` or `::`
+   |     ^
+...
 LL | }
    | - the item list ends here
+   |
+help: if you meant to call a macro, try
+   |
+LL |     f!();
+   |     ~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/parser/missing-enum-issue-125446.rs b/tests/ui/parser/missing-enum-issue-125446.rs
new file mode 100644
index 00000000000..8d2cdb0dc40
--- /dev/null
+++ b/tests/ui/parser/missing-enum-issue-125446.rs
@@ -0,0 +1,6 @@
+Whoops {
+//~^ ERROR missing `enum` for enum definition
+//~| HELP add `enum` here to parse `Whoops` as an enum
+    OptionA,
+    OptionB,
+}
diff --git a/tests/ui/parser/missing-enum-issue-125446.stderr b/tests/ui/parser/missing-enum-issue-125446.stderr
new file mode 100644
index 00000000000..113b147473c
--- /dev/null
+++ b/tests/ui/parser/missing-enum-issue-125446.stderr
@@ -0,0 +1,13 @@
+error: missing `enum` for enum definition
+  --> $DIR/missing-enum-issue-125446.rs:1:1
+   |
+LL | Whoops {
+   | ^^^^^^
+   |
+help: add `enum` here to parse `Whoops` as an enum
+   |
+LL | enum Whoops {
+   | ++++
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/missing-enum-or-struct-issue-125446.rs b/tests/ui/parser/missing-enum-or-struct-issue-125446.rs
new file mode 100644
index 00000000000..c817ffaf859
--- /dev/null
+++ b/tests/ui/parser/missing-enum-or-struct-issue-125446.rs
@@ -0,0 +1,2 @@
+Whoops {}
+//~^ ERROR missing `enum` or `struct` for enum or struct definition
diff --git a/tests/ui/parser/missing-enum-or-struct-issue-125446.stderr b/tests/ui/parser/missing-enum-or-struct-issue-125446.stderr
new file mode 100644
index 00000000000..15916cebf45
--- /dev/null
+++ b/tests/ui/parser/missing-enum-or-struct-issue-125446.stderr
@@ -0,0 +1,8 @@
+error: missing `enum` or `struct` for enum or struct definition
+  --> $DIR/missing-enum-or-struct-issue-125446.rs:1:1
+   |
+LL | Whoops {}
+   | ^^^^^^
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/missing-fn-issue-125446.rs b/tests/ui/parser/missing-fn-issue-125446.rs
new file mode 100644
index 00000000000..73c9c3a60e1
--- /dev/null
+++ b/tests/ui/parser/missing-fn-issue-125446.rs
@@ -0,0 +1,7 @@
+whoops() {}
+//~^ ERROR missing `fn` for function definition
+//~| HELP add `fn` here to parse `whoops` as a function
+
+fn main() {
+    whoops();
+}
diff --git a/tests/ui/parser/missing-fn-issue-125446.stderr b/tests/ui/parser/missing-fn-issue-125446.stderr
new file mode 100644
index 00000000000..2a92e0062cb
--- /dev/null
+++ b/tests/ui/parser/missing-fn-issue-125446.stderr
@@ -0,0 +1,13 @@
+error: missing `fn` for function definition
+  --> $DIR/missing-fn-issue-125446.rs:1:1
+   |
+LL | whoops() {}
+   | ^^^^^^
+   |
+help: add `fn` here to parse `whoops` as a function
+   |
+LL | fn whoops() {}
+   | ++
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/missing-fn-issue-65381-1.rs b/tests/ui/parser/missing-fn-issue-65381-1.rs
new file mode 100644
index 00000000000..ac0299ae37b
--- /dev/null
+++ b/tests/ui/parser/missing-fn-issue-65381-1.rs
@@ -0,0 +1,4 @@
+main() {
+//~^ ERROR missing `fn` for function definition
+//~| HELP add `fn` here to parse `main` as a function
+}
diff --git a/tests/ui/parser/missing-fn-issue-65381-1.stderr b/tests/ui/parser/missing-fn-issue-65381-1.stderr
new file mode 100644
index 00000000000..95ccd8518ee
--- /dev/null
+++ b/tests/ui/parser/missing-fn-issue-65381-1.stderr
@@ -0,0 +1,13 @@
+error: missing `fn` for function definition
+  --> $DIR/missing-fn-issue-65381-1.rs:1:1
+   |
+LL | main() {
+   | ^^^^
+   |
+help: add `fn` here to parse `main` as a function
+   |
+LL | fn main() {
+   | ++
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/missing-fn-issue-65381-2.rs b/tests/ui/parser/missing-fn-issue-65381-2.rs
new file mode 100644
index 00000000000..e8c214a58b6
--- /dev/null
+++ b/tests/ui/parser/missing-fn-issue-65381-2.rs
@@ -0,0 +1,3 @@
+main();
+//~^ ERROR missing `fn` or `struct` for function or struct definition
+//~| HELP if you meant to call a macro, try
diff --git a/tests/ui/parser/missing-fn-issue-65381-2.stderr b/tests/ui/parser/missing-fn-issue-65381-2.stderr
new file mode 100644
index 00000000000..e13d395d70d
--- /dev/null
+++ b/tests/ui/parser/missing-fn-issue-65381-2.stderr
@@ -0,0 +1,13 @@
+error: missing `fn` or `struct` for function or struct definition
+  --> $DIR/missing-fn-issue-65381-2.rs:1:1
+   |
+LL | main();
+   | ^^^^
+   |
+help: if you meant to call a macro, try
+   |
+LL | main!();
+   | ~~~~~
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/missing-fn-issue-65381-3.rs b/tests/ui/parser/missing-fn-issue-65381-3.rs
new file mode 100644
index 00000000000..28e5b5aa430
--- /dev/null
+++ b/tests/ui/parser/missing-fn-issue-65381-3.rs
@@ -0,0 +1,4 @@
+pub const initial_value() -> Self {
+//~^ ERROR missing `fn` for function definition
+//~| HELP add `fn` here to parse `initial_value` as a function
+}
diff --git a/tests/ui/parser/missing-fn-issue-65381-3.stderr b/tests/ui/parser/missing-fn-issue-65381-3.stderr
new file mode 100644
index 00000000000..883295baede
--- /dev/null
+++ b/tests/ui/parser/missing-fn-issue-65381-3.stderr
@@ -0,0 +1,13 @@
+error: missing `fn` for function definition
+  --> $DIR/missing-fn-issue-65381-3.rs:1:11
+   |
+LL | pub const initial_value() -> Self {
+   |           ^^^^^^^^^^^^^
+   |
+help: add `fn` here to parse `initial_value` as a function
+   |
+LL | pub const fn initial_value() -> Self {
+   |           ++
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/parser/missing-struct-issue-125446.rs b/tests/ui/parser/missing-struct-issue-125446.rs
new file mode 100644
index 00000000000..439087aeec9
--- /dev/null
+++ b/tests/ui/parser/missing-struct-issue-125446.rs
@@ -0,0 +1,5 @@
+Whoops {
+//~^ ERROR missing `struct` for struct definition
+//~| HELP add `struct` here to parse `Whoops` as a struct
+    value: u64,
+}
diff --git a/tests/ui/parser/missing-struct-issue-125446.stderr b/tests/ui/parser/missing-struct-issue-125446.stderr
new file mode 100644
index 00000000000..ff3135b2357
--- /dev/null
+++ b/tests/ui/parser/missing-struct-issue-125446.stderr
@@ -0,0 +1,13 @@
+error: missing `struct` for struct definition
+  --> $DIR/missing-struct-issue-125446.rs:1:1
+   |
+LL | Whoops {
+   | ^^^^^^
+   |
+help: add `struct` here to parse `Whoops` as a struct
+   |
+LL | struct Whoops {
+   | ++++++
+
+error: aborting due to 1 previous error
+
diff --git a/tests/ui/pub/pub-ident-fn-2.stderr b/tests/ui/pub/pub-ident-fn-2.stderr
index e724278b233..80f4b14da89 100644
--- a/tests/ui/pub/pub-ident-fn-2.stderr
+++ b/tests/ui/pub/pub-ident-fn-2.stderr
@@ -1,10 +1,10 @@
 error: missing `fn` for function definition
-  --> $DIR/pub-ident-fn-2.rs:3:4
+  --> $DIR/pub-ident-fn-2.rs:3:1
    |
 LL | pub foo(_s: usize) { bar() }
-   |    ^
+   | ^^^^^^^
    |
-help: add `fn` here to parse `foo` as a public function
+help: add `fn` here to parse `foo` as a function
    |
 LL | pub fn foo(_s: usize) { bar() }
    |     ++
diff --git a/tests/ui/pub/pub-ident-fn-or-struct.stderr b/tests/ui/pub/pub-ident-fn-or-struct.stderr
index a8fa4bd3bd3..ceadc510c63 100644
--- a/tests/ui/pub/pub-ident-fn-or-struct.stderr
+++ b/tests/ui/pub/pub-ident-fn-or-struct.stderr
@@ -1,8 +1,13 @@
 error: missing `fn` or `struct` for function or struct definition
-  --> $DIR/pub-ident-fn-or-struct.rs:1:4
+  --> $DIR/pub-ident-fn-or-struct.rs:1:1
    |
 LL | pub S (foo) bar
-   | ---^- help: if you meant to call a macro, try: `S!`
+   | ^^^^^
+   |
+help: if you meant to call a macro, try
+   |
+LL | pub S! (foo) bar
+   |     ~~
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/pub/pub-ident-fn-with-lifetime-2.rs b/tests/ui/pub/pub-ident-fn-with-lifetime-2.rs
index 1ee8c84f13b..3fb93cb669b 100644
--- a/tests/ui/pub/pub-ident-fn-with-lifetime-2.rs
+++ b/tests/ui/pub/pub-ident-fn-with-lifetime-2.rs
@@ -1,4 +1,4 @@
-pub   bar<'a>(&self, _s: &'a usize) -> bool { true }
+pub bar<'a>(&self, _s: &'a usize) -> bool { true }
 //~^ ERROR missing `fn` for method definition
 
 fn main() {
diff --git a/tests/ui/pub/pub-ident-fn-with-lifetime-2.stderr b/tests/ui/pub/pub-ident-fn-with-lifetime-2.stderr
index b0d5ce9de5c..e6523ca58ab 100644
--- a/tests/ui/pub/pub-ident-fn-with-lifetime-2.stderr
+++ b/tests/ui/pub/pub-ident-fn-with-lifetime-2.stderr
@@ -1,10 +1,10 @@
 error: missing `fn` for method definition
-  --> $DIR/pub-ident-fn-with-lifetime-2.rs:1:4
+  --> $DIR/pub-ident-fn-with-lifetime-2.rs:1:1
    |
-LL | pub   bar<'a>(&self, _s: &'a usize) -> bool { true }
-   |    ^^^
+LL | pub bar<'a>(&self, _s: &'a usize) -> bool { true }
+   | ^^^^^^^
    |
-help: add `fn` here to parse `bar` as a public method
+help: add `fn` here to parse `bar` as a method
    |
 LL | pub fn bar<'a>(&self, _s: &'a usize) -> bool { true }
    |     ++
diff --git a/tests/ui/pub/pub-ident-fn-with-lifetime.rs b/tests/ui/pub/pub-ident-fn-with-lifetime.rs
index 8cdc152f163..0fd25ca0b1c 100644
--- a/tests/ui/pub/pub-ident-fn-with-lifetime.rs
+++ b/tests/ui/pub/pub-ident-fn-with-lifetime.rs
@@ -1,6 +1,6 @@
 //@ run-rustfix
 
-pub   foo<'a>(_s: &'a usize) -> bool { true }
+pub foo<'a>(_s: &'a usize) -> bool { true }
 //~^ ERROR missing `fn` for function definition
 
 fn main() {
diff --git a/tests/ui/pub/pub-ident-fn-with-lifetime.stderr b/tests/ui/pub/pub-ident-fn-with-lifetime.stderr
index 63fcf6bf5d5..52c6206a75f 100644
--- a/tests/ui/pub/pub-ident-fn-with-lifetime.stderr
+++ b/tests/ui/pub/pub-ident-fn-with-lifetime.stderr
@@ -1,10 +1,10 @@
 error: missing `fn` for function definition
-  --> $DIR/pub-ident-fn-with-lifetime.rs:3:4
+  --> $DIR/pub-ident-fn-with-lifetime.rs:3:1
    |
-LL | pub   foo<'a>(_s: &'a usize) -> bool { true }
-   |    ^^^
+LL | pub foo<'a>(_s: &'a usize) -> bool { true }
+   | ^^^^^^^
    |
-help: add `fn` here to parse `foo` as a public function
+help: add `fn` here to parse `foo` as a function
    |
 LL | pub fn foo<'a>(_s: &'a usize) -> bool { true }
    |     ++
diff --git a/tests/ui/pub/pub-ident-fn.rs b/tests/ui/pub/pub-ident-fn.rs
index 899ea82ccb7..1032f3375ea 100644
--- a/tests/ui/pub/pub-ident-fn.rs
+++ b/tests/ui/pub/pub-ident-fn.rs
@@ -1,6 +1,6 @@
 //@ run-rustfix
 
-pub   foo(_s: usize) -> bool { true }
+pub foo(_s: usize) -> bool { true }
 //~^ ERROR missing `fn` for function definition
 
 fn main() {
diff --git a/tests/ui/pub/pub-ident-fn.stderr b/tests/ui/pub/pub-ident-fn.stderr
index 06dac616443..54360061fef 100644
--- a/tests/ui/pub/pub-ident-fn.stderr
+++ b/tests/ui/pub/pub-ident-fn.stderr
@@ -1,10 +1,10 @@
 error: missing `fn` for function definition
-  --> $DIR/pub-ident-fn.rs:3:4
+  --> $DIR/pub-ident-fn.rs:3:1
    |
-LL | pub   foo(_s: usize) -> bool { true }
-   |    ^^^
+LL | pub foo(_s: usize) -> bool { true }
+   | ^^^^^^^
    |
-help: add `fn` here to parse `foo` as a public function
+help: add `fn` here to parse `foo` as a function
    |
 LL | pub fn foo(_s: usize) -> bool { true }
    |     ++
diff --git a/tests/ui/pub/pub-ident-struct-2.stderr b/tests/ui/pub/pub-ident-struct-2.stderr
index 5e0f328d986..ef2ff1d2f0e 100644
--- a/tests/ui/pub/pub-ident-struct-2.stderr
+++ b/tests/ui/pub/pub-ident-struct-2.stderr
@@ -1,10 +1,10 @@
 error: missing `struct` for struct definition
-  --> $DIR/pub-ident-struct-2.rs:4:8
+  --> $DIR/pub-ident-struct-2.rs:4:5
    |
 LL |     pub bar();
-   |        ^
+   |     ^^^^^^^
    |
-help: add `struct` here to parse `bar` as a public struct
+help: add `struct` here to parse `bar` as a struct
    |
 LL |     pub struct bar();
    |         ++++++
diff --git a/tests/ui/pub/pub-ident-struct-3.stderr b/tests/ui/pub/pub-ident-struct-3.stderr
index d08e5120570..0a23ad51211 100644
--- a/tests/ui/pub/pub-ident-struct-3.stderr
+++ b/tests/ui/pub/pub-ident-struct-3.stderr
@@ -1,10 +1,10 @@
 error: missing `struct` for struct definition
-  --> $DIR/pub-ident-struct-3.rs:1:4
+  --> $DIR/pub-ident-struct-3.rs:1:1
    |
 LL | pub S();
-   |    ^
+   | ^^^^^
    |
-help: add `struct` here to parse `S` as a public struct
+help: add `struct` here to parse `S` as a struct
    |
 LL | pub struct S();
    |     ++++++
diff --git a/tests/ui/pub/pub-ident-struct-4.stderr b/tests/ui/pub/pub-ident-struct-4.stderr
index ec136783211..d3072464e05 100644
--- a/tests/ui/pub/pub-ident-struct-4.stderr
+++ b/tests/ui/pub/pub-ident-struct-4.stderr
@@ -1,10 +1,10 @@
 error: missing `struct` for struct definition
-  --> $DIR/pub-ident-struct-4.rs:4:4
+  --> $DIR/pub-ident-struct-4.rs:4:1
    |
 LL | pub T(String);
-   |    ^
+   | ^^^^^
    |
-help: add `struct` here to parse `T` as a public struct
+help: add `struct` here to parse `T` as a struct
    |
 LL | pub struct T(String);
    |     ++++++
diff --git a/tests/ui/pub/pub-ident-struct-with-lifetime.rs b/tests/ui/pub/pub-ident-struct-with-lifetime.rs
index 2feb0266070..baa5dc8cfb9 100644
--- a/tests/ui/pub/pub-ident-struct-with-lifetime.rs
+++ b/tests/ui/pub/pub-ident-struct-with-lifetime.rs
@@ -1,4 +1,4 @@
 pub S<'a> {
-//~^ ERROR missing `struct` for struct definition
+//~^ ERROR missing `enum` or `struct` for enum or struct definition
 }
 fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct-with-lifetime.stderr b/tests/ui/pub/pub-ident-struct-with-lifetime.stderr
index 0e08a5ff449..1367c941f80 100644
--- a/tests/ui/pub/pub-ident-struct-with-lifetime.stderr
+++ b/tests/ui/pub/pub-ident-struct-with-lifetime.stderr
@@ -1,13 +1,8 @@
-error: missing `struct` for struct definition
-  --> $DIR/pub-ident-struct-with-lifetime.rs:1:4
+error: missing `enum` or `struct` for enum or struct definition
+  --> $DIR/pub-ident-struct-with-lifetime.rs:1:1
    |
 LL | pub S<'a> {
-   |    ^
-   |
-help: add `struct` here to parse `S` as a public struct
-   |
-LL | pub struct S<'a> {
-   |     ++++++
+   | ^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/pub/pub-ident-struct.fixed b/tests/ui/pub/pub-ident-struct.fixed
deleted file mode 100644
index 3f0610cd765..00000000000
--- a/tests/ui/pub/pub-ident-struct.fixed
+++ /dev/null
@@ -1,6 +0,0 @@
-//@ run-rustfix
-
-pub struct S {
-//~^ ERROR missing `struct` for struct definition
-}
-fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct.rs b/tests/ui/pub/pub-ident-struct.rs
index 6d06c406f6c..f9f31a9ed2b 100644
--- a/tests/ui/pub/pub-ident-struct.rs
+++ b/tests/ui/pub/pub-ident-struct.rs
@@ -1,6 +1,4 @@
-//@ run-rustfix
-
 pub S {
-//~^ ERROR missing `struct` for struct definition
+//~^ ERROR missing `enum` or `struct` for enum or struct definition
 }
 fn main() {}
diff --git a/tests/ui/pub/pub-ident-struct.stderr b/tests/ui/pub/pub-ident-struct.stderr
index 2d5d61d9381..6d85305ceaf 100644
--- a/tests/ui/pub/pub-ident-struct.stderr
+++ b/tests/ui/pub/pub-ident-struct.stderr
@@ -1,13 +1,8 @@
-error: missing `struct` for struct definition
-  --> $DIR/pub-ident-struct.rs:3:4
+error: missing `enum` or `struct` for enum or struct definition
+  --> $DIR/pub-ident-struct.rs:1:1
    |
 LL | pub S {
-   |    ^
-   |
-help: add `struct` here to parse `S` as a public struct
-   |
-LL | pub struct S {
-   |     ++++++
+   | ^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/pub/pub-ident-with-lifetime-incomplete.rs b/tests/ui/pub/pub-ident-with-lifetime-incomplete.rs
index c86a9f2fdd6..dec1a502646 100644
--- a/tests/ui/pub/pub-ident-with-lifetime-incomplete.rs
+++ b/tests/ui/pub/pub-ident-with-lifetime-incomplete.rs
@@ -1,5 +1,5 @@
 fn main() {
 }
 
-pub   foo<'a>
+pub foo<'a>
 //~^ ERROR missing `fn` or `struct` for function or struct definition
diff --git a/tests/ui/pub/pub-ident-with-lifetime-incomplete.stderr b/tests/ui/pub/pub-ident-with-lifetime-incomplete.stderr
index 750e2d17e0a..67b6b9c7ba8 100644
--- a/tests/ui/pub/pub-ident-with-lifetime-incomplete.stderr
+++ b/tests/ui/pub/pub-ident-with-lifetime-incomplete.stderr
@@ -1,8 +1,8 @@
 error: missing `fn` or `struct` for function or struct definition
-  --> $DIR/pub-ident-with-lifetime-incomplete.rs:4:4
+  --> $DIR/pub-ident-with-lifetime-incomplete.rs:4:1
    |
-LL | pub   foo<'a>
-   |    ^^^
+LL | pub foo<'a>
+   | ^^^^^^^
 
 error: aborting due to 1 previous error
 
diff --git a/tests/ui/track-diagnostics/track4.stderr b/tests/ui/track-diagnostics/track4.stderr
index d9eaea93638..19499fa7abc 100644
--- a/tests/ui/track-diagnostics/track4.stderr
+++ b/tests/ui/track-diagnostics/track4.stderr
@@ -1,14 +1,14 @@
-error: missing `struct` for struct definition
+error: missing `enum` for enum definition
   --> $DIR/track4.rs:LL:CC
    |
 LL | pub onion {
-   |    ^
+   | ^^^^^^^^^
 -Ztrack-diagnostics: created at compiler/rustc_parse/src/parser/item.rs:LL:CC
    |
-help: add `struct` here to parse `onion` as a public struct
+help: add `enum` here to parse `onion` as an enum
    |
-LL | pub struct onion {
-   |     ++++++
+LL | pub enum onion {
+   |     ++++
 
 error: aborting due to 1 previous error