Skip to content

Commit 5a3ac50

Browse files
committed
Add suggestions for possible missing fn, struct, or enum keywords
1 parent 51917e2 commit 5a3ac50

39 files changed

+241
-95
lines changed

compiler/rustc_parse/messages.ftl

+6-3
Original file line numberDiff line numberDiff line change
@@ -528,14 +528,17 @@ parse_missing_comma_after_match_arm = expected `,` following `match` arm
528528
parse_missing_const_type = missing type for `{$kind}` item
529529
.suggestion = provide a type for the item
530530
531+
parse_missing_enum_for_enum_definition = missing `enum` for enum definition
532+
.suggestion = add `enum` here to parse `{$ident}` as an enum
533+
531534
parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop
532535
.suggestion = try adding an expression to the `for` loop
533536
534537
parse_missing_fn_for_function_definition = missing `fn` for function definition
535-
.suggestion = add `fn` here to parse `{$ident}` as a public function
538+
.suggestion = add `fn` here to parse `{$ident}` as a function
536539
537540
parse_missing_fn_for_method_definition = missing `fn` for method definition
538-
.suggestion = add `fn` here to parse `{$ident}` as a public method
541+
.suggestion = add `fn` here to parse `{$ident}` as a method
539542
540543
parse_missing_fn_params = missing parameters for function definition
541544
.suggestion = add a parameter list
@@ -555,7 +558,7 @@ parse_missing_semicolon_before_array = expected `;`, found `[`
555558
.suggestion = consider adding `;` here
556559
557560
parse_missing_struct_for_struct_definition = missing `struct` for struct definition
558-
.suggestion = add `struct` here to parse `{$ident}` as a public struct
561+
.suggestion = add `struct` here to parse `{$ident}` as a struct
559562
560563
parse_missing_trait_in_trait_impl = missing trait in a trait impl
561564
.suggestion_add_trait = add a trait here

compiler/rustc_parse/src/errors.rs

+16-4
Original file line numberDiff line numberDiff line change
@@ -1612,24 +1612,31 @@ pub(crate) struct DefaultNotFollowedByItem {
16121612

16131613
#[derive(Diagnostic)]
16141614
pub(crate) enum MissingKeywordForItemDefinition {
1615+
#[diag(parse_missing_enum_for_enum_definition)]
1616+
Enum {
1617+
#[primary_span]
1618+
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "enum ")]
1619+
span: Span,
1620+
ident: Ident,
1621+
},
16151622
#[diag(parse_missing_struct_for_struct_definition)]
16161623
Struct {
16171624
#[primary_span]
1618-
#[suggestion(style = "short", applicability = "maybe-incorrect", code = " struct ")]
1625+
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "struct ")]
16191626
span: Span,
16201627
ident: Ident,
16211628
},
16221629
#[diag(parse_missing_fn_for_function_definition)]
16231630
Function {
16241631
#[primary_span]
1625-
#[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")]
1632+
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "fn ")]
16261633
span: Span,
16271634
ident: Ident,
16281635
},
16291636
#[diag(parse_missing_fn_for_method_definition)]
16301637
Method {
16311638
#[primary_span]
1632-
#[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")]
1639+
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "fn ")]
16331640
span: Span,
16341641
ident: Ident,
16351642
},
@@ -1644,7 +1651,12 @@ pub(crate) enum MissingKeywordForItemDefinition {
16441651

16451652
#[derive(Subdiagnostic)]
16461653
pub(crate) enum AmbiguousMissingKwForItemSub {
1647-
#[suggestion(parse_suggestion, applicability = "maybe-incorrect", code = "{snippet}!")]
1654+
#[suggestion(
1655+
parse_suggestion,
1656+
style = "verbose",
1657+
applicability = "maybe-incorrect",
1658+
code = "{snippet}!"
1659+
)]
16481660
SuggestMacro {
16491661
#[primary_span]
16501662
span: Span,

compiler/rustc_parse/src/parser/item.rs

+31-22
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,7 @@ impl<'a> Parser<'a> {
239239
self.recover_const_impl(const_span, attrs, def_())?
240240
} else {
241241
self.recover_const_mut(const_span);
242+
self.recover_missing_kw_before_item()?;
242243
let (ident, generics, ty, expr) = self.parse_const_item()?;
243244
(
244245
ident,
@@ -311,6 +312,9 @@ impl<'a> Parser<'a> {
311312
Case::Insensitive,
312313
);
313314
} else if macros_allowed && self.check_path() {
315+
if self.isnt_macro_invocation() {
316+
self.recover_missing_kw_before_item()?;
317+
}
314318
// MACRO INVOCATION ITEM
315319
(Ident::empty(), ItemKind::MacCall(P(self.parse_item_macro(vis)?)))
316320
} else {
@@ -374,25 +378,24 @@ impl<'a> Parser<'a> {
374378
self.check_ident() && self.look_ahead(1, |t| *t != token::Not && *t != token::PathSep)
375379
}
376380

377-
/// Recover on encountering a struct or method definition where the user
378-
/// forgot to add the `struct` or `fn` keyword after writing `pub`: `pub S {}`.
381+
/// Recover on encountering a struct, enum, or method definition where the user
382+
/// forgot to add the `struct`, `enum`, or `fn` keyword
379383
fn recover_missing_kw_before_item(&mut self) -> PResult<'a, ()> {
380-
// Space between `pub` keyword and the identifier
381-
//
382-
// pub S {}
383-
// ^^^ `sp` points here
384-
let sp = self.prev_token.span.between(self.token.span);
385-
let full_sp = self.prev_token.span.to(self.token.span);
384+
let is_pub = self.prev_token.is_keyword(kw::Pub);
385+
let is_const = self.prev_token.is_keyword(kw::Const);
386+
let sp = self.token.span.shrink_to_lo();
386387
let ident_sp = self.token.span;
387388

388-
let ident = if self.look_ahead(1, |t| {
389-
[
390-
token::Lt,
391-
token::OpenDelim(Delimiter::Brace),
392-
token::OpenDelim(Delimiter::Parenthesis),
393-
]
394-
.contains(&t.kind)
395-
}) {
389+
let ident = if (!is_const
390+
|| self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Parenthesis)))
391+
&& self.look_ahead(1, |t| {
392+
[
393+
token::Lt,
394+
token::OpenDelim(Delimiter::Brace),
395+
token::OpenDelim(Delimiter::Parenthesis),
396+
]
397+
.contains(&t.kind)
398+
}) {
396399
self.parse_ident().unwrap()
397400
} else {
398401
return Ok(());
@@ -406,11 +409,17 @@ impl<'a> Parser<'a> {
406409
}
407410

408411
let err = if self.check(&token::OpenDelim(Delimiter::Brace)) {
409-
// possible public struct definition where `struct` was forgotten
410-
Some(errors::MissingKeywordForItemDefinition::Struct { span: sp, ident })
412+
// possible struct or enum definition where `struct` or `enum` was forgotten
413+
if self.look_ahead(1, |t| *t == token::CloseDelim(Delimiter::Brace))
414+
|| self.look_ahead(2, |t| *t == token::Colon)
415+
|| self.look_ahead(3, |t| *t == token::Colon)
416+
{
417+
Some(errors::MissingKeywordForItemDefinition::Struct { span: sp, ident })
418+
} else {
419+
Some(errors::MissingKeywordForItemDefinition::Enum { span: sp, ident })
420+
}
411421
} else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
412-
// possible public function or tuple struct definition where `fn`/`struct` was
413-
// forgotten
422+
// possible function or tuple struct definition where `fn` or `struct` was forgotten
414423
self.bump(); // `(`
415424
let is_method = self.recover_self_param();
416425

@@ -426,7 +435,7 @@ impl<'a> Parser<'a> {
426435
} else {
427436
errors::MissingKeywordForItemDefinition::Function { span: sp, ident }
428437
}
429-
} else if self.check(&token::Semi) {
438+
} else if is_pub && self.check(&token::Semi) {
430439
errors::MissingKeywordForItemDefinition::Struct { span: sp, ident }
431440
} else {
432441
errors::MissingKeywordForItemDefinition::Ambiguous {
@@ -435,7 +444,7 @@ impl<'a> Parser<'a> {
435444
None
436445
} else if let Ok(snippet) = self.span_to_snippet(ident_sp) {
437446
Some(errors::AmbiguousMissingKwForItemSub::SuggestMacro {
438-
span: full_sp,
447+
span: ident_sp,
439448
snippet,
440449
})
441450
} else {

tests/ui/did_you_mean/issue-40006.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,15 @@ impl dyn A {
55
struct S;
66

77
trait X {
8-
X() {} //~ ERROR expected one of `!` or `::`, found `(`
8+
X() {} //~ ERROR missing `fn` for function definition
99
fn xxx() { ### }
1010
L = M;
1111
Z = { 2 + 3 };
1212
::Y ();
1313
}
1414

1515
trait A {
16-
X() {} //~ ERROR expected one of `!` or `::`, found `(`
16+
X() {} //~ ERROR missing `fn` for function definition
1717
}
1818
trait B {
1919
fn xxx() { ### } //~ ERROR expected

tests/ui/did_you_mean/issue-40006.stderr

+19-9
Original file line numberDiff line numberDiff line change
@@ -11,26 +11,36 @@ LL | }
1111
| unexpected token
1212
| the item list ends here
1313

14-
error: expected one of `!` or `::`, found `(`
15-
--> $DIR/issue-40006.rs:8:6
14+
error: missing `fn` for function definition
15+
--> $DIR/issue-40006.rs:8:5
1616
|
1717
LL | trait X {
1818
| - while parsing this item list starting here
1919
LL | X() {}
20-
| ^ expected one of `!` or `::`
20+
| ^
2121
...
2222
LL | }
2323
| - the item list ends here
24+
|
25+
help: add `fn` here to parse `X` as a function
26+
|
27+
LL | fn X() {}
28+
| ++
2429

25-
error: expected one of `!` or `::`, found `(`
26-
--> $DIR/issue-40006.rs:16:6
30+
error: missing `fn` for function definition
31+
--> $DIR/issue-40006.rs:16:5
2732
|
2833
LL | trait A {
2934
| - while parsing this item list starting here
3035
LL | X() {}
31-
| ^ expected one of `!` or `::`
36+
| ^
3237
LL | }
3338
| - the item list ends here
39+
|
40+
help: add `fn` here to parse `X` as a function
41+
|
42+
LL | fn X() {}
43+
| ++
3444

3545
error: expected one of `!` or `[`, found `#`
3646
--> $DIR/issue-40006.rs:19:17
@@ -69,17 +79,17 @@ LL | }
6979
| - the item list ends here
7080

7181
error: missing `fn` for method definition
72-
--> $DIR/issue-40006.rs:32:8
82+
--> $DIR/issue-40006.rs:32:9
7383
|
7484
LL | impl S {
7585
| - while parsing this item list starting here
7686
LL | pub hello_method(&self) {
77-
| ^
87+
| ^
7888
...
7989
LL | }
8090
| - the item list ends here
8191
|
82-
help: add `fn` here to parse `hello_method` as a public method
92+
help: add `fn` here to parse `hello_method` as a method
8393
|
8494
LL | pub fn hello_method(&self) {
8595
| ++

tests/ui/mismatched_types/recovered-block.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,6 @@ pub fn foo() -> Foo {
1010

1111
pub Foo { text }
1212
}
13-
//~^^ ERROR missing `struct` for struct definition
13+
//~^^ ERROR missing `enum` for enum definition
1414

1515
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
1-
error: missing `struct` for struct definition
2-
--> $DIR/recovered-block.rs:11:8
1+
error: missing `enum` for enum definition
2+
--> $DIR/recovered-block.rs:11:9
33
|
44
LL | pub Foo { text }
5-
| ^
5+
| ^
66
|
7-
help: add `struct` here to parse `Foo` as a public struct
7+
help: add `enum` here to parse `Foo` as an enum
88
|
9-
LL | pub struct Foo { text }
10-
| ++++++
9+
LL | pub enum Foo { text }
10+
| ++++
1111

1212
error: aborting due to 1 previous error
1313

tests/ui/parser/extern-no-fn.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
extern "C" {
2-
f(); //~ ERROR expected one of `!` or `::`, found `(`
2+
f();
3+
//~^ ERROR missing `fn` or `struct` for function or struct definition
4+
//~| HELP if you meant to call a macro, try
35
}
46

57
fn main() {

tests/ui/parser/extern-no-fn.stderr

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1-
error: expected one of `!` or `::`, found `(`
2-
--> $DIR/extern-no-fn.rs:2:6
1+
error: missing `fn` or `struct` for function or struct definition
2+
--> $DIR/extern-no-fn.rs:2:5
33
|
44
LL | extern "C" {
55
| - while parsing this item list starting here
66
LL | f();
7-
| ^ expected one of `!` or `::`
7+
| ^
8+
...
89
LL | }
910
| - the item list ends here
11+
|
12+
help: if you meant to call a macro, try
13+
|
14+
LL | f!();
15+
| ~~
1016

1117
error: aborting due to 1 previous error
1218

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
Whoops {
2+
//~^ ERROR missing `enum` for enum definition
3+
//~| HELP add `enum` here to parse `Whoops` as an enum
4+
OptionA,
5+
OptionB,
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error: missing `enum` for enum definition
2+
|
3+
help: add `enum` here to parse `Whoops` as an enum
4+
|
5+
LL | enum Whoops {
6+
| ++++
7+
8+
error: aborting due to 1 previous error
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
whoops() {}
2+
//~^ ERROR missing `fn` for function definition
3+
//~| HELP add `fn` here to parse `whoops` as a function
4+
5+
fn main() {
6+
whoops();
7+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error: missing `fn` for function definition
2+
|
3+
help: add `fn` here to parse `whoops` as a function
4+
|
5+
LL | fn whoops() {}
6+
| ++
7+
8+
error: aborting due to 1 previous error
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
main() {
2+
//~^ ERROR missing `fn` for function definition
3+
//~| HELP add `fn` here to parse `main` as a function
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error: missing `fn` for function definition
2+
|
3+
help: add `fn` here to parse `main` as a function
4+
|
5+
LL | fn main() {
6+
| ++
7+
8+
error: aborting due to 1 previous error
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
main();
2+
//~^ ERROR missing `fn` or `struct` for function or struct definition
3+
//~| HELP if you meant to call a macro, try
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error: missing `fn` or `struct` for function or struct definition
2+
|
3+
help: if you meant to call a macro, try
4+
|
5+
LL | main!();
6+
| ~~~~~
7+
8+
error: aborting due to 1 previous error
9+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
pub const initial_value() -> Self {
2+
//~^ ERROR missing `fn` for function definition
3+
//~| HELP add `fn` here to parse `initial_value` as a function
4+
}

0 commit comments

Comments
 (0)