From 5834772177540912b53dc4f19413a4ba3c804ea4 Mon Sep 17 00:00:00 2001 From: Veera Date: Thu, 4 Jul 2024 19:16:47 -0400 Subject: [PATCH 1/2] Update Tests --- ...ced-return-type-complex-type-issue-126311.rs | 5 +++++ ...return-type-complex-type-issue-126311.stderr | 14 ++++++++++++++ .../misplaced-return-type-issue-126311.rs | 5 +++++ .../misplaced-return-type-issue-126311.stderr | 14 ++++++++++++++ ...turn-type-where-in-next-line-issue-126311.rs | 11 +++++++++++ ...-type-where-in-next-line-issue-126311.stderr | 17 +++++++++++++++++ ...ced-return-type-without-type-issue-126311.rs | 6 ++++++ ...return-type-without-type-issue-126311.stderr | 8 ++++++++ ...ed-return-type-without-where-issue-126311.rs | 4 ++++ ...eturn-type-without-where-issue-126311.stderr | 8 ++++++++ 10 files changed, 92 insertions(+) create mode 100644 tests/ui/parser/issues/misplaced-return-type-complex-type-issue-126311.rs create mode 100644 tests/ui/parser/issues/misplaced-return-type-complex-type-issue-126311.stderr create mode 100644 tests/ui/parser/issues/misplaced-return-type-issue-126311.rs create mode 100644 tests/ui/parser/issues/misplaced-return-type-issue-126311.stderr create mode 100644 tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.rs create mode 100644 tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.stderr create mode 100644 tests/ui/parser/issues/misplaced-return-type-without-type-issue-126311.rs create mode 100644 tests/ui/parser/issues/misplaced-return-type-without-type-issue-126311.stderr create mode 100644 tests/ui/parser/issues/misplaced-return-type-without-where-issue-126311.rs create mode 100644 tests/ui/parser/issues/misplaced-return-type-without-where-issue-126311.stderr diff --git a/tests/ui/parser/issues/misplaced-return-type-complex-type-issue-126311.rs b/tests/ui/parser/issues/misplaced-return-type-complex-type-issue-126311.rs new file mode 100644 index 0000000000000..722303dd03482 --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-complex-type-issue-126311.rs @@ -0,0 +1,5 @@ +fn foo() where T: Default -> impl Default + 'static {} +//~^ ERROR return type should be specified after the function parameters +//~| HELP place the return type after the function parameters + +fn main() {} diff --git a/tests/ui/parser/issues/misplaced-return-type-complex-type-issue-126311.stderr b/tests/ui/parser/issues/misplaced-return-type-complex-type-issue-126311.stderr new file mode 100644 index 0000000000000..2ce3b78bb8ba9 --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-complex-type-issue-126311.stderr @@ -0,0 +1,14 @@ +error: return type should be specified after the function parameters + --> $DIR/misplaced-return-type-complex-type-issue-126311.rs:1:30 + | +LL | fn foo() where T: Default -> impl Default + 'static {} + | ^^ expected one of `(`, `+`, `,`, `::`, `<`, or `{` + | +help: place the return type after the function parameters + | +LL - fn foo() where T: Default -> impl Default + 'static {} +LL + fn foo() -> impl Default + 'static where T: Default {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/issues/misplaced-return-type-issue-126311.rs b/tests/ui/parser/issues/misplaced-return-type-issue-126311.rs new file mode 100644 index 0000000000000..ad164f77beea6 --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-issue-126311.rs @@ -0,0 +1,5 @@ +fn foo() where T: Default -> u8 {} +//~^ ERROR return type should be specified after the function parameters +//~| HELP place the return type after the function parameters + +fn main() {} diff --git a/tests/ui/parser/issues/misplaced-return-type-issue-126311.stderr b/tests/ui/parser/issues/misplaced-return-type-issue-126311.stderr new file mode 100644 index 0000000000000..e473b902ce3ef --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-issue-126311.stderr @@ -0,0 +1,14 @@ +error: return type should be specified after the function parameters + --> $DIR/misplaced-return-type-issue-126311.rs:1:30 + | +LL | fn foo() where T: Default -> u8 {} + | ^^ expected one of `(`, `+`, `,`, `::`, `<`, or `{` + | +help: place the return type after the function parameters + | +LL - fn foo() where T: Default -> u8 {} +LL + fn foo() -> u8 where T: Default {} + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.rs b/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.rs new file mode 100644 index 0000000000000..782d7d5ee4905 --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.rs @@ -0,0 +1,11 @@ +fn foo() +//~^ HELP place the return type after the function parameters +where + T: Default, + K: Clone, -> Result +//~^ ERROR return type should be specified after the function parameters +{ + Ok(0) +} + +fn main() {} diff --git a/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.stderr b/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.stderr new file mode 100644 index 0000000000000..196a46d7ea54b --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-where-in-next-line-issue-126311.stderr @@ -0,0 +1,17 @@ +error: return type should be specified after the function parameters + --> $DIR/misplaced-return-type-where-in-next-line-issue-126311.rs:5:15 + | +LL | K: Clone, -> Result + | ^^ expected one of `{`, lifetime, or type + | +help: place the return type after the function parameters + | +LL ~ fn foo() -> Result +LL | +LL | where +LL | T: Default, +LL ~ K: Clone, + | + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/issues/misplaced-return-type-without-type-issue-126311.rs b/tests/ui/parser/issues/misplaced-return-type-without-type-issue-126311.rs new file mode 100644 index 0000000000000..2c09edbc7926c --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-without-type-issue-126311.rs @@ -0,0 +1,6 @@ +fn foo() where T: Default -> { +//~^ ERROR expected one of `(`, `+`, `,`, `::`, `<`, or `{`, found `->` + 0 +} + +fn main() {} diff --git a/tests/ui/parser/issues/misplaced-return-type-without-type-issue-126311.stderr b/tests/ui/parser/issues/misplaced-return-type-without-type-issue-126311.stderr new file mode 100644 index 0000000000000..0eb3bb7d8126e --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-without-type-issue-126311.stderr @@ -0,0 +1,8 @@ +error: expected one of `(`, `+`, `,`, `::`, `<`, or `{`, found `->` + --> $DIR/misplaced-return-type-without-type-issue-126311.rs:1:30 + | +LL | fn foo() where T: Default -> { + | ^^ expected one of `(`, `+`, `,`, `::`, `<`, or `{` + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/issues/misplaced-return-type-without-where-issue-126311.rs b/tests/ui/parser/issues/misplaced-return-type-without-where-issue-126311.rs new file mode 100644 index 0000000000000..672233674a058 --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-without-where-issue-126311.rs @@ -0,0 +1,4 @@ +fn bar() -> u8 -> u64 {} +//~^ ERROR expected one of `!`, `(`, `+`, `::`, `<`, `where`, or `{`, found `->` + +fn main() {} diff --git a/tests/ui/parser/issues/misplaced-return-type-without-where-issue-126311.stderr b/tests/ui/parser/issues/misplaced-return-type-without-where-issue-126311.stderr new file mode 100644 index 0000000000000..730904d3671f1 --- /dev/null +++ b/tests/ui/parser/issues/misplaced-return-type-without-where-issue-126311.stderr @@ -0,0 +1,8 @@ +error: expected one of `!`, `(`, `+`, `::`, `<`, `where`, or `{`, found `->` + --> $DIR/misplaced-return-type-without-where-issue-126311.rs:1:19 + | +LL | fn bar() -> u8 -> u64 {} + | ^^ expected one of 7 possible tokens + +error: aborting due to 1 previous error + From 86d6f1312a77997ef994240e716288d61a343a6d Mon Sep 17 00:00:00 2001 From: Veera Date: Thu, 4 Jul 2024 19:19:15 -0400 Subject: [PATCH 2/2] Parser: Suggest Placing the Return Type After Function Parameters --- compiler/rustc_parse/messages.ftl | 2 + compiler/rustc_parse/src/errors.rs | 22 +++++--- compiler/rustc_parse/src/parser/item.rs | 73 +++++++++++++++++++++---- compiler/rustc_parse/src/parser/ty.rs | 5 ++ 4 files changed, 83 insertions(+), 19 deletions(-) diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index f08efe60d96b8..984d402390327 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -526,6 +526,8 @@ parse_mismatched_closing_delimiter = mismatched closing delimiter: `{$delimiter} .label_opening_candidate = closing delimiter possibly meant for this .label_unclosed = unclosed delimiter +parse_misplaced_return_type = place the return type after the function parameters + parse_missing_comma_after_match_arm = expected `,` following `match` arm .suggestion = missing a comma here to end this `match` arm diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 8d49887f16441..05bc0686e8efe 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1434,6 +1434,20 @@ pub(crate) struct FnPtrWithGenerics { pub sugg: Option, } +#[derive(Subdiagnostic)] +#[multipart_suggestion( + parse_misplaced_return_type, + style = "verbose", + applicability = "maybe-incorrect" +)] +pub(crate) struct MisplacedReturnType { + #[suggestion_part(code = " {snippet}")] + pub fn_params_end: Span, + pub snippet: String, + #[suggestion_part(code = "")] + pub ret_ty_span: Span, +} + #[derive(Subdiagnostic)] #[multipart_suggestion(parse_suggestion, applicability = "maybe-incorrect")] pub(crate) struct FnPtrWithGenericsSugg { @@ -1448,7 +1462,6 @@ pub(crate) struct FnPtrWithGenericsSugg { pub(crate) struct FnTraitMissingParen { pub span: Span, - pub machine_applicable: bool, } impl Subdiagnostic for FnTraitMissingParen { @@ -1458,16 +1471,11 @@ impl Subdiagnostic for FnTraitMissingParen { _: &F, ) { diag.span_label(self.span, crate::fluent_generated::parse_fn_trait_missing_paren); - let applicability = if self.machine_applicable { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; diag.span_suggestion_short( self.span.shrink_to_hi(), crate::fluent_generated::parse_add_paren, "()", - applicability, + Applicability::MachineApplicable, ); } } diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index abb6b51cebd68..11fe6d5493a84 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2332,10 +2332,20 @@ impl<'a> Parser<'a> { } } }; + + // Store the end of function parameters to give better diagnostics + // inside `parse_fn_body()`. + let fn_params_end = self.prev_token.span.shrink_to_hi(); + generics.where_clause = self.parse_where_clause()?; // `where T: Ord` + // `fn_params_end` is needed only when it's followed by a where clause. + let fn_params_end = + if generics.where_clause.has_where_token { Some(fn_params_end) } else { None }; + let mut sig_hi = self.prev_token.span; - let body = self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body)?; // `;` or `{ ... }`. + let body = + self.parse_fn_body(attrs, &ident, &mut sig_hi, fn_parse_mode.req_body, fn_params_end)?; // `;` or `{ ... }`. let fn_sig_span = sig_lo.to(sig_hi); Ok((ident, FnSig { header, decl, span: fn_sig_span }, generics, body)) } @@ -2349,6 +2359,7 @@ impl<'a> Parser<'a> { ident: &Ident, sig_hi: &mut Span, req_body: bool, + fn_params_end: Option, ) -> PResult<'a, Option>> { let has_semi = if req_body { self.token.kind == TokenKind::Semi @@ -2388,20 +2399,58 @@ impl<'a> Parser<'a> { // the AST for typechecking. err.span_label(ident.span, "while parsing this `fn`"); err.emit(); - } else { - // check for typo'd Fn* trait bounds such as - // fn foo() where F: FnOnce -> () {} - if self.token.kind == token::RArrow { - let machine_applicable = [sym::FnOnce, sym::FnMut, sym::Fn] - .into_iter() - .any(|s| self.prev_token.is_ident_named(s)); - - err.subdiagnostic(errors::FnTraitMissingParen { - span: self.prev_token.span, - machine_applicable, + } else if self.token.kind == token::RArrow + && let Some(fn_params_end) = fn_params_end + { + // Instead of a function body, the parser has encountered a right arrow + // preceded by a where clause. + + // Find whether token behind the right arrow is a function trait and + // store its span. + let prev_token_is_fn_trait = [sym::FnOnce, sym::FnMut, sym::Fn] + .into_iter() + .any(|s| self.prev_token.is_ident_named(s)); + let fn_trait_span = self.prev_token.span; + + // Parse the return type (along with the right arrow) and store its span. + // If there's a parse error, cancel it and return the existing error + // as we are primarily concerned with the + // expected-function-body-but-found-something-else error here. + let arrow_span = self.token.span; + let ty_span = match self.parse_ret_ty( + AllowPlus::Yes, + RecoverQPath::Yes, + RecoverReturnSign::Yes, + ) { + Ok(ty_span) => ty_span.span().shrink_to_hi(), + Err(parse_error) => { + parse_error.cancel(); + return Err(err); + } + }; + let ret_ty_span = arrow_span.to(ty_span); + + if prev_token_is_fn_trait { + // Typo'd Fn* trait bounds such as + // fn foo() where F: FnOnce -> () {} + err.subdiagnostic(errors::FnTraitMissingParen { span: fn_trait_span }); + } else if let Ok(snippet) = self.psess.source_map().span_to_snippet(ret_ty_span) + { + // If token behind right arrow is not a Fn* trait, the programmer + // probably misplaced the return type after the where clause like + // `fn foo() where T: Default -> u8 {}` + err.primary_message( + "return type should be specified after the function parameters", + ); + err.subdiagnostic(errors::MisplacedReturnType { + fn_params_end, + snippet, + ret_ty_span, }); } return Err(err); + } else { + return Err(err); } } (AttrVec::new(), None) diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index 1e5b227aaa9be..17a0829c6d2f6 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -21,6 +21,11 @@ use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::{ErrorGuaranteed, Span, Symbol}; use thin_vec::{thin_vec, ThinVec}; +/// Signals whether parsing a type should allow `+`. +/// +/// For example, let T be the type `impl Default + 'static` +/// With `AllowPlus::Yes`, T will be parsed successfully +/// With `AllowPlus::No`, parsing T will return a parse error #[derive(Copy, Clone, PartialEq)] pub(super) enum AllowPlus { Yes,