Skip to content

Improve errors for incomplete functions in struct definitions #102350

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

Merged
merged 2 commits into from
Sep 30, 2022
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
28 changes: 17 additions & 11 deletions compiler/rustc_parse/src/parser/item.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1753,18 +1753,24 @@ impl<'a> Parser<'a> {
};
// We use `parse_fn` to get a span for the function
let fn_parse_mode = FnParseMode { req_name: |_| true, req_body: true };
if let Err(mut db) =
self.parse_fn(&mut AttrVec::new(), fn_parse_mode, lo, &inherited_vis)
{
db.delay_as_bug();
match self.parse_fn(&mut AttrVec::new(), fn_parse_mode, lo, &inherited_vis) {
Ok(_) => {
let mut err = self.struct_span_err(
lo.to(self.prev_token.span),
&format!("functions are not allowed in {adt_ty} definitions"),
);
err.help(
"unlike in C++, Java, and C#, functions are declared in `impl` blocks",
);
err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information");
err
}
Err(err) => {
err.cancel();
self.restore_snapshot(snapshot);
self.expected_ident_found()
}
}
let mut err = self.struct_span_err(
lo.to(self.prev_token.span),
&format!("functions are not allowed in {adt_ty} definitions"),
);
err.help("unlike in C++, Java, and C#, functions are declared in `impl` blocks");
err.help("see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information");
err
} else if self.eat_keyword(kw::Struct) {
match self.parse_item_struct() {
Ok((ident, _)) => {
Expand Down
2 changes: 1 addition & 1 deletion src/test/ui/parser/fn-field-parse-error-ice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
struct Baz {
inner : dyn fn ()
//~^ ERROR expected `,`, or `}`, found keyword `fn`
//~| ERROR functions are not allowed in struct definitions
//~| ERROR expected identifier, found keyword `fn`
//~| ERROR cannot find type `dyn` in this scope
}

Expand Down
10 changes: 6 additions & 4 deletions src/test/ui/parser/fn-field-parse-error-ice.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,18 @@ error: expected `,`, or `}`, found keyword `fn`
LL | inner : dyn fn ()
| ^ help: try adding a comma: `,`

error: functions are not allowed in struct definitions
error: expected identifier, found keyword `fn`
--> $DIR/fn-field-parse-error-ice.rs:4:17
|
LL | struct Baz {
| --- while parsing this struct
LL | inner : dyn fn ()
| ^^
| ^^ expected identifier, found keyword
|
= help: unlike in C++, Java, and C#, functions are declared in `impl` blocks
= help: see https://doc.rust-lang.org/book/ch05-03-method-syntax.html for more information
help: escape `fn` to use it as an identifier
|
LL | inner : dyn r#fn ()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm. This doesn't seem to be useful. They might have meant Fn instead?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In edition 2021, rustc seems suggests like the following:

error: expected identifier, found keyword `fn`
 --> src/main.rs:4:17
  |
4 |     inner : dyn fn ()
  |                 ^^ expected identifier, found keyword
  |
help: escape `fn` to use it as an identifier
  |
4 |     inner : dyn r#fn ()
  |                 ++

error[E0405]: cannot find trait `r#fn` in this scope
  --> src/main.rs:4:17
   |
4  |     inner : dyn fn ()
   |                 ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
   |
  ::: /Users/maeda.takayuki/GitHub/rust/rust/library/core/src/ops/function.rs:75:1
   |
75 | pub trait Fn<Args>: FnMut<Args> {
   | ------------------------------- similarly named trait `Fn` defined here

For more information about this error, try `rustc --explain E0405`.
error: could not compile `debug_playground` due to 2 previous errors

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So could we not emit the first help if we know that it should be a trait? Or maybe just delay this error when we know that there will be path resolution anyways?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if that is doable though, it is fine to do it in a followup

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure if that is doable though, it is fine to do it in a followup

Sure. I will do it as a follow-up.

| ++

error[E0412]: cannot find type `dyn` in this scope
--> $DIR/fn-field-parse-error-ice.rs:4:13
Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/structs/incomplete-fn-in-struct-definition.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fn main() {}

struct S {
fn: u8 //~ ERROR expected identifier, found keyword `fn`
}
15 changes: 15 additions & 0 deletions src/test/ui/structs/incomplete-fn-in-struct-definition.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
error: expected identifier, found keyword `fn`
--> $DIR/incomplete-fn-in-struct-definition.rs:4:5
|
LL | struct S {
| - while parsing this struct
LL | fn: u8
| ^^ expected identifier, found keyword
|
help: escape `fn` to use it as an identifier
|
LL | r#fn: u8
| ++

error: aborting due to previous error