about summary refs log tree commit diff
diff options
context:
space:
mode:
authorMichael Goulet <michael@errs.io>2022-12-27 06:14:40 +0000
committerMichael Goulet <michael@errs.io>2022-12-27 06:14:46 +0000
commitaff403cf689e30fdd20b96333d3a10db92086e4f (patch)
tree026a00919d467b4b9ccd637c6e460301b3fd1c53
parentcaa64e5b5e7605a1c1428b2a402021bef83f3e1e (diff)
downloadrust-aff403cf689e30fdd20b96333d3a10db92086e4f.tar.gz
rust-aff403cf689e30fdd20b96333d3a10db92086e4f.zip
Recover `fn` keyword as `Fn` trait in bounds
-rw-r--r--compiler/rustc_error_messages/locales/en-US/parse.ftl3
-rw-r--r--compiler/rustc_expand/src/build.rs2
-rw-r--r--compiler/rustc_lexer/src/lib.rs2
-rw-r--r--compiler/rustc_parse/src/errors.rs8
-rw-r--r--compiler/rustc_parse/src/parser/ty.rs48
-rw-r--r--src/test/ui/parser/kw-in-trait-bounds.rs16
-rw-r--r--src/test/ui/parser/kw-in-trait-bounds.stderr88
-rw-r--r--src/test/ui/parser/recover-fn-trait-from-fn-kw.rs12
-rw-r--r--src/test/ui/parser/recover-fn-trait-from-fn-kw.stderr48
9 files changed, 148 insertions, 79 deletions
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<ast::Path> {
+        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<GenericParam>> {
         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<F: fn(), G>(_: 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<A: struct, B>(_: 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<F: fn(), G>(_: 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<F: r#fn(), G>(_: impl fn(), _: &dyn fn())
-   |          ++
+LL | fn _f<F: Fn(), G>(_: impl fn(), _: &dyn fn())
+   |          ~~
 
 error: expected identifier, found keyword `fn`
   --> $DIR/kw-in-trait-bounds.rs:3:27
    |
 LL | fn _f<F: fn(), G>(_: 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<F: fn(), G>(_: impl r#fn(), _: &dyn fn())
-   |                           ++
+LL | fn _f<F: fn(), G>(_: impl Fn(), _: &dyn fn())
+   |                           ~~
 
 error: expected identifier, found keyword `fn`
   --> $DIR/kw-in-trait-bounds.rs:3:41
    |
 LL | fn _f<F: fn(), G>(_: 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<F: fn(), G>(_: impl fn(), _: &dyn r#fn())
-   |                                         ++
+LL | fn _f<F: fn(), G>(_: 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<A: struct, B>(_: impl struct, _: &dyn struct)
    |          ^^^^^^ expected identifier, found keyword
@@ -54,7 +54,7 @@ LL | fn _g<A: r#struct, B>(_: 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<A: struct, B>(_: impl struct, _: &dyn struct)
    |                             ^^^^^^ expected identifier, found keyword
@@ -65,7 +65,7 @@ LL | fn _g<A: struct, B>(_: 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<A: struct, B>(_: impl struct, _: &dyn struct)
    |                                             ^^^^^^ expected identifier, found keyword
@@ -76,7 +76,7 @@ LL | fn _g<A: struct, B>(_: 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<F: fn(), G>(_: 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<F: fn(), G>(_: 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<F: fn(), G>(_: 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<A: struct, B>(_: 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<A: struct, B>(_: 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<A: struct, B>(_: impl struct, _: &dyn struct)
    |                                             ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
@@ -158,6 +122,6 @@ LL | fn _g<A: struct, B>(_: 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: fn(i32)>(_: 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: fn(i32)>(_: T) {}
+   |            ^^
+   |
+help: use `Fn` to refer to the trait
+   |
+LL | fn foo2<T: Fn(i32)>(_: 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: fn(i32)>(_: 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`.