Skip to content

Commit d07be1a

Browse files
authoredDec 28, 2022
Rollup merge of #106176 - compiler-errors:fn-kw-as-fn-trait, r=estebank
Recover `fn` keyword as `Fn` trait in bounds `impl fn()` -> `impl Fn()` Fixes #82515
2 parents c991c24 + aff403c commit d07be1a

File tree

9 files changed

+148
-79
lines changed

9 files changed

+148
-79
lines changed
 

‎compiler/rustc_error_messages/locales/en-US/parse.ftl

+3
Original file line numberDiff line numberDiff line change
@@ -365,3 +365,6 @@ parse_invalid_identifier_with_leading_number = expected identifier, found number
365365
366366
parse_maybe_fn_typo_with_impl = you might have meant to write `impl` instead of `fn`
367367
.suggestion = replace `fn` with `impl` here
368+
369+
parse_expected_fn_path_found_fn_keyword = expected identifier, found keyword `fn`
370+
.suggestion = use `Fn` to refer to the trait

‎compiler/rustc_expand/src/build.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -626,7 +626,7 @@ impl<'a> ExtCtxt<'a> {
626626

627627
// Builds `#[name = val]`.
628628
//
629-
// Note: `span` is used for both the identifer and the value.
629+
// Note: `span` is used for both the identifier and the value.
630630
pub fn attr_name_value_str(&self, name: Symbol, val: Symbol, span: Span) -> ast::Attribute {
631631
let g = &self.sess.parse_sess.attr_id_generator;
632632
attr::mk_attr_name_value_str(g, ast::AttrStyle::Outer, name, val, span)

‎compiler/rustc_lexer/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -851,7 +851,7 @@ impl Cursor<'_> {
851851
}
852852

853853
// Eats the identifier. Note: succeeds on `_`, which isn't a valid
854-
// identifer.
854+
// identifier.
855855
fn eat_identifier(&mut self) {
856856
if !is_id_start(self.first()) {
857857
return;

‎compiler/rustc_parse/src/errors.rs

+8
Original file line numberDiff line numberDiff line change
@@ -1229,3 +1229,11 @@ pub(crate) struct FnTypoWithImpl {
12291229
#[suggestion(applicability = "maybe-incorrect", code = "impl", style = "verbose")]
12301230
pub fn_span: Span,
12311231
}
1232+
1233+
#[derive(Diagnostic)]
1234+
#[diag(parse_expected_fn_path_found_fn_keyword)]
1235+
pub(crate) struct ExpectedFnPathFoundFnKeyword {
1236+
#[primary_span]
1237+
#[suggestion(applicability = "machine-applicable", code = "Fn", style = "verbose")]
1238+
pub fn_token_span: Span,
1239+
}

‎compiler/rustc_parse/src/parser/ty.rs

+45-3
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use super::{Parser, PathStyle, TokenType};
22

3-
use crate::errors::{FnPtrWithGenerics, FnPtrWithGenericsSugg};
3+
use crate::errors::{ExpectedFnPathFoundFnKeyword, FnPtrWithGenerics, FnPtrWithGenericsSugg};
44
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
55

6+
use ast::DUMMY_NODE_ID;
67
use rustc_ast::ptr::P;
78
use rustc_ast::token::{self, Delimiter, Token, TokenKind};
89
use rustc_ast::util::case::Case;
@@ -12,7 +13,9 @@ use rustc_ast::{
1213
};
1314
use rustc_errors::{pluralize, struct_span_err, Applicability, PResult};
1415
use rustc_span::source_map::Span;
15-
use rustc_span::symbol::{kw, sym};
16+
use rustc_span::symbol::{kw, sym, Ident};
17+
use rustc_span::Symbol;
18+
use thin_vec::thin_vec;
1619

1720
/// Any `?` or `~const` modifiers that appear at the start of a bound.
1821
struct BoundModifiers {
@@ -931,7 +934,14 @@ impl<'a> Parser<'a> {
931934
modifiers: BoundModifiers,
932935
) -> PResult<'a, GenericBound> {
933936
let lifetime_defs = self.parse_late_bound_lifetime_defs()?;
934-
let path = self.parse_path(PathStyle::Type)?;
937+
let path = if self.token.is_keyword(kw::Fn)
938+
&& self.look_ahead(1, |tok| tok.kind == TokenKind::OpenDelim(Delimiter::Parenthesis))
939+
&& let Some(path) = self.recover_path_from_fn()
940+
{
941+
path
942+
} else {
943+
self.parse_path(PathStyle::Type)?
944+
};
935945
if has_parens {
936946
if self.token.is_like_plus() {
937947
// Someone has written something like `&dyn (Trait + Other)`. The correct code
@@ -960,6 +970,38 @@ impl<'a> Parser<'a> {
960970
Ok(GenericBound::Trait(poly_trait, modifier))
961971
}
962972

973+
// recovers a `Fn(..)` parenthesized-style path from `fn(..)`
974+
fn recover_path_from_fn(&mut self) -> Option<ast::Path> {
975+
let fn_token_span = self.token.span;
976+
self.bump();
977+
let args_lo = self.token.span;
978+
let snapshot = self.create_snapshot_for_diagnostic();
979+
match self.parse_fn_decl(|_| false, AllowPlus::No, RecoverReturnSign::OnlyFatArrow) {
980+
Ok(decl) => {
981+
self.sess.emit_err(ExpectedFnPathFoundFnKeyword { fn_token_span });
982+
Some(ast::Path {
983+
span: fn_token_span.to(self.prev_token.span),
984+
segments: thin_vec![ast::PathSegment {
985+
ident: Ident::new(Symbol::intern("Fn"), fn_token_span),
986+
id: DUMMY_NODE_ID,
987+
args: Some(P(ast::GenericArgs::Parenthesized(ast::ParenthesizedArgs {
988+
span: args_lo.to(self.prev_token.span),
989+
inputs: decl.inputs.iter().map(|a| a.ty.clone()).collect(),
990+
inputs_span: args_lo.until(decl.output.span()),
991+
output: decl.output.clone(),
992+
}))),
993+
}],
994+
tokens: None,
995+
})
996+
}
997+
Err(diag) => {
998+
diag.cancel();
999+
self.restore_snapshot(snapshot);
1000+
None
1001+
}
1002+
}
1003+
}
1004+
9631005
/// Optionally parses `for<$generic_params>`.
9641006
pub(super) fn parse_late_bound_lifetime_defs(&mut self) -> PResult<'a, Vec<GenericParam>> {
9651007
if self.eat_keyword(kw::For) {

‎src/test/ui/parser/kw-in-trait-bounds.rs

+4-12
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,13 @@ fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
44
//~^ ERROR expected identifier, found keyword `fn`
55
//~| ERROR expected identifier, found keyword `fn`
66
//~| ERROR expected identifier, found keyword `fn`
7-
//~| ERROR cannot find trait `r#fn` in this scope
8-
//~| ERROR cannot find trait `r#fn` in this scope
9-
//~| ERROR cannot find trait `r#fn` in this scope
10-
//~| HELP a trait with a similar name exists
11-
//~| HELP a trait with a similar name exists
12-
//~| HELP a trait with a similar name exists
13-
//~| HELP escape `fn` to use it as an identifier
14-
//~| HELP escape `fn` to use it as an identifier
15-
//~| HELP escape `fn` to use it as an identifier
7+
//~| HELP use `Fn` to refer to the trait
8+
//~| HELP use `Fn` to refer to the trait
9+
//~| HELP use `Fn` to refer to the trait
1610
where
1711
G: fn(),
1812
//~^ ERROR expected identifier, found keyword `fn`
19-
//~| ERROR cannot find trait `r#fn` in this scope
20-
//~| HELP a trait with a similar name exists
21-
//~| HELP escape `fn` to use it as an identifier
13+
//~| HELP use `Fn` to refer to the trait
2214
{}
2315

2416
fn _g<A: struct, B>(_: impl struct, _: &dyn struct)

‎src/test/ui/parser/kw-in-trait-bounds.stderr

+26-62
Original file line numberDiff line numberDiff line change
@@ -2,48 +2,48 @@ error: expected identifier, found keyword `fn`
22
--> $DIR/kw-in-trait-bounds.rs:3:10
33
|
44
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
5-
| ^^ expected identifier, found keyword
5+
| ^^
66
|
7-
help: escape `fn` to use it as an identifier
7+
help: use `Fn` to refer to the trait
88
|
9-
LL | fn _f<F: r#fn(), G>(_: impl fn(), _: &dyn fn())
10-
| ++
9+
LL | fn _f<F: Fn(), G>(_: impl fn(), _: &dyn fn())
10+
| ~~
1111

1212
error: expected identifier, found keyword `fn`
1313
--> $DIR/kw-in-trait-bounds.rs:3:27
1414
|
1515
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
16-
| ^^ expected identifier, found keyword
16+
| ^^
1717
|
18-
help: escape `fn` to use it as an identifier
18+
help: use `Fn` to refer to the trait
1919
|
20-
LL | fn _f<F: fn(), G>(_: impl r#fn(), _: &dyn fn())
21-
| ++
20+
LL | fn _f<F: fn(), G>(_: impl Fn(), _: &dyn fn())
21+
| ~~
2222

2323
error: expected identifier, found keyword `fn`
2424
--> $DIR/kw-in-trait-bounds.rs:3:41
2525
|
2626
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
27-
| ^^ expected identifier, found keyword
27+
| ^^
2828
|
29-
help: escape `fn` to use it as an identifier
29+
help: use `Fn` to refer to the trait
3030
|
31-
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn r#fn())
32-
| ++
31+
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn Fn())
32+
| ~~
3333

3434
error: expected identifier, found keyword `fn`
35-
--> $DIR/kw-in-trait-bounds.rs:17:4
35+
--> $DIR/kw-in-trait-bounds.rs:11:4
3636
|
3737
LL | G: fn(),
38-
| ^^ expected identifier, found keyword
38+
| ^^
3939
|
40-
help: escape `fn` to use it as an identifier
40+
help: use `Fn` to refer to the trait
4141
|
42-
LL | G: r#fn(),
43-
| ++
42+
LL | G: Fn(),
43+
| ~~
4444

4545
error: expected identifier, found keyword `struct`
46-
--> $DIR/kw-in-trait-bounds.rs:24:10
46+
--> $DIR/kw-in-trait-bounds.rs:16:10
4747
|
4848
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
4949
| ^^^^^^ expected identifier, found keyword
@@ -54,7 +54,7 @@ LL | fn _g<A: r#struct, B>(_: impl struct, _: &dyn struct)
5454
| ++
5555

5656
error: expected identifier, found keyword `struct`
57-
--> $DIR/kw-in-trait-bounds.rs:24:29
57+
--> $DIR/kw-in-trait-bounds.rs:16:29
5858
|
5959
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
6060
| ^^^^^^ expected identifier, found keyword
@@ -65,7 +65,7 @@ LL | fn _g<A: struct, B>(_: impl r#struct, _: &dyn struct)
6565
| ++
6666

6767
error: expected identifier, found keyword `struct`
68-
--> $DIR/kw-in-trait-bounds.rs:24:45
68+
--> $DIR/kw-in-trait-bounds.rs:16:45
6969
|
7070
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
7171
| ^^^^^^ expected identifier, found keyword
@@ -76,7 +76,7 @@ LL | fn _g<A: struct, B>(_: impl struct, _: &dyn r#struct)
7676
| ++
7777

7878
error: expected identifier, found keyword `struct`
79-
--> $DIR/kw-in-trait-bounds.rs:38:8
79+
--> $DIR/kw-in-trait-bounds.rs:30:8
8080
|
8181
LL | B: struct,
8282
| ^^^^^^ expected identifier, found keyword
@@ -86,44 +86,8 @@ help: escape `struct` to use it as an identifier
8686
LL | B: r#struct,
8787
| ++
8888

89-
error[E0405]: cannot find trait `r#fn` in this scope
90-
--> $DIR/kw-in-trait-bounds.rs:3:10
91-
|
92-
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
93-
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
94-
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
95-
|
96-
= note: similarly named trait `Fn` defined here
97-
98-
error[E0405]: cannot find trait `r#fn` in this scope
99-
--> $DIR/kw-in-trait-bounds.rs:17:4
100-
|
101-
LL | G: fn(),
102-
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
103-
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
104-
|
105-
= note: similarly named trait `Fn` defined here
106-
107-
error[E0405]: cannot find trait `r#fn` in this scope
108-
--> $DIR/kw-in-trait-bounds.rs:3:27
109-
|
110-
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
111-
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
112-
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
113-
|
114-
= note: similarly named trait `Fn` defined here
115-
116-
error[E0405]: cannot find trait `r#fn` in this scope
117-
--> $DIR/kw-in-trait-bounds.rs:3:41
118-
|
119-
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
120-
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
121-
--> $SRC_DIR/core/src/ops/function.rs:LL:COL
122-
|
123-
= note: similarly named trait `Fn` defined here
124-
12589
error[E0405]: cannot find trait `r#struct` in this scope
126-
--> $DIR/kw-in-trait-bounds.rs:24:10
90+
--> $DIR/kw-in-trait-bounds.rs:16:10
12791
|
12892
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
12993
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
@@ -132,7 +96,7 @@ LL | trait Struct {}
13296
| ------------ similarly named trait `Struct` defined here
13397

13498
error[E0405]: cannot find trait `r#struct` in this scope
135-
--> $DIR/kw-in-trait-bounds.rs:38:8
99+
--> $DIR/kw-in-trait-bounds.rs:30:8
136100
|
137101
LL | B: struct,
138102
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
@@ -141,7 +105,7 @@ LL | trait Struct {}
141105
| ------------ similarly named trait `Struct` defined here
142106

143107
error[E0405]: cannot find trait `r#struct` in this scope
144-
--> $DIR/kw-in-trait-bounds.rs:24:29
108+
--> $DIR/kw-in-trait-bounds.rs:16:29
145109
|
146110
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
147111
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
@@ -150,14 +114,14 @@ LL | trait Struct {}
150114
| ------------ similarly named trait `Struct` defined here
151115

152116
error[E0405]: cannot find trait `r#struct` in this scope
153-
--> $DIR/kw-in-trait-bounds.rs:24:45
117+
--> $DIR/kw-in-trait-bounds.rs:16:45
154118
|
155119
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
156120
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
157121
...
158122
LL | trait Struct {}
159123
| ------------ similarly named trait `Struct` defined here
160124

161-
error: aborting due to 16 previous errors
125+
error: aborting due to 12 previous errors
162126

163127
For more information about this error, try `rustc --explain E0405`.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
fn foo(_: impl fn() -> i32) {}
2+
//~^ ERROR expected identifier, found keyword `fn`
3+
4+
fn foo2<T: fn(i32)>(_: T) {}
5+
//~^ ERROR expected identifier, found keyword `fn`
6+
7+
fn main() {
8+
foo(|| ());
9+
//~^ mismatched types
10+
foo2(|_: ()| {});
11+
//~^ type mismatch in closure arguments
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
error: expected identifier, found keyword `fn`
2+
--> $DIR/recover-fn-trait-from-fn-kw.rs:1:16
3+
|
4+
LL | fn foo(_: impl fn() -> i32) {}
5+
| ^^
6+
|
7+
help: use `Fn` to refer to the trait
8+
|
9+
LL | fn foo(_: impl Fn() -> i32) {}
10+
| ~~
11+
12+
error: expected identifier, found keyword `fn`
13+
--> $DIR/recover-fn-trait-from-fn-kw.rs:4:12
14+
|
15+
LL | fn foo2<T: fn(i32)>(_: T) {}
16+
| ^^
17+
|
18+
help: use `Fn` to refer to the trait
19+
|
20+
LL | fn foo2<T: Fn(i32)>(_: T) {}
21+
| ~~
22+
23+
error[E0308]: mismatched types
24+
--> $DIR/recover-fn-trait-from-fn-kw.rs:8:12
25+
|
26+
LL | foo(|| ());
27+
| ^^ expected `i32`, found `()`
28+
29+
error[E0631]: type mismatch in closure arguments
30+
--> $DIR/recover-fn-trait-from-fn-kw.rs:10:5
31+
|
32+
LL | foo2(|_: ()| {});
33+
| ^^^^ ------- found signature defined here
34+
| |
35+
| expected due to this
36+
|
37+
= note: expected closure signature `fn(i32) -> _`
38+
found closure signature `fn(()) -> _`
39+
note: required by a bound in `foo2`
40+
--> $DIR/recover-fn-trait-from-fn-kw.rs:4:12
41+
|
42+
LL | fn foo2<T: fn(i32)>(_: T) {}
43+
| ^^^^^^^ required by this bound in `foo2`
44+
45+
error: aborting due to 4 previous errors
46+
47+
Some errors have detailed explanations: E0308, E0631.
48+
For more information about an error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)
Please sign in to comment.