Skip to content

Improve Lifetime::suggestion #139046

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
May 2, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 5 additions & 5 deletions compiler/rustc_ast_lowering/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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) => {
Expand Down Expand Up @@ -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,
)
}
Expand Down
30 changes: 17 additions & 13 deletions compiler/rustc_ast_lowering/src/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
}),
);
Expand Down
29 changes: 20 additions & 9 deletions compiler/rustc_hir/src/hir.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,24 @@ use crate::def_id::{DefId, LocalDefIdMap};
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,
Expand Down Expand Up @@ -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}>"))
}

Expand Down
11 changes: 11 additions & 0 deletions tests/ui/suggestions/impl-trait-missing-lifetime-gated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>; }

Expand Down
55 changes: 51 additions & 4 deletions tests/ui/suggestions/impl-trait-missing-lifetime-gated.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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`
Expand Down Expand Up @@ -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
|
Expand All @@ -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
Expand All @@ -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`.
Loading