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:
Dylan DPC 2022-08-22 11:45:42 +05:30 committed by GitHub
commit 3842117ef2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 244 additions and 4 deletions

View file

@ -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