Skip to content

Commit 3842117

Browse files
authoredAug 22, 2022
Rollup merge of #99915 - WaffleLapkin:recover_keyword_bounds, r=compiler-errors
Recover keywords in trait bounds (_this pr was inspired by [this tweet](https://twitter.com/Azumanga/status/1552982326409367561)_) Recover keywords in trait bound, motivational example: ```rust fn f(_: impl fn()) {} // mistyped, meant `Fn` ``` <details><summary>Current nightly (3 needless and confusing errors!)</summary> <p> ```text error: expected identifier, found keyword `fn` --> ./t.rs:1:15 | 1 | fn _f(_: impl fn()) {} | ^^ expected identifier, found keyword | help: escape `fn` to use it as an identifier | 1 | fn _f(_: impl r#fn()) {} | ++ error: expected one of `:` or `|`, found `)` --> ./t.rs:1:19 | 1 | fn _f(_: impl fn()) {} | ^ expected one of `:` or `|` error: expected one of `!`, `(`, `)`, `,`, `?`, `for`, `~`, lifetime, or path, found keyword `fn` --> ./t.rs:1:15 | 1 | fn _f(_: impl fn()) {} | -^^ expected one of 9 possible tokens | | | help: missing `,` error: at least one trait must be specified --> ./t.rs:1:10 | 1 | fn _f(_: impl fn()) {} | ^^^^ ``` </p> </details> This PR: ```text error: expected identifier, found keyword `fn` --> ./t.rs:1:15 | 1 | fn _f(_: impl fn()) {} | ^^ expected identifier, found keyword | help: escape `fn` to use it as an identifier | 1 | fn _f(_: impl r#fn()) {} | ++ error[E0405]: cannot find trait `r#fn` in this scope --> ./t.rs:1:15 | 1 | fn _f(_: impl fn()) {} | ^^ help: a trait with a similar name exists (notice the capitalization): `Fn` | ::: /home/waffle/projects/repos/rust/library/core/src/ops/function.rs:74:1 | 74 | pub trait Fn<Args>: FnMut<Args> { | ------------------------------- similarly named trait `Fn` defined here ``` It would be nice to have suggestion in the first error like "have you meant `Fn` trait", instead of a separate error, but the recovery is deep inside ident parsing, which makes it a lot harder to do. r? `@compiler-errors`
2 parents 33b5ce6 + 5d5e451 commit 3842117

File tree

5 files changed

+244
-4
lines changed

5 files changed

+244
-4
lines changed
 

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

+21-1
Original file line numberDiff line numberDiff line change
@@ -640,7 +640,13 @@ impl<'a> Parser<'a> {
640640
let mut bounds = Vec::new();
641641
let mut negative_bounds = Vec::new();
642642

643-
while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) {
643+
while self.can_begin_bound()
644+
// Continue even if we find a keyword.
645+
// This is necessary for error recover on, for example, `impl fn()`.
646+
//
647+
// The only keyword that can go after generic bounds is `where`, so stop if it's it.
648+
|| (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))
649+
{
644650
if self.token.is_keyword(kw::Dyn) {
645651
// Account for `&dyn Trait + dyn Other`.
646652
self.struct_span_err(self.token.span, "invalid `dyn` keyword")
@@ -803,6 +809,20 @@ impl<'a> Parser<'a> {
803809
self.expect_keyword(kw::Const)?;
804810
let span = tilde.to(self.prev_token.span);
805811
self.sess.gated_spans.gate(sym::const_trait_impl, span);
812+
Some(span)
813+
} else if self.eat_keyword(kw::Const) {
814+
let span = self.prev_token.span;
815+
self.sess.gated_spans.gate(sym::const_trait_impl, span);
816+
817+
self.struct_span_err(span, "const bounds must start with `~`")
818+
.span_suggestion(
819+
span.shrink_to_lo(),
820+
"add `~`",
821+
"~",
822+
Applicability::MachineApplicable,
823+
)
824+
.emit();
825+
806826
Some(span)
807827
} else {
808828
None
+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// edition:2018
2+
3+
fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
4+
//~^ ERROR expected identifier, found keyword `fn`
5+
//~| ERROR expected identifier, found keyword `fn`
6+
//~| 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
16+
where
17+
G: fn(),
18+
//~^ 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
22+
{}
23+
24+
fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
25+
//~^ ERROR expected identifier, found keyword `struct`
26+
//~| ERROR expected identifier, found keyword `struct`
27+
//~| ERROR expected identifier, found keyword `struct`
28+
//~| ERROR cannot find trait `r#struct` in this scope
29+
//~| ERROR cannot find trait `r#struct` in this scope
30+
//~| ERROR cannot find trait `r#struct` in this scope
31+
//~| HELP a trait with a similar name exists
32+
//~| HELP a trait with a similar name exists
33+
//~| HELP a trait with a similar name exists
34+
//~| HELP escape `struct` to use it as an identifier
35+
//~| HELP escape `struct` to use it as an identifier
36+
//~| HELP escape `struct` to use it as an identifier
37+
where
38+
B: struct,
39+
//~^ ERROR expected identifier, found keyword `struct`
40+
//~| ERROR cannot find trait `r#struct` in this scope
41+
//~| HELP a trait with a similar name exists
42+
//~| HELP escape `struct` to use it as an identifier
43+
{}
44+
45+
trait Struct {}
46+
47+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
error: expected identifier, found keyword `fn`
2+
--> $DIR/kw-in-trait-bounds.rs:3:10
3+
|
4+
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
5+
| ^^ expected identifier, found keyword
6+
|
7+
help: escape `fn` to use it as an identifier
8+
|
9+
LL | fn _f<F: r#fn(), G>(_: impl fn(), _: &dyn fn())
10+
| ++
11+
12+
error: expected identifier, found keyword `fn`
13+
--> $DIR/kw-in-trait-bounds.rs:3:27
14+
|
15+
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
16+
| ^^ expected identifier, found keyword
17+
|
18+
help: escape `fn` to use it as an identifier
19+
|
20+
LL | fn _f<F: fn(), G>(_: impl r#fn(), _: &dyn fn())
21+
| ++
22+
23+
error: expected identifier, found keyword `fn`
24+
--> $DIR/kw-in-trait-bounds.rs:3:41
25+
|
26+
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
27+
| ^^ expected identifier, found keyword
28+
|
29+
help: escape `fn` to use it as an identifier
30+
|
31+
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn r#fn())
32+
| ++
33+
34+
error: expected identifier, found keyword `fn`
35+
--> $DIR/kw-in-trait-bounds.rs:17:4
36+
|
37+
LL | G: fn(),
38+
| ^^ expected identifier, found keyword
39+
|
40+
help: escape `fn` to use it as an identifier
41+
|
42+
LL | G: r#fn(),
43+
| ++
44+
45+
error: expected identifier, found keyword `struct`
46+
--> $DIR/kw-in-trait-bounds.rs:24:10
47+
|
48+
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
49+
| ^^^^^^ expected identifier, found keyword
50+
|
51+
help: escape `struct` to use it as an identifier
52+
|
53+
LL | fn _g<A: r#struct, B>(_: impl struct, _: &dyn struct)
54+
| ++
55+
56+
error: expected identifier, found keyword `struct`
57+
--> $DIR/kw-in-trait-bounds.rs:24:29
58+
|
59+
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
60+
| ^^^^^^ expected identifier, found keyword
61+
|
62+
help: escape `struct` to use it as an identifier
63+
|
64+
LL | fn _g<A: struct, B>(_: impl r#struct, _: &dyn struct)
65+
| ++
66+
67+
error: expected identifier, found keyword `struct`
68+
--> $DIR/kw-in-trait-bounds.rs:24:45
69+
|
70+
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
71+
| ^^^^^^ expected identifier, found keyword
72+
|
73+
help: escape `struct` to use it as an identifier
74+
|
75+
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn r#struct)
76+
| ++
77+
78+
error: expected identifier, found keyword `struct`
79+
--> $DIR/kw-in-trait-bounds.rs:38:8
80+
|
81+
LL | B: struct,
82+
| ^^^^^^ expected identifier, found keyword
83+
|
84+
help: escape `struct` to use it as an identifier
85+
|
86+
LL | B: r#struct,
87+
| ++
88+
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+
|
95+
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
96+
|
97+
LL | pub trait Fn<Args>: FnMut<Args> {
98+
| ------------------------------- similarly named trait `Fn` defined here
99+
100+
error[E0405]: cannot find trait `r#fn` in this scope
101+
--> $DIR/kw-in-trait-bounds.rs:17:4
102+
|
103+
LL | G: fn(),
104+
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
105+
|
106+
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
107+
|
108+
LL | pub trait Fn<Args>: FnMut<Args> {
109+
| ------------------------------- similarly named trait `Fn` defined here
110+
111+
error[E0405]: cannot find trait `r#fn` in this scope
112+
--> $DIR/kw-in-trait-bounds.rs:3:27
113+
|
114+
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
115+
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
116+
|
117+
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
118+
|
119+
LL | pub trait Fn<Args>: FnMut<Args> {
120+
| ------------------------------- similarly named trait `Fn` defined here
121+
122+
error[E0405]: cannot find trait `r#fn` in this scope
123+
--> $DIR/kw-in-trait-bounds.rs:3:41
124+
|
125+
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn fn())
126+
| ^^ help: a trait with a similar name exists (notice the capitalization): `Fn`
127+
|
128+
::: $SRC_DIR/core/src/ops/function.rs:LL:COL
129+
|
130+
LL | pub trait Fn<Args>: FnMut<Args> {
131+
| ------------------------------- similarly named trait `Fn` defined here
132+
133+
error[E0405]: cannot find trait `r#struct` in this scope
134+
--> $DIR/kw-in-trait-bounds.rs:24:10
135+
|
136+
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
137+
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
138+
...
139+
LL | trait Struct {}
140+
| ------------ similarly named trait `Struct` defined here
141+
142+
error[E0405]: cannot find trait `r#struct` in this scope
143+
--> $DIR/kw-in-trait-bounds.rs:38:8
144+
|
145+
LL | B: struct,
146+
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
147+
...
148+
LL | trait Struct {}
149+
| ------------ similarly named trait `Struct` defined here
150+
151+
error[E0405]: cannot find trait `r#struct` in this scope
152+
--> $DIR/kw-in-trait-bounds.rs:24:29
153+
|
154+
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
155+
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
156+
...
157+
LL | trait Struct {}
158+
| ------------ similarly named trait `Struct` defined here
159+
160+
error[E0405]: cannot find trait `r#struct` in this scope
161+
--> $DIR/kw-in-trait-bounds.rs:24:45
162+
|
163+
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
164+
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
165+
...
166+
LL | trait Struct {}
167+
| ------------ similarly named trait `Struct` defined here
168+
169+
error: aborting due to 16 previous errors
170+
171+
For more information about this error, try `rustc --explain E0405`.

‎src/test/ui/rfc-2632-const-trait-impl/without-tilde.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,4 @@
33
#![feature(const_trait_impl)]
44

55
struct S<T: const Tr>;
6-
//~^ ERROR expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, `~`, lifetime, or path
6+
//~^ ERROR const bounds must start with `~`
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
1-
error: expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, `~`, lifetime, or path, found keyword `const`
1+
error: const bounds must start with `~`
22
--> $DIR/without-tilde.rs:5:13
33
|
44
LL | struct S<T: const Tr>;
5-
| ^^^^^ expected one of 10 possible tokens
5+
| -^^^^
6+
| |
7+
| help: add `~`: `~`
68

79
error: aborting due to previous error
810

0 commit comments

Comments
 (0)
Please sign in to comment.