diff --git a/compiler/rustc_parse/messages.ftl b/compiler/rustc_parse/messages.ftl index 266190da035..49a2c414467 100644 --- a/compiler/rustc_parse/messages.ftl +++ b/compiler/rustc_parse/messages.ftl @@ -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 diff --git a/compiler/rustc_parse/src/errors.rs b/compiler/rustc_parse/src/errors.rs index 8ab1ec298a1..0a077d40ec7 100644 --- a/compiler/rustc_parse/src/errors.rs +++ b/compiler/rustc_parse/src/errors.rs @@ -2828,3 +2828,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, +} diff --git a/compiler/rustc_parse/src/parser/ty.rs b/compiler/rustc_parse/src/parser/ty.rs index be2cbaf3020..051028cf7f8 100644 --- a/compiler/rustc_parse/src/parser/ty.rs +++ b/compiler/rustc_parse/src/parser/ty.rs @@ -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` @@ -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)? diff --git a/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs new file mode 100644 index 00000000000..fe363a6887f --- /dev/null +++ b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.rs @@ -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() {} diff --git a/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr new file mode 100644 index 00000000000..6fc1259b910 --- /dev/null +++ b/tests/ui/parser/recover-hrtb-before-dyn-impl-kw.stderr @@ -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 +