Skip to content

Commit 0c81f94

Browse files
committed
Auto merge of #127419 - trevyn:issue-125446, r=fee1-dead
Add suggestions for possible missing `fn`, `struct`, or `enum` keywords Closes #125446 Closes #65381
2 parents c092b28 + b40adc9 commit 0c81f94

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+309
-147
lines changed

compiler/rustc_parse/messages.ftl

+12-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,5 @@
11
parse_add_paren = try adding parentheses
22
3-
parse_ambiguous_missing_keyword_for_item_definition = missing `fn` or `struct` for function or struct definition
4-
.suggestion = if you meant to call a macro, try
5-
.help = if you meant to call a macro, remove the `pub` and add a trailing `!` after the identifier
6-
73
parse_ambiguous_range_pattern = the range pattern here has ambiguous interpretation
84
.suggestion = add parentheses to clarify the precedence
95
@@ -528,14 +524,23 @@ parse_missing_comma_after_match_arm = expected `,` following `match` arm
528524
parse_missing_const_type = missing type for `{$kind}` item
529525
.suggestion = provide a type for the item
530526
527+
parse_missing_enum_for_enum_definition = missing `enum` for enum definition
528+
.suggestion = add `enum` here to parse `{$ident}` as an enum
529+
530+
parse_missing_enum_or_struct_for_item_definition = missing `enum` or `struct` for enum or struct definition
531+
531532
parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop
532533
.suggestion = try adding an expression to the `for` loop
533534
534535
parse_missing_fn_for_function_definition = missing `fn` for function definition
535-
.suggestion = add `fn` here to parse `{$ident}` as a public function
536+
.suggestion = add `fn` here to parse `{$ident}` as a function
536537
537538
parse_missing_fn_for_method_definition = missing `fn` for method definition
538-
.suggestion = add `fn` here to parse `{$ident}` as a public method
539+
.suggestion = add `fn` here to parse `{$ident}` as a method
540+
541+
parse_missing_fn_or_struct_for_item_definition = missing `fn` or `struct` for function or struct definition
542+
.suggestion = if you meant to call a macro, try
543+
.help = if you meant to call a macro, remove the `pub` and add a trailing `!` after the identifier
539544
540545
parse_missing_fn_params = missing parameters for function definition
541546
.suggestion = add a parameter list
@@ -555,7 +560,7 @@ parse_missing_semicolon_before_array = expected `;`, found `[`
555560
.suggestion = consider adding `;` here
556561
557562
parse_missing_struct_for_struct_definition = missing `struct` for struct definition
558-
.suggestion = add `struct` here to parse `{$ident}` as a public struct
563+
.suggestion = add `struct` here to parse `{$ident}` as a struct
559564
560565
parse_missing_trait_in_trait_impl = missing trait in a trait impl
561566
.suggestion_add_trait = add a trait here

compiler/rustc_parse/src/errors.rs

+26-5
Original file line numberDiff line numberDiff line change
@@ -1612,28 +1612,44 @@ 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+
span: Span,
1619+
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "enum ")]
1620+
insert_span: Span,
1621+
ident: Ident,
1622+
},
1623+
#[diag(parse_missing_enum_or_struct_for_item_definition)]
1624+
EnumOrStruct {
1625+
#[primary_span]
1626+
span: Span,
1627+
},
16151628
#[diag(parse_missing_struct_for_struct_definition)]
16161629
Struct {
16171630
#[primary_span]
1618-
#[suggestion(style = "short", applicability = "maybe-incorrect", code = " struct ")]
16191631
span: Span,
1632+
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "struct ")]
1633+
insert_span: Span,
16201634
ident: Ident,
16211635
},
16221636
#[diag(parse_missing_fn_for_function_definition)]
16231637
Function {
16241638
#[primary_span]
1625-
#[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")]
16261639
span: Span,
1640+
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "fn ")]
1641+
insert_span: Span,
16271642
ident: Ident,
16281643
},
16291644
#[diag(parse_missing_fn_for_method_definition)]
16301645
Method {
16311646
#[primary_span]
1632-
#[suggestion(style = "short", applicability = "maybe-incorrect", code = " fn ")]
16331647
span: Span,
1648+
#[suggestion(style = "verbose", applicability = "maybe-incorrect", code = "fn ")]
1649+
insert_span: Span,
16341650
ident: Ident,
16351651
},
1636-
#[diag(parse_ambiguous_missing_keyword_for_item_definition)]
1652+
#[diag(parse_missing_fn_or_struct_for_item_definition)]
16371653
Ambiguous {
16381654
#[primary_span]
16391655
span: Span,
@@ -1644,7 +1660,12 @@ pub(crate) enum MissingKeywordForItemDefinition {
16441660

16451661
#[derive(Subdiagnostic)]
16461662
pub(crate) enum AmbiguousMissingKwForItemSub {
1647-
#[suggestion(parse_suggestion, applicability = "maybe-incorrect", code = "{snippet}!")]
1663+
#[suggestion(
1664+
parse_suggestion,
1665+
style = "verbose",
1666+
applicability = "maybe-incorrect",
1667+
code = "{snippet}!"
1668+
)]
16481669
SuggestMacro {
16491670
#[primary_span]
16501671
span: Span,

compiler/rustc_parse/src/parser/item.rs

+63-49
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,25 @@ 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);
386-
let ident_sp = self.token.span;
387-
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-
}) {
384+
let is_pub = self.prev_token.is_keyword(kw::Pub);
385+
let is_const = self.prev_token.is_keyword(kw::Const);
386+
let ident_span = self.token.span;
387+
let span = if is_pub { self.prev_token.span.to(ident_span) } else { ident_span };
388+
let insert_span = ident_span.shrink_to_lo();
389+
390+
let ident = if (!is_const
391+
|| self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Parenthesis)))
392+
&& self.look_ahead(1, |t| {
393+
[
394+
token::Lt,
395+
token::OpenDelim(Delimiter::Brace),
396+
token::OpenDelim(Delimiter::Parenthesis),
397+
]
398+
.contains(&t.kind)
399+
}) {
396400
self.parse_ident().unwrap()
397401
} else {
398402
return Ok(());
@@ -406,46 +410,56 @@ impl<'a> Parser<'a> {
406410
}
407411

408412
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 })
413+
// possible struct or enum definition where `struct` or `enum` was forgotten
414+
if self.look_ahead(1, |t| *t == token::CloseDelim(Delimiter::Brace)) {
415+
// `S {}` could be unit enum or struct
416+
Some(errors::MissingKeywordForItemDefinition::EnumOrStruct { span })
417+
} else if self.look_ahead(2, |t| *t == token::Colon)
418+
|| self.look_ahead(3, |t| *t == token::Colon)
419+
{
420+
// `S { f:` or `S { pub f:`
421+
Some(errors::MissingKeywordForItemDefinition::Struct { span, insert_span, ident })
422+
} else {
423+
Some(errors::MissingKeywordForItemDefinition::Enum { span, insert_span, ident })
424+
}
411425
} else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
412-
// possible public function or tuple struct definition where `fn`/`struct` was
413-
// forgotten
426+
// possible function or tuple struct definition where `fn` or `struct` was forgotten
414427
self.bump(); // `(`
415428
let is_method = self.recover_self_param();
416429

417430
self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes);
418431

419-
let err =
420-
if self.check(&token::RArrow) || self.check(&token::OpenDelim(Delimiter::Brace)) {
421-
self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
422-
self.bump(); // `{`
423-
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
424-
if is_method {
425-
errors::MissingKeywordForItemDefinition::Method { span: sp, ident }
426-
} else {
427-
errors::MissingKeywordForItemDefinition::Function { span: sp, ident }
428-
}
429-
} else if self.check(&token::Semi) {
430-
errors::MissingKeywordForItemDefinition::Struct { span: sp, ident }
432+
let err = if self.check(&token::RArrow)
433+
|| self.check(&token::OpenDelim(Delimiter::Brace))
434+
{
435+
self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
436+
self.bump(); // `{`
437+
self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
438+
if is_method {
439+
errors::MissingKeywordForItemDefinition::Method { span, insert_span, ident }
431440
} else {
432-
errors::MissingKeywordForItemDefinition::Ambiguous {
433-
span: sp,
434-
subdiag: if found_generics {
435-
None
436-
} else if let Ok(snippet) = self.span_to_snippet(ident_sp) {
437-
Some(errors::AmbiguousMissingKwForItemSub::SuggestMacro {
438-
span: full_sp,
439-
snippet,
440-
})
441-
} else {
442-
Some(errors::AmbiguousMissingKwForItemSub::HelpMacro)
443-
},
444-
}
445-
};
441+
errors::MissingKeywordForItemDefinition::Function { span, insert_span, ident }
442+
}
443+
} else if is_pub && self.check(&token::Semi) {
444+
errors::MissingKeywordForItemDefinition::Struct { span, insert_span, ident }
445+
} else {
446+
errors::MissingKeywordForItemDefinition::Ambiguous {
447+
span,
448+
subdiag: if found_generics {
449+
None
450+
} else if let Ok(snippet) = self.span_to_snippet(ident_span) {
451+
Some(errors::AmbiguousMissingKwForItemSub::SuggestMacro {
452+
span: ident_span,
453+
snippet,
454+
})
455+
} else {
456+
Some(errors::AmbiguousMissingKwForItemSub::HelpMacro)
457+
},
458+
}
459+
};
446460
Some(err)
447461
} else if found_generics {
448-
Some(errors::MissingKeywordForItemDefinition::Ambiguous { span: sp, subdiag: None })
462+
Some(errors::MissingKeywordForItemDefinition::Ambiguous { span, subdiag: None })
449463
} else {
450464
None
451465
};

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:5
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:5
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

0 commit comments

Comments
 (0)