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`
This commit is contained in:
commit
3842117ef2
5 changed files with 244 additions and 4 deletions
|
@ -640,7 +640,13 @@ impl<'a> Parser<'a> {
|
|||
let mut bounds = Vec::new();
|
||||
let mut negative_bounds = Vec::new();
|
||||
|
||||
while self.can_begin_bound() || self.token.is_keyword(kw::Dyn) {
|
||||
while self.can_begin_bound()
|
||||
// Continue even if we find a keyword.
|
||||
// This is necessary for error recover on, for example, `impl fn()`.
|
||||
//
|
||||
// The only keyword that can go after generic bounds is `where`, so stop if it's it.
|
||||
|| (self.token.is_reserved_ident() && !self.token.is_keyword(kw::Where))
|
||||
{
|
||||
if self.token.is_keyword(kw::Dyn) {
|
||||
// Account for `&dyn Trait + dyn Other`.
|
||||
self.struct_span_err(self.token.span, "invalid `dyn` keyword")
|
||||
|
@ -803,6 +809,20 @@ impl<'a> Parser<'a> {
|
|||
self.expect_keyword(kw::Const)?;
|
||||
let span = tilde.to(self.prev_token.span);
|
||||
self.sess.gated_spans.gate(sym::const_trait_impl, span);
|
||||
Some(span)
|
||||
} else if self.eat_keyword(kw::Const) {
|
||||
let span = self.prev_token.span;
|
||||
self.sess.gated_spans.gate(sym::const_trait_impl, span);
|
||||
|
||||
self.struct_span_err(span, "const bounds must start with `~`")
|
||||
.span_suggestion(
|
||||
span.shrink_to_lo(),
|
||||
"add `~`",
|
||||
"~",
|
||||
Applicability::MachineApplicable,
|
||||
)
|
||||
.emit();
|
||||
|
||||
Some(span)
|
||||
} else {
|
||||
None
|
||||
|
|
47
src/test/ui/parser/kw-in-trait-bounds.rs
Normal file
47
src/test/ui/parser/kw-in-trait-bounds.rs
Normal file
|
@ -0,0 +1,47 @@
|
|||
// edition:2018
|
||||
|
||||
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
|
||||
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
|
||||
{}
|
||||
|
||||
fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
|
||||
//~^ ERROR expected identifier, found keyword `struct`
|
||||
//~| ERROR expected identifier, found keyword `struct`
|
||||
//~| ERROR expected identifier, found keyword `struct`
|
||||
//~| ERROR cannot find trait `r#struct` in this scope
|
||||
//~| ERROR cannot find trait `r#struct` in this scope
|
||||
//~| ERROR cannot find trait `r#struct` 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 `struct` to use it as an identifier
|
||||
//~| HELP escape `struct` to use it as an identifier
|
||||
//~| HELP escape `struct` to use it as an identifier
|
||||
where
|
||||
B: struct,
|
||||
//~^ ERROR expected identifier, found keyword `struct`
|
||||
//~| ERROR cannot find trait `r#struct` in this scope
|
||||
//~| HELP a trait with a similar name exists
|
||||
//~| HELP escape `struct` to use it as an identifier
|
||||
{}
|
||||
|
||||
trait Struct {}
|
||||
|
||||
fn main() {}
|
171
src/test/ui/parser/kw-in-trait-bounds.stderr
Normal file
171
src/test/ui/parser/kw-in-trait-bounds.stderr
Normal file
|
@ -0,0 +1,171 @@
|
|||
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
|
||||
|
|
||||
LL | fn _f<F: r#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
|
||||
|
|
||||
LL | fn _f<F: fn(), G>(_: impl r#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
|
||||
|
|
||||
LL | fn _f<F: fn(), G>(_: impl fn(), _: &dyn r#fn())
|
||||
| ++
|
||||
|
||||
error: expected identifier, found keyword `fn`
|
||||
--> $DIR/kw-in-trait-bounds.rs:17:4
|
||||
|
|
||||
LL | G: fn(),
|
||||
| ^^ expected identifier, found keyword
|
||||
|
|
||||
help: escape `fn` to use it as an identifier
|
||||
|
|
||||
LL | G: r#fn(),
|
||||
| ++
|
||||
|
||||
error: expected identifier, found keyword `struct`
|
||||
--> $DIR/kw-in-trait-bounds.rs:24:10
|
||||
|
|
||||
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
|
||||
| ^^^^^^ expected identifier, found keyword
|
||||
|
|
||||
help: escape `struct` to use it as an identifier
|
||||
|
|
||||
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
|
||||
|
|
||||
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
|
||||
| ^^^^^^ expected identifier, found keyword
|
||||
|
|
||||
help: escape `struct` to use it as an identifier
|
||||
|
|
||||
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
|
||||
|
|
||||
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
|
||||
| ^^^^^^ expected identifier, found keyword
|
||||
|
|
||||
help: escape `struct` to use it as an identifier
|
||||
|
|
||||
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
|
||||
|
|
||||
LL | B: struct,
|
||||
| ^^^^^^ expected identifier, found keyword
|
||||
|
|
||||
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
|
||||
|
|
||||
LL | pub trait Fn<Args>: FnMut<Args> {
|
||||
| ------------------------------- 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
|
||||
|
|
||||
LL | pub trait Fn<Args>: FnMut<Args> {
|
||||
| ------------------------------- 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
|
||||
|
|
||||
LL | pub trait Fn<Args>: FnMut<Args> {
|
||||
| ------------------------------- 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
|
||||
|
|
||||
LL | pub trait Fn<Args>: FnMut<Args> {
|
||||
| ------------------------------- similarly named trait `Fn` defined here
|
||||
|
||||
error[E0405]: cannot find trait `r#struct` in this scope
|
||||
--> $DIR/kw-in-trait-bounds.rs:24:10
|
||||
|
|
||||
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
|
||||
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
|
||||
...
|
||||
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
|
||||
|
|
||||
LL | B: struct,
|
||||
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
|
||||
...
|
||||
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
|
||||
|
|
||||
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
|
||||
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
|
||||
...
|
||||
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
|
||||
|
|
||||
LL | fn _g<A: struct, B>(_: impl struct, _: &dyn struct)
|
||||
| ^^^^^^ help: a trait with a similar name exists (notice the capitalization): `Struct`
|
||||
...
|
||||
LL | trait Struct {}
|
||||
| ------------ similarly named trait `Struct` defined here
|
||||
|
||||
error: aborting due to 16 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0405`.
|
|
@ -3,4 +3,4 @@
|
|||
#![feature(const_trait_impl)]
|
||||
|
||||
struct S<T: const Tr>;
|
||||
//~^ ERROR expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, `~`, lifetime, or path
|
||||
//~^ ERROR const bounds must start with `~`
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
error: expected one of `!`, `(`, `,`, `=`, `>`, `?`, `for`, `~`, lifetime, or path, found keyword `const`
|
||||
error: const bounds must start with `~`
|
||||
--> $DIR/without-tilde.rs:5:13
|
||||
|
|
||||
LL | struct S<T: const Tr>;
|
||||
| ^^^^^ expected one of 10 possible tokens
|
||||
| -^^^^
|
||||
| |
|
||||
| help: add `~`: `~`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue