about summary refs log tree commit diff
diff options
context:
space:
mode:
authorNicholas Nethercote <n.nethercote@gmail.com>2025-04-28 19:24:45 +1000
committerNicholas Nethercote <n.nethercote@gmail.com>2025-05-02 08:26:39 +1000
commitd42edee451b87ce3d7b9de82f43c142592e1e661 (patch)
tree399edbc4bbd8816e2ac4ebc4b7dfa9c16ed7ae57
parentb2bf951dd60a57b423ab0a3f6b544ff580a08cd1 (diff)
downloadrust-d42edee451b87ce3d7b9de82f43c142592e1e661.tar.gz
rust-d42edee451b87ce3d7b9de82f43c142592e1e661.zip
Handle `Path<>` better in error messages.
`Path<>` needs to be distinguished from `Path<T>`. This commit does
that, improving some error messages.
-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.stderr18
4 files changed, 50 insertions, 37 deletions
diff --git a/compiler/rustc_ast_lowering/src/lib.rs b/compiler/rustc_ast_lowering/src/lib.rs
index 1e14b4d6723..1844cb2de6c 100644
--- a/compiler/rustc_ast_lowering/src/lib.rs
+++ b/compiler/rustc_ast_lowering/src/lib.rs
@@ -54,8 +54,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;
@@ -1081,7 +1081,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) => {
@@ -1773,13 +1773,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.stderr b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
index e7dbb06987a..92996ca8467 100644
--- a/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
+++ b/tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
@@ -238,32 +238,30 @@ 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:21
+  --> $DIR/impl-trait-missing-lifetime-gated.rs:55:22
    |
 LL |     fn f(_: impl Foo<>) {}
-   |                     ^ expected named lifetime parameter
+   |                      ^ 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(_: impl Foo<>) {}
-LL +     fn f<'a>(_: impl Foo'a, >) {}
-   |
+LL |     fn f<'a>(_: impl Foo<'a>) {}
+   |         ++++             ++
 
 error[E0658]: anonymous lifetimes in `impl Trait` are unstable
-  --> $DIR/impl-trait-missing-lifetime-gated.rs:58:25
+  --> $DIR/impl-trait-missing-lifetime-gated.rs:58:26
    |
 LL |     fn g(mut x: impl Foo<>) -> Option<&()> { x.next() }
-   |                         ^ expected named lifetime parameter
+   |                          ^ 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(mut x: impl Foo<>) -> Option<&()> { x.next() }
-LL +     fn g<'a>(mut x: impl Foo'a, >) -> Option<&()> { x.next() }
-   |
+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