rust-lang/rust · #163454

Improve suggestion for lifetime preceding type in generics

tehforsch · merged Oct 3, 20267 files · 175 + / 18 −
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 error
tests/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 error
tests/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+