diff --git a/compiler/rustc_error_messages/locales/en-US/parse.ftl b/compiler/rustc_error_messages/locales/en-US/parse.ftl index b53550e5fd5..3401978caf5 100644 --- a/compiler/rustc_error_messages/locales/en-US/parse.ftl +++ b/compiler/rustc_error_messages/locales/en-US/parse.ftl @@ -365,3 +365,6 @@ parse_invalid_identifier_with_leading_number = expected identifier, found number parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn` .suggestion = replace `fn` with `impl` here + +parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn` + .suggestion = use `Fn` to refer to the trait diff --git a/compiler/rustc_expand/src/build.rs b/compiler/rustc_expand/src/build.rs index ef50efb8125..406e1569a6f 100644 --- a/compiler/rustc_expand/src/build.rs +++ b/compiler/rustc_expand/src/build.rs @@ -626,7 +626,7 @@ impl<'a> ExtCtxt<'a> { // Builds `#[name = val]`. // - // Note: `span` is used for both the identifer and the value. + // Note: `span` is used for both the identifier and the value. pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute { let g = &self.sess.parse_sess.attr_id_generator; attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span) diff --git a/compiler/rustc_lexer/src/lib.rs b/compiler/rustc_lexer/src/lib.rs index 50d6d5b9bab..4c65fca29b8 100644 --- a/compiler/rustc_lexer/src/lib.rs +++ b/compiler/rustc_lexer/src/lib.rs @@ -851,7 +851,7 @@ impl Cursor<'_> { } // Eats the identifier. Note: succeeds on `_`, which isn't a valid - // identifer. + // identifier. fn eat_identifier(&mut self) { if !is_id_start(self.first()) { return; diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 18a0bee9c2e..574591529f3 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -1229,3 +1229,11 @@ pub(crate) struct FnTypoWithImpl { #[suggestion(applicability = "maybe-incorrect", code = "impl", style = "verbose")] pub fn_span: Span, } + +#[derive(Diagnostic)] +#[diag(parse_expected_fn_path_found_fn_keyword)] +pub(crate) struct ExpectedFnPathFoundFnKeyword { + #[primary_span] + #[suggestion(applicability = "machine-applicable", code = "Fn", style = "verbose")] + pub fn_token_span: Span, +} diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index b7206b57642..a4b0f66e1ec 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -1,8 +1,9 @@ use super::{Parser, PathStyle, TokenType}; -use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg}; +use crate::errors::{ExpectedFnPathFoundFnKeyword, FnPtrWithGenerics, FnPtrWithGenericsSugg}; use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole}; +use ast::DUMMY_NODE_ID; use rustc_ast::ptr::P; use rustc_ast::token::{self, Delimiter, Token, TokenKind}; use rustc_ast::util::case::Case; @@ -12,7 +13,9 @@ use rustc_ast::{ }; use rustc_errors::{pluralize, struct_span_err, Applicability, PResult}; use rustc_span::source_map::Span; -use rustc_span::symbol::{kw, sym}; +use rustc_span::symbol::{kw, sym, Ident}; +use rustc_span::Symbol; +use thin_vec::thin_vec; /// Any `?` or `~const` modifiers that appear at the start of a bound. struct BoundModifiers { @@ -912,7 +915,14 @@ impl<'a> Parser<'a> { modifiers: BoundModifiers, ) -> PResult<'a, GenericBound> { let lifetime_defs = self.parse_late_bound_lifetime_defs()?; - let path = self.parse_path(PathStyle::Type)?; + let path = if self.token.is_keyword(kw::Fn) + && self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis)) + && let Some(path) = self.recover_path_from_fn() + { + path + } else { + self.parse_path(PathStyle::Type)? + }; if has_parens { if self.token.is_like_plus() { // Someone has written something like `&dyn (Trait + Other)`. The correct code @@ -941,6 +951,38 @@ impl<'a> Parser<'a> { Ok(GenericBound::Trait(poly_trait, modifier)) } + // recovers a `Fn(..)` parenthesized-style path from `fn(..)` + fn recover_path_from_fn(&mut self) -> Option { + let fn_token_span = self.token.span; + self.bump(); + let args_lo = self.token.span; + let snapshot = self.create_snapshot_for_diagnostic(); + match self.parse_fn_decl(|_| false, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) { + Ok(decl) => { + self.sess.emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span }); + Some(ast::Path { + span: fn_token_span.to(self.prev_token.span), + segments: thin_vec![ast::PathSegment { + ident: Ident::new(Symbol::intern("Fn"), fn_token_span), + id: DUMMY_NODE_ID, + args: Some(P(ast::GenericArgs::Parenthesized(ast::ParenthesizedArgs { + span: args_lo.to(self.prev_token.span), + inputs: decl.inputs.iter().map(|a| a.ty.clone()).collect(), + inputs_span: args_lo.until(decl.output.span()), + output: decl.output.clone(), + }))), + }], + tokens: None, + }) + } + Err(diag) => { + diag.cancel(); + self.restore_snapshot(snapshot); + None + } + } + } + /// Optionally parses `for<$generic_params>`. pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec> { if self.eat_keyword(kw::For) { diff --git a/src/test/ui/parser/kw-in-trait-bounds.rs b/src/test/ui/parser/kw-in-trait-bounds.rs index fa037e5937d..e9e85339aff 100644 --- a/src/test/ui/parser/kw-in-trait-bounds.rs +++ b/src/test/ui/parser/kw-in-trait-bounds.rs @@ -4,21 +4,13 @@ fn _f(_: impl fn(), _: &dyn fn()) //~^ ERROR expected identifier, found keyword `fn` //~| ERROR expected identifier, found keyword `fn` //~| ERROR expected identifier, found keyword `fn` -//~| ERROR cannot find trait `r#fn` in this scope -//~| ERROR cannot find trait `r#fn` in this scope -//~| ERROR cannot find trait `r#fn` in this scope -//~| HELP a trait with a similar name exists -//~| HELP a trait with a similar name exists -//~| HELP a trait with a similar name exists -//~| HELP escape `fn` to use it as an identifier -//~| HELP escape `fn` to use it as an identifier -//~| HELP escape `fn` to use it as an identifier +//~| HELP use `Fn` to refer to the trait +//~| HELP use `Fn` to refer to the trait +//~| HELP use `Fn` to refer to the trait where G: fn(), //~^ ERROR expected identifier, found keyword `fn` - //~| ERROR cannot find trait `r#fn` in this scope - //~| HELP a trait with a similar name exists - //~| HELP escape `fn` to use it as an identifier + //~| HELP use `Fn` to refer to the trait {} fn _g(_: impl struct, _: &dyn struct) diff --git a/src/test/ui/parser/kw-in-trait-bounds.stderr b/src/test/ui/parser/kw-in-trait-bounds.stderr index 79643660e8b..2d3aad4d6ba 100644 --- a/src/test/ui/parser/kw-in-trait-bounds.stderr +++ b/src/test/ui/parser/kw-in-trait-bounds.stderr @@ -2,48 +2,48 @@ error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:3:10 | LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ expected identifier, found keyword + | ^^ | -help: escape `fn` to use it as an identifier +help: use `Fn` to refer to the trait | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ++ +LL | fn _f(_: impl fn(), _: &dyn fn()) + | ~~ error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:3:27 | LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ expected identifier, found keyword + | ^^ | -help: escape `fn` to use it as an identifier +help: use `Fn` to refer to the trait | -LL | fn _f(_: impl r#fn(), _: &dyn fn()) - | ++ +LL | fn _f(_: impl Fn(), _: &dyn fn()) + | ~~ error: expected identifier, found keyword `fn` --> $DIR/kw-in-trait-bounds.rs:3:41 | LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ expected identifier, found keyword + | ^^ | -help: escape `fn` to use it as an identifier +help: use `Fn` to refer to the trait | -LL | fn _f(_: impl fn(), _: &dyn r#fn()) - | ++ +LL | fn _f(_: impl fn(), _: &dyn Fn()) + | ~~ error: expected identifier, found keyword `fn` - --> $DIR/kw-in-trait-bounds.rs:17:4 + --> $DIR/kw-in-trait-bounds.rs:11:4 | LL | G: fn(), - | ^^ expected identifier, found keyword + | ^^ | -help: escape `fn` to use it as an identifier +help: use `Fn` to refer to the trait | -LL | G: r#fn(), - | ++ +LL | G: Fn(), + | ~~ error: expected identifier, found keyword `struct` - --> $DIR/kw-in-trait-bounds.rs:24:10 + --> $DIR/kw-in-trait-bounds.rs:16:10 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ expected identifier, found keyword @@ -54,7 +54,7 @@ LL | fn _g(_: impl struct, _: &dyn struct) | ++ error: expected identifier, found keyword `struct` - --> $DIR/kw-in-trait-bounds.rs:24:29 + --> $DIR/kw-in-trait-bounds.rs:16:29 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ expected identifier, found keyword @@ -65,7 +65,7 @@ LL | fn _g(_: impl r#struct, _: &dyn struct) | ++ error: expected identifier, found keyword `struct` - --> $DIR/kw-in-trait-bounds.rs:24:45 + --> $DIR/kw-in-trait-bounds.rs:16:45 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ expected identifier, found keyword @@ -76,7 +76,7 @@ LL | fn _g(_: impl struct, _: &dyn r#struct) | ++ error: expected identifier, found keyword `struct` - --> $DIR/kw-in-trait-bounds.rs:38:8 + --> $DIR/kw-in-trait-bounds.rs:30:8 | LL | B: struct, | ^^^^^^ expected identifier, found keyword @@ -86,44 +86,8 @@ help: escape `struct` to use it as an identifier LL | B: r#struct, | ++ -error[E0405]: cannot find trait `r#fn` in this scope - --> $DIR/kw-in-trait-bounds.rs:3:10 - | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - | - = note: similarly named trait `Fn` defined here - -error[E0405]: cannot find trait `r#fn` in this scope - --> $DIR/kw-in-trait-bounds.rs:17:4 - | -LL | G: fn(), - | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - | - = note: similarly named trait `Fn` defined here - -error[E0405]: cannot find trait `r#fn` in this scope - --> $DIR/kw-in-trait-bounds.rs:3:27 - | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - | - = note: similarly named trait `Fn` defined here - -error[E0405]: cannot find trait `r#fn` in this scope - --> $DIR/kw-in-trait-bounds.rs:3:41 - | -LL | fn _f(_: impl fn(), _: &dyn fn()) - | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` - --> $SRC_DIR/core/src/ops/function.rs:LL:COL - | - = note: similarly named trait `Fn` defined here - error[E0405]: cannot find trait `r#struct` in this scope - --> $DIR/kw-in-trait-bounds.rs:24:10 + --> $DIR/kw-in-trait-bounds.rs:16:10 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct` @@ -132,7 +96,7 @@ LL | trait Struct {} | ------------ similarly named trait `Struct` defined here error[E0405]: cannot find trait `r#struct` in this scope - --> $DIR/kw-in-trait-bounds.rs:38:8 + --> $DIR/kw-in-trait-bounds.rs:30:8 | LL | B: struct, | ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct` @@ -141,7 +105,7 @@ LL | trait Struct {} | ------------ similarly named trait `Struct` defined here error[E0405]: cannot find trait `r#struct` in this scope - --> $DIR/kw-in-trait-bounds.rs:24:29 + --> $DIR/kw-in-trait-bounds.rs:16:29 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct` @@ -150,7 +114,7 @@ LL | trait Struct {} | ------------ similarly named trait `Struct` defined here error[E0405]: cannot find trait `r#struct` in this scope - --> $DIR/kw-in-trait-bounds.rs:24:45 + --> $DIR/kw-in-trait-bounds.rs:16:45 | LL | fn _g(_: impl struct, _: &dyn struct) | ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct` @@ -158,6 +122,6 @@ LL | fn _g(_: impl struct, _: &dyn struct) LL | trait Struct {} | ------------ similarly named trait `Struct` defined here -error: aborting due to 16 previous errors +error: aborting due to 12 previous errors For more information about this error, try `rustc --explain E0405`. diff --git a/src/test/ui/parser/recover-fn-trait-from-fn-kw.rs b/src/test/ui/parser/recover-fn-trait-from-fn-kw.rs new file mode 100644 index 00000000000..b6611e6273d --- /dev/null +++ b/src/test/ui/parser/recover-fn-trait-from-fn-kw.rs @@ -0,0 +1,12 @@ +fn foo(_: impl fn() -> i32) {} +//~^ ERROR expected identifier, found keyword `fn` + +fn foo2(_: T) {} +//~^ ERROR expected identifier, found keyword `fn` + +fn main() { + foo(|| ()); + //~^ mismatched types + foo2(|_: ()| {}); + //~^ type mismatch in closure arguments +} diff --git a/src/test/ui/parser/recover-fn-trait-from-fn-kw.stderr b/src/test/ui/parser/recover-fn-trait-from-fn-kw.stderr new file mode 100644 index 00000000000..3681a796c53 --- /dev/null +++ b/src/test/ui/parser/recover-fn-trait-from-fn-kw.stderr @@ -0,0 +1,48 @@ +error: expected identifier, found keyword `fn` + --> $DIR/recover-fn-trait-from-fn-kw.rs:1:16 + | +LL | fn foo(_: impl fn() -> i32) {} + | ^^ + | +help: use `Fn` to refer to the trait + | +LL | fn foo(_: impl Fn() -> i32) {} + | ~~ + +error: expected identifier, found keyword `fn` + --> $DIR/recover-fn-trait-from-fn-kw.rs:4:12 + | +LL | fn foo2(_: T) {} + | ^^ + | +help: use `Fn` to refer to the trait + | +LL | fn foo2(_: T) {} + | ~~ + +error[E0308]: mismatched types + --> $DIR/recover-fn-trait-from-fn-kw.rs:8:12 + | +LL | foo(|| ()); + | ^^ expected `i32`, found `()` + +error[E0631]: type mismatch in closure arguments + --> $DIR/recover-fn-trait-from-fn-kw.rs:10:5 + | +LL | foo2(|_: ()| {}); + | ^^^^ ------- found signature defined here + | | + | expected due to this + | + = note: expected closure signature `fn(i32) -> _` + found closure signature `fn(()) -> _` +note: required by a bound in `foo2` + --> $DIR/recover-fn-trait-from-fn-kw.rs:4:12 + | +LL | fn foo2(_: T) {} + | ^^^^^^^ required by this bound in `foo2` + +error: aborting due to 4 previous errors + +Some errors have detailed explanations: E0308, E0631. +For more information about an error, try `rustc --explain E0308`.