From 904aceec7d9595d49b1ce6e9e8a04f64f6a814fd Mon Sep 17 00:00:00 2001 From: clubby789 Date: Fri, 27 Oct 2023 19:51:51 +0000 Subject: [PATCH] Give a better diagnostic for missing parens in Fn* bounds --- compiler/rustc_parse/messages.ftl | 3 ++ compiler/rustc_parse/src/errors.rs | 28 +++++++++++++++++++ compiler/rustc_parse/src/parser/item.rs | 12 ++++++++ compiler/rustc_span/src/symbol.rs | 3 ++ .../issue-108109-fn-trait-missing-paren.fixed | 8 ++++++ .../issue-108109-fn-trait-missing-paren.rs | 8 ++++++ ...issue-108109-fn-trait-missing-paren.stderr | 11 ++++++++ 7 files changed, 73 insertions(+) create mode 100644 tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.fixed create mode 100644 tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.rs create mode 100644 tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.stderr diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index e0778f72bfe6e..8036c68f61810 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -270,6 +270,9 @@ parse_fn_ptr_with_generics = function pointer types may not have generic paramet *[false] a } `for` parameter list +parse_fn_trait_missing_paren = `Fn` bounds require arguments in parentheses + .add_paren = add the missing parentheses + parse_forgot_paren = perhaps you forgot parentheses? parse_found_expr_would_be_stmt = expected expression, found `{$token}` diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index aeb4fd0a304aa..311d573d1fcfd 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1370,6 +1370,34 @@ pub(crate) struct FnPtrWithGenericsSugg { pub for_param_list_exists: bool, } +pub(crate) struct FnTraitMissingParen { + pub span: Span, + pub machine_applicable: bool, +} + +impl AddToDiagnostic for FnTraitMissingParen { + fn add_to_diagnostic_with(self, diag: &mut rustc_errors::Diagnostic, _: F) + where + F: Fn( + &mut rustc_errors::Diagnostic, + rustc_errors::SubdiagnosticMessage, + ) -> rustc_errors::SubdiagnosticMessage, + { + 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, + ); + } +} + #[derive(Diagnostic)] #[diag(parse_unexpected_if_with_if)] pub(crate) struct UnexpectedIfWithIf( diff --git a/compiler/rustc_parse/src/parser/item.rs b/compiler/rustc_parse/src/parser/item.rs index 982f601c0d5a2..aa06fd9df516e 100644 --- a/compiler/rustc_parse/src/parser/item.rs +++ b/compiler/rustc_parse/src/parser/item.rs @@ -2278,6 +2278,18 @@ impl<'a> Parser<'a> { 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, + }); + } return Err(err); } } diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 88d9dab2ba560..8b33c1b825a19 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -193,6 +193,9 @@ symbols! { Error, File, FileType, + Fn, + FnMut, + FnOnce, FormatSpec, Formatter, From, diff --git a/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.fixed b/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.fixed new file mode 100644 index 0000000000000..eaae288864a3d --- /dev/null +++ b/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.fixed @@ -0,0 +1,8 @@ +// run-rustfix + +pub fn func() where F: FnOnce() -> () {} +//~^ ERROR expected one of +//~| NOTE expected one of +//~| NOTE `Fn` bounds require arguments in parentheses + +fn main() {} diff --git a/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.rs b/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.rs new file mode 100644 index 0000000000000..ea5c71150e83c --- /dev/null +++ b/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.rs @@ -0,0 +1,8 @@ +// run-rustfix + +pub fn func() where F: FnOnce -> () {} +//~^ ERROR expected one of +//~| NOTE expected one of +//~| NOTE `Fn` bounds require arguments in parentheses + +fn main() {} diff --git a/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.stderr b/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.stderr new file mode 100644 index 0000000000000..7cda667570de6 --- /dev/null +++ b/tests/ui/parser/issues/issue-108109-fn-trait-missing-paren.stderr @@ -0,0 +1,11 @@ +error: expected one of `(`, `+`, `,`, `::`, `<`, or `{`, found `->` + --> $DIR/issue-108109-fn-trait-missing-paren.rs:3:34 + | +LL | pub fn func() where F: FnOnce -> () {} + | -------^^ expected one of `(`, `+`, `,`, `::`, `<`, or `{` + | | | + | | help: try adding parentheses + | `Fn` bounds require arguments in parentheses + +error: aborting due to previous error +