diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_error_messages/locales/en-US/parse.ftl index 114b7ec1628..b53550e5fd5 100644 --- a/compiler/rustc_error_messages/locales/en-US/parse.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parse.ftl @@ -362,3 +362,6 @@ parse_fn_ptr_with_generics = function pointer types may not have generic paramet parse_invalid_identifier_with_leading_number = expected identifier, found number literal .label = identifiers cannot start with a number + +parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn` + .suggestion = replace `fn` with `impl` here diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 9875cde4a05..18a0bee9c2e 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1221,3 +1221,11 @@ pub(crate) struct UnexpectedIfWithIf( #[suggestion(applicability = "machine-applicable", code = " ", style = "verbose")] pub Span, ); + +#[derive(Diagnostic)] +#[diag(parse_maybe_fn_typo_with_impl)] +pub(crate) struct FnTypoWithImpl { + #[primary_span] + #[suggestion(applicability = "maybe-incorrect", code = "impl", style = "verbose")] + pub fn_span: Span, +} diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index beb9d55d454..7c2d01509de 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -3,6 +3,7 @@ use crate::errors::{DocCommentDoesNotDocumentAnything, UseEmptyBlockNotSemi}; use super::diagnostics::{dummy_arg, ConsumeClosingDelim}; use super::ty::{AllowPlus, RecoverQPath, RecoverReturnSign}; use super::{AttrWrapper, FollowedByType, ForceCollect, Parser, PathStyle, TrailingToken}; +use crate::errors::FnTypoWithImpl; use rustc_ast::ast::*; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, TokenKind}; @@ -2131,11 +2132,26 @@ impl<'a> Parser<'a> { vis: &Visibility, case: Case, ) -> PResult<'a, (Ident, FnSig, Generics, Option>)> { + let fn_span = self.token.span; let header = self.parse_fn_front_matter(vis, case)?; // `const ... fn` let ident = self.parse_ident()?; // `foo` let mut generics = self.parse_generics()?; // `<'a, T, ...>` - let decl = - self.parse_fn_decl(fn_parse_mode.req_name, AllowPlus::Yes, RecoverReturnSign::Yes)?; // `(p: u8, ...)` + let decl = match self.parse_fn_decl( + fn_parse_mode.req_name, + AllowPlus::Yes, + RecoverReturnSign::Yes, + ) { + Ok(decl) => decl, + Err(old_err) => { + // If we see `for Ty ...` then user probably meant `impl` item. + if self.token.is_keyword(kw::For) { + old_err.cancel(); + return Err(self.sess.create_err(FnTypoWithImpl { fn_span })); + } else { + return Err(old_err); + } + } + }; generics.where_clause = self.parse_where_clause()?; // `where T: Ord` let mut sig_hi = self.prev_token.span; diff --git a/src/test/ui/parser/issue-105366.fixed b/src/test/ui/parser/issue-105366.fixed new file mode 100644 index 00000000000..ad26643c327 --- /dev/null +++ b/src/test/ui/parser/issue-105366.fixed @@ -0,0 +1,12 @@ +// run-rustfix + +struct Foo; + +impl From for Foo { + //~^ ERROR you might have meant to write `impl` instead of `fn` + fn from(_a: i32) -> Self { + Foo + } +} + +fn main() {} diff --git a/src/test/ui/parser/issue-105366.rs b/src/test/ui/parser/issue-105366.rs new file mode 100644 index 00000000000..311b6a60f1a --- /dev/null +++ b/src/test/ui/parser/issue-105366.rs @@ -0,0 +1,12 @@ +// run-rustfix + +struct Foo; + +fn From for Foo { + //~^ ERROR you might have meant to write `impl` instead of `fn` + fn from(_a: i32) -> Self { + Foo + } +} + +fn main() {} diff --git a/src/test/ui/parser/issue-105366.stderr b/src/test/ui/parser/issue-105366.stderr new file mode 100644 index 00000000000..0a7408e2c17 --- /dev/null +++ b/src/test/ui/parser/issue-105366.stderr @@ -0,0 +1,13 @@ +error: you might have meant to write `impl` instead of `fn` + --> $DIR/issue-105366.rs:5:1 + | +LL | fn From for Foo { + | ^^ + | +help: replace `fn` with `impl` here + | +LL | impl From for Foo { + | ~~~~ + +error: aborting due to previous error +