rust-lang/rust · #163454
Improve suggestion for lifetime preceding type in generics
compiler/rustc_parse/src/parser/diagnostics.rs38 + / 0 −
@@ -2286,6 +2286,44 @@ impl<'a> Parser<'a> { } } + /// Handle encountering a lifetime in a generic argument list that is not+ /// followed by a `,` or `>`.+ /// We emit an error and check if the lifetime is followed by a type+ /// in, e.g. `Foo<'a T>`.+ /// In this case, it is likely the user meant either `Foo<&'a T>` or `Foo<'a, T>`.+ /// We give both suggestions, then emit an error. Note that we do not try to recover,+ /// since we cannot know which of the two suggestions is correct and emitting either+ /// of the two types could cause confusing errors further on.+ pub(super) fn handle_lifetime_arg_preceding_type(&mut self, span: Span) -> PResult<'a, ()> {+ let snapshot = self.create_snapshot_for_diagnostic();+ let (mutbl, ty) = self.parse_ref_ty_no_leading_ampersand();+ self.restore_snapshot(snapshot);+ if ty.is_none() {+ // The lifetime is not followed by a type, do nothing.+ return Ok(());+ }+ // If we find `'a mut T`, suggesting to add a comma is wrong.+ let suggest_comma = mutbl.is_not();+ // Add `>` to the list of expected tokens.+ self.check(exp!(Gt));+ let mut err = self.unexpected().unwrap_err();+ err.span_suggestion_verbose(+ span.shrink_to_lo(),+ "you might have meant to write a reference type here",+ "&",+ Applicability::MaybeIncorrect,+ );+ if suggest_comma {+ err.span_suggestion_verbose(+ span.shrink_to_hi(),+ "use a comma to separate type parameters",+ ",",+ Applicability::MaybeIncorrect,+ );+ }+ Err(err.into())+ }+ /// Handle encountering a symbol in a generic argument list that is not a `,` or `>`. In this /// case, we emit an error and try to suggest enclosing a const argument in braces if it looks /// like the user has forgotten them.compiler/rustc_parse/src/parser/path.rs10 + / 6 −
@@ -705,12 +705,16 @@ impl<'a> Parser<'a> { err.emit(); continue; }- if !self.token.kind.should_end_const_arg()- && self.handle_ambiguous_unbraced_const_arg(&mut args)?- {- // We've managed to (partially) recover, so continue trying to parse- // arguments.- continue;+ if !self.token.kind.should_end_const_arg() {+ if let Some(arg @ AngleBracketedArg::Arg(GenericArg::Lifetime(_))) = args.last()+ {+ self.handle_lifetime_arg_preceding_type(arg.span())?;+ }+ if self.handle_ambiguous_unbraced_const_arg(&mut args)? {+ // We've managed to (partially) recover, so continue trying to parse+ // arguments.+ continue;+ } } break; }compiler/rustc_parse/src/parser/ty.rs17 + / 8 −
@@ -545,6 +545,17 @@ impl<'a> Parser<'a> { )) } + pub(super) fn parse_ref_ty_no_leading_ampersand(&mut self) -> (Mutability, Option<Box<Ty>>) {+ let mutbl = self.parse_mutability();+ match self.parse_ty_no_plus() {+ Ok(ty) => (mutbl, Some(ty)),+ Err(err) => {+ err.cancel();+ (mutbl, None)+ }+ }+ }+ fn maybe_recover_ref_ty_no_leading_ampersand<'cx>( &mut self, lt: Lifetime,@@ -555,9 +566,12 @@ impl<'a> Parser<'a> { return Err(err); } let snapshot = self.create_snapshot_for_diagnostic();- let mutbl = self.parse_mutability();- match self.parse_ty_no_plus() {- Ok(ty) => {+ match self.parse_ref_ty_no_leading_ampersand() {+ (_, None) => {+ self.restore_snapshot(snapshot);+ Err(err)+ }+ (mutbl, Some(ty)) => { err.span_suggestion_verbose( lo.shrink_to_lo(), "you might have meant to write a reference type here",@@ -567,11 +581,6 @@ impl<'a> Parser<'a> { err.emit(); Ok(TyKind::Ref(Some(lt), MutTy { ty, mutbl })) }- Err(diag) => {- diag.cancel();- self.restore_snapshot(snapshot);- Err(err)- } } } tests/ui/parser/issues/issue-20616-2.stderr6 + / 2 −
@@ -4,9 +4,13 @@ error: expected one of `,` or `>`, found `(` LL | type Type_2 = Type_1_<'static ()>; | ^ expected one of `,` or `>` |-help: you might have meant to end the type parameters here+help: you might have meant to write a reference type here |-LL | type Type_2 = Type_1_<'static> ()>;+LL | type Type_2 = Type_1_<&'static ()>;+ | ++help: use a comma to separate type parameters+ |+LL | type Type_2 = Type_1_<'static, ()>; | + error: aborting due to 1 previous errortests/ui/parser/issues/issue-93867.stderr6 + / 2 −
@@ -4,9 +4,13 @@ error: expected one of `,` or `>`, found `K` LL | pub fn entry<'a, K, V>() -> Entry<'a K, V> { | ^ expected one of `,` or `>` |-help: you might have meant to end the type parameters here+help: you might have meant to write a reference type here |-LL | pub fn entry<'a, K, V>() -> Entry<'a> K, V> {+LL | pub fn entry<'a, K, V>() -> Entry<&'a K, V> {+ | ++help: use a comma to separate type parameters+ |+LL | pub fn entry<'a, K, V>() -> Entry<'a, K, V> { | + error: aborting due to 1 previous errortests/ui/parser/lifetime-preceding-type-in-generic-args.rsadded32 + / 0 −
@@ -0,0 +1,32 @@+// Regression test for #59325, specifically for ampersand-less lifetimes in generics.+// Similar tests outside of generics are in recover/recover-ampersand-less-ref-ty.rs.+struct T1;+struct T2;++struct Foo<T>(T);+struct Bar<T1, T2>(T1, T2);++struct A1 {+ x: Foo<'static T1>, //~ERROR expected one of `,` or `>`+ //~^ HELP you might have meant to write a reference type here+ //~| HELP use a comma to separate type parameters+}++struct A2 {+ x: Foo<'static mut T1>, //~ERROR expected one of `,` or `>`+ //~^ HELP you might have meant to write a reference type here+}++struct A3 {+ x: Bar<'static T1, T2>, //~ERROR expected one of `,` or `>`+ //~^ HELP you might have meant to write a reference type here+ //~| HELP use a comma to separate type parameters+}++struct A4 {+ x: Bar<T1, 'static T2>, //~ERROR expected one of `,` or `>`+ //~^ HELP you might have meant to write a reference type here+ //~| HELP use a comma to separate type parameters+}++fn main() {}tests/ui/parser/lifetime-preceding-type-in-generic-args.stderradded66 + / 0 −
@@ -0,0 +1,66 @@+error: expected one of `,` or `>`, found `T1`+ --> $DIR/lifetime-preceding-type-in-generic-args.rs:10:20+ |+LL | struct A1 {+ | -- while parsing this struct+LL | x: Foo<'static T1>,+ | ^^ expected one of `,` or `>`+ |+help: you might have meant to write a reference type here+ |+LL | x: Foo<&'static T1>,+ | ++help: use a comma to separate type parameters+ |+LL | x: Foo<'static, T1>,+ | +++error: expected one of `,` or `>`, found keyword `mut`+ --> $DIR/lifetime-preceding-type-in-generic-args.rs:16:20+ |+LL | struct A2 {+ | -- while parsing this struct+LL | x: Foo<'static mut T1>,+ | ^^^ expected one of `,` or `>`+ |+help: you might have meant to write a reference type here+ |+LL | x: Foo<&'static mut T1>,+ | +++error: expected one of `,` or `>`, found `T1`+ --> $DIR/lifetime-preceding-type-in-generic-args.rs:21:20+ |+LL | struct A3 {+ | -- while parsing this struct+LL | x: Bar<'static T1, T2>,+ | ^^ expected one of `,` or `>`+ |+help: you might have meant to write a reference type here+ |+LL | x: Bar<&'static T1, T2>,+ | ++help: use a comma to separate type parameters+ |+LL | x: Bar<'static, T1, T2>,+ | +++error: expected one of `,` or `>`, found `T2`+ --> $DIR/lifetime-preceding-type-in-generic-args.rs:27:24+ |+LL | struct A4 {+ | -- while parsing this struct+LL | x: Bar<T1, 'static T2>,+ | ^^ expected one of `,` or `>`+ |+help: you might have meant to write a reference type here+ |+LL | x: Bar<T1, &'static T2>,+ | ++help: use a comma to separate type parameters+ |+LL | x: Bar<T1, 'static, T2>,+ | +++error: aborting due to 4 previous errors+