Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add suggestions for possible missing fn, struct, or enum keywords #127419

Merged
merged 1 commit into from
Jul 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 12 additions & 7 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
@@ -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

Expand Down Expand Up @@ -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
Expand All @@ -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
Expand Down
31 changes: 26 additions & 5 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down
112 changes: 63 additions & 49 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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(());
Expand All @@ -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
};
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/did_you_mean/issue-40006.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,15 +5,15 @@ 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 };
::Y ();
}

trait A {
X() {} //~ ERROR expected one of `!` or `::`, found `(`
X() {} //~ ERROR missing `fn` for function definition
}
trait B {
fn xxx() { ### } //~ ERROR expected
Expand Down
28 changes: 19 additions & 9 deletions tests/ui/did_you_mean/issue-40006.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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) {
| ++
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/mismatched_types/recovered-block.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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() {}
12 changes: 6 additions & 6 deletions tests/ui/mismatched_types/recovered-block.stderr
Original file line number Diff line number Diff line change
@@ -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

4 changes: 3 additions & 1 deletion tests/ui/parser/extern-no-fn.rs
Original file line number Diff line number Diff line change
@@ -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() {
Expand Down
12 changes: 9 additions & 3 deletions tests/ui/parser/extern-no-fn.stderr
Original file line number Diff line number Diff line change
@@ -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

Loading
Loading