about summary refs log tree commit diff
diff options
context:
space:
mode:
authorStuart Cook <Zalathar@users.noreply.github.com>2025-05-02 22:16:58 +1000
committerGitHub <noreply@github.com>2025-05-02 22:16:58 +1000
commitc3f500ec4d1eef978dffe8d9b3a31c7481f82f0a (patch)
tree7b2c953dac08624d2faa5e5c0fe0b04aa634ab39
parentb24288ba45495aa625b28f632fcc6aae32a4966e (diff)
parentd42edee451b87ce3d7b9de82f43c142592e1e661 (diff)
downloadrust-c3f500ec4d1eef978dffe8d9b3a31c7481f82f0a.tar.gz
rust-c3f500ec4d1eef978dffe8d9b3a31c7481f82f0a.zip
Rollup merge of #139046 - nnethercote:hir-Lifetime-better, r=lcnr
Improve `Lifetime::suggestion`

r? ``@lcnr``
-rw-r--r--compiler/rustc_ast_lowering/src/lib.rs10
-rw-r--r--compiler/rustc_ast_lowering/src/path.rs30
-rw-r--r--compiler/rustc_hir/src/hir.rs29
-rw-r--r--tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs11
-rw-r--r--tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr55
5 files changed, 104 insertions, 31 deletions
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 8b1c63cd21d..9f9d1f9a556 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -55,8 +55,8 @@ use rustc_errors::{DiagArgFromDisplay, DiagCtxtHandle, StashKey};
 use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
 use rustc_hir::def_id::{CRATE_DEF_ID, LOCAL_CRATE, LocalDefId};
 use rustc_hir::{
-    self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, LifetimeSource,
-    LifetimeSyntax, ParamName, TraitCandidate,
+    self as hir, AngleBrackets, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem,
+    LifetimeSource, LifetimeSyntax, ParamName, TraitCandidate,
 };
 use rustc_index::{Idx, IndexSlice, IndexVec};
 use rustc_macros::extension;
@@ -1087,7 +1087,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         match arg {
             ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(
                 lt,
-                LifetimeSource::Path { with_angle_brackets: true },
+                LifetimeSource::Path { angle_brackets: hir::AngleBrackets::Full },
                 lt.ident.into(),
             )),
             ast::GenericArg::Type(ty) => {
@@ -1779,13 +1779,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
         &mut self,
         id: NodeId,
         span: Span,
-        with_angle_brackets: bool,
+        angle_brackets: AngleBrackets,
     ) -> &'hir hir::Lifetime {
         self.new_named_lifetime(
             id,
             id,
             Ident::new(kw::UnderscoreLifetime, span),
-            LifetimeSource::Path { with_angle_brackets },
+            LifetimeSource::Path { angle_brackets },
             LifetimeSyntax::Hidden,
         )
     }
diff --git a/compiler/rustc_ast_lowering/src/path.rs b/compiler/rustc_ast_lowering/src/path.rs
index fabe40a9d04..5cda64ce7b4 100644
--- a/compiler/rustc_ast_lowering/src/path.rs
+++ b/compiler/rustc_ast_lowering/src/path.rs
@@ -432,27 +432,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
 
         // Note: these spans are used for diagnostics when they can't be inferred.
         // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
-        let (elided_lifetime_span, with_angle_brackets) = if generic_args.span.is_empty() {
-            // If there are no brackets, use the identifier span.
+        let (elided_lifetime_span, angle_brackets) = if generic_args.span.is_empty() {
+            // No brackets, e.g. `Path`: use an empty span just past the end of the identifier.
             // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
             // originating from macros, since the segment's span might be from a macro arg.
-            (segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span), false)
-        } else if generic_args.is_empty() {
-            // If there are brackets, but not generic arguments, then use the opening bracket
-            (generic_args.span.with_hi(generic_args.span.lo() + BytePos(1)), true)
+            (
+                segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span),
+                hir::AngleBrackets::Missing,
+            )
         } else {
-            // Else use an empty span right after the opening bracket.
-            (generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo(), true)
+            // Brackets, e.g. `Path<>` or `Path<T>`: use an empty span just after the `<`.
+            (
+                generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo(),
+                if generic_args.is_empty() {
+                    hir::AngleBrackets::Empty
+                } else {
+                    hir::AngleBrackets::Full
+                },
+            )
         };
 
         generic_args.args.insert_many(
             0,
             (start..end).map(|id| {
-                let l = self.lower_lifetime_hidden_in_path(
-                    id,
-                    elided_lifetime_span,
-                    with_angle_brackets,
-                );
+                let l =
+                    self.lower_lifetime_hidden_in_path(id, elided_lifetime_span, angle_brackets);
                 GenericArg::Lifetime(l)
             }),
         );
diff --git a/compiler/rustc_hir/src/hir.rs b/compiler/rustc_hir/src/hir.rs
index af587ee5bdc..58b776bdc6a 100644
--- a/compiler/rustc_hir/src/hir.rs
+++ b/compiler/rustc_hir/src/hir.rs
@@ -36,17 +36,23 @@ pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
 use crate::intravisit::{FnKind, VisitorExt};
 
 #[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)]
+pub enum AngleBrackets {
+    /// E.g. `Path`.
+    Missing,
+    /// E.g. `Path<>`.
+    Empty,
+    /// E.g. `Path<T>`.
+    Full,
+}
+
+#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)]
 pub enum LifetimeSource {
     /// E.g. `&Type`, `&'_ Type`, `&'a Type`, `&mut Type`, `&'_ mut Type`, `&'a mut Type`
     Reference,
 
-    /// E.g. `ContainsLifetime`, `ContainsLifetime<'_>`, `ContainsLifetime<'a>`
-    Path {
-        /// - true for `ContainsLifetime<'_>`, `ContainsLifetime<'a>`,
-        ///   `ContainsLifetime<'_, T>`, `ContainsLifetime<'a, T>`
-        /// - false for `ContainsLifetime`
-        with_angle_brackets: bool,
-    },
+    /// E.g. `ContainsLifetime`, `ContainsLifetime<>`, `ContainsLifetime<'_>`,
+    /// `ContainsLifetime<'a>`
+    Path { angle_brackets: AngleBrackets },
 
     /// E.g. `impl Trait + '_`, `impl Trait + 'a`
     OutlivesBound,
@@ -304,12 +310,17 @@ impl Lifetime {
             (Named | Anonymous, _) => (self.ident.span, format!("{new_lifetime}")),
 
             // The user wrote `Path<T>`, and omitted the `'_,`.
-            (Hidden, Path { with_angle_brackets: true }) => {
+            (Hidden, Path { angle_brackets: AngleBrackets::Full }) => {
                 (self.ident.span, format!("{new_lifetime}, "))
             }
 
+            // The user wrote `Path<>`, and omitted the `'_`..
+            (Hidden, Path { angle_brackets: AngleBrackets::Empty }) => {
+                (self.ident.span, format!("{new_lifetime}"))
+            }
+
             // The user wrote `Path` and omitted the `<'_>`.
-            (Hidden, Path { with_angle_brackets: false }) => {
+            (Hidden, Path { angle_brackets: AngleBrackets::Missing }) => {
                 (self.ident.span.shrink_to_hi(), format!("<{new_lifetime}>"))
             }
 
diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs
index 443a7e3835e..f5c3da847c7 100644
--- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs
+++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs
@@ -49,6 +49,17 @@ mod alone_in_path {
     //~| ERROR missing lifetime specifier
 }
 
+mod alone_in_path2 {
+    trait Foo<'a> { fn next(&mut self) -> Option<&'a ()>; }
+
+    fn f(_: impl Foo<>) {}
+    //~^ ERROR anonymous lifetimes in `impl Trait` are unstable
+
+    fn g(mut x: impl Foo<>) -> Option<&()> { x.next() }
+    //~^ ERROR anonymous lifetimes in `impl Trait` are unstable
+    //~| ERROR missing lifetime specifier
+}
+
 mod in_path {
     trait Foo<'a, T> { fn next(&mut self) -> Option<&'a T>; }
 
diff --git a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
index 24013c85c87..92996ca8467 100644
--- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
+++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
@@ -108,7 +108,28 @@ LL +     fn g(mut x: impl Foo) -> Option<()> { x.next() }
    |
 
 error[E0106]: missing lifetime specifier
-  --> $DIR/impl-trait-missing-lifetime-gated.rs:58:41
+  --> $DIR/impl-trait-missing-lifetime-gated.rs:58:39
+   |
+LL |     fn g(mut x: impl Foo<>) -> Option<&()> { x.next() }
+   |                                       ^ expected named lifetime parameter
+   |
+   = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime, but this is uncommon unless you're returning a borrowed value from a `const` or a `static`
+   |
+LL |     fn g(mut x: impl Foo<>) -> Option<&'static ()> { x.next() }
+   |                                        +++++++
+help: consider introducing a named lifetime parameter
+   |
+LL |     fn g<'a>(mut x: impl Foo<>) -> Option<&'a ()> { x.next() }
+   |         ++++                               ++
+help: alternatively, you might want to return an owned value
+   |
+LL -     fn g(mut x: impl Foo<>) -> Option<&()> { x.next() }
+LL +     fn g(mut x: impl Foo<>) -> Option<()> { x.next() }
+   |
+
+error[E0106]: missing lifetime specifier
+  --> $DIR/impl-trait-missing-lifetime-gated.rs:69:41
    |
 LL |     fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() }
    |                                         ^ expected named lifetime parameter
@@ -129,7 +150,7 @@ LL +     fn g(mut x: impl Foo<()>) -> Option<()> { x.next() }
    |
 
 warning: elided lifetime has a name
-  --> $DIR/impl-trait-missing-lifetime-gated.rs:64:57
+  --> $DIR/impl-trait-missing-lifetime-gated.rs:75:57
    |
 LL | fn resolved_anonymous<'a, T: 'a>(f: impl Fn(&'a str) -> &T) {
    |                       -- lifetime `'a` declared here    ^ this elided lifetime gets resolved as `'a`
@@ -219,6 +240,32 @@ LL |     fn g<'a>(mut x: impl Foo<'a>) -> Option<&()> { x.next() }
 error[E0658]: anonymous lifetimes in `impl Trait` are unstable
   --> $DIR/impl-trait-missing-lifetime-gated.rs:55:22
    |
+LL |     fn f(_: impl Foo<>) {}
+   |                      ^ expected named lifetime parameter
+   |
+   = help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+help: consider introducing a named lifetime parameter
+   |
+LL |     fn f<'a>(_: impl Foo<'a>) {}
+   |         ++++             ++
+
+error[E0658]: anonymous lifetimes in `impl Trait` are unstable
+  --> $DIR/impl-trait-missing-lifetime-gated.rs:58:26
+   |
+LL |     fn g(mut x: impl Foo<>) -> Option<&()> { x.next() }
+   |                          ^ expected named lifetime parameter
+   |
+   = help: add `#![feature(anonymous_lifetime_in_impl_trait)]` to the crate attributes to enable
+   = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
+help: consider introducing a named lifetime parameter
+   |
+LL |     fn g<'a>(mut x: impl Foo<'a>) -> Option<&()> { x.next() }
+   |         ++++                 ++
+
+error[E0658]: anonymous lifetimes in `impl Trait` are unstable
+  --> $DIR/impl-trait-missing-lifetime-gated.rs:66:22
+   |
 LL |     fn f(_: impl Foo<()>) {}
    |                      ^ expected named lifetime parameter
    |
@@ -230,7 +277,7 @@ LL |     fn f<'a>(_: impl Foo<'a, ()>) {}
    |         ++++             +++
 
 error[E0658]: anonymous lifetimes in `impl Trait` are unstable
-  --> $DIR/impl-trait-missing-lifetime-gated.rs:58:26
+  --> $DIR/impl-trait-missing-lifetime-gated.rs:69:26
    |
 LL |     fn g(mut x: impl Foo<()>) -> Option<&()> { x.next() }
    |                          ^ expected named lifetime parameter
@@ -242,7 +289,7 @@ help: consider introducing a named lifetime parameter
 LL |     fn g<'a>(mut x: impl Foo<'a, ()>) -> Option<&()> { x.next() }
    |         ++++                 +++
 
-error: aborting due to 14 previous errors; 1 warning emitted
+error: aborting due to 17 previous errors; 1 warning emitted
 
 Some errors have detailed explanations: E0106, E0658.
 For more information about an error, try `rustc --explain E0106`.