Skip to content

Commit

Permalink
Rollup merge of #117891 - compiler-errors:recover-for-dyn, r=davidtwco
Browse files Browse the repository at this point in the history
Recover `dyn` and `impl` after `for<...>`

Recover `dyn` and `impl` after `for<...>` in types. Reuses the logic for parsing bare trait objects, so it doesn't fix cases like `for<'a> dyn Trait + dyn Trait` or anything, but that seems somewhat of a different issue.

Parsing recovery logic is a bit involved, but I couldn't find a way to simplify it.

Fixes #117882
  • Loading branch information
compiler-errors authored Nov 20, 2023
2 parents 94d9b7e + a8a2ee4 commit a7f805d
Show file tree
Hide file tree
Showing 5 changed files with 95 additions and 3 deletions.
3 changes: 3 additions & 0 deletions compiler/rustc_parse/messages.ftl
Original file line number Diff line number Diff line change
Expand Up @@ -739,6 +739,9 @@ parse_trailing_vert_not_allowed = a trailing `|` is not allowed in an or-pattern
parse_trait_alias_cannot_be_auto = trait aliases cannot be `auto`
parse_trait_alias_cannot_be_unsafe = trait aliases cannot be `unsafe`
parse_transpose_dyn_or_impl = `for<...>` expected after `{$kw}`, not before
.suggestion = move `{$kw}` before the `for<...>`
parse_type_ascription_removed =
if you meant to annotate an expression with a type, the type ascription syntax has been removed, see issue #101728 <https://github.com/rust-lang/rust/issues/101728>
Expand Down
20 changes: 20 additions & 0 deletions compiler/rustc_parse/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2827,3 +2827,23 @@ pub(crate) struct GenericArgsInPatRequireTurbofishSyntax {
)]
pub suggest_turbofish: Span,
}

#[derive(Diagnostic)]
#[diag(parse_transpose_dyn_or_impl)]
pub(crate) struct TransposeDynOrImpl<'a> {
#[primary_span]
pub span: Span,
pub kw: &'a str,
#[subdiagnostic]
pub sugg: TransposeDynOrImplSugg<'a>,
}

#[derive(Subdiagnostic)]
#[multipart_suggestion(parse_suggestion, applicability = "machine-applicable")]
pub(crate) struct TransposeDynOrImplSugg<'a> {
#[suggestion_part(code = "")]
pub removal_span: Span,
#[suggestion_part(code = "{kw} ")]
pub insertion_span: Span,
pub kw: &'a str,
}
40 changes: 37 additions & 3 deletions compiler/rustc_parse/src/parser/ty.rs
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@ impl<'a> Parser<'a> {
// Function pointer type
self.parse_ty_bare_fn(lo, ThinVec::new(), None, recover_return_sign)?
} else if self.check_keyword(kw::For) {
let for_span = self.token.span;
// Function pointer type or bound list (trait object type) starting with a poly-trait.
// `for<'lt> [unsafe] [extern "ABI"] fn (&'lt S) -> T`
// `for<'lt> Trait1<'lt> + Trait2 + 'a`
Expand All @@ -299,9 +300,42 @@ impl<'a> Parser<'a> {
recover_return_sign,
)?
} else {
let path = self.parse_path(PathStyle::Type)?;
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)?
// Try to recover `for<'a> dyn Trait` or `for<'a> impl Trait`.
if self.may_recover()
&& (self.eat_keyword_noexpect(kw::Impl) || self.eat_keyword_noexpect(kw::Dyn))
{
let kw = self.prev_token.ident().unwrap().0.name;
let mut err = self.sess.create_err(errors::TransposeDynOrImpl {
span: self.prev_token.span,
kw: kw.as_str(),
sugg: errors::TransposeDynOrImplSugg {
removal_span: self.prev_token.span.with_hi(self.token.span.lo()),
insertion_span: for_span.shrink_to_lo(),
kw: kw.as_str(),
},
});
let path = self.parse_path(PathStyle::Type)?;
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
let kind =
self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)?;
// Take the parsed bare trait object and turn it either
// into a `dyn` object or an `impl Trait`.
let kind = match (kind, kw) {
(TyKind::TraitObject(bounds, _), kw::Dyn) => {
TyKind::TraitObject(bounds, TraitObjectSyntax::Dyn)
}
(TyKind::TraitObject(bounds, _), kw::Impl) => {
TyKind::ImplTrait(ast::DUMMY_NODE_ID, bounds)
}
_ => return Err(err),
};
err.emit();
kind
} else {
let path = self.parse_path(PathStyle::Type)?;
let parse_plus = allow_plus == AllowPlus::Yes && self.check_plus();
self.parse_remaining_bounds_path(lifetime_defs, path, lo, parse_plus)?
}
}
} else if self.eat_keyword(kw::Impl) {
self.parse_impl_ty(&mut impl_dyn_multi)?
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
trait Trait {}

fn test(_: &for<'a> dyn Trait) {}
//~^ ERROR `for<...>` expected after `dyn`, not before

fn test2(_: for<'a> impl Trait) {}
//~^ ERROR `for<...>` expected after `impl`, not before

fn main() {}
26 changes: 26 additions & 0 deletions tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
error: `for<...>` expected after `dyn`, not before
--> $DIR/recover-hrtb-before-dyn-impl-kw.rs:3:21
|
LL | fn test(_: &for<'a> dyn Trait) {}
| ^^^
|
help: move `dyn` before the `for<...>`
|
LL - fn test(_: &for<'a> dyn Trait) {}
LL + fn test(_: &dyn for<'a> Trait) {}
|

error: `for<...>` expected after `impl`, not before
--> $DIR/recover-hrtb-before-dyn-impl-kw.rs:6:21
|
LL | fn test2(_: for<'a> impl Trait) {}
| ^^^^
|
help: move `impl` before the `for<...>`
|
LL - fn test2(_: for<'a> impl Trait) {}
LL + fn test2(_: impl for<'a> Trait) {}
|

error: aborting due to 2 previous errors

0 comments on commit a7f805d

Please sign in to comment.