mirror of
https://github.com/rust-lang/rust.git
synced 2025-06-04 19:29:07 +00:00
Extend HIR to track the source and syntax of a lifetime
An upcoming lint will want to be able to know if a lifetime is hidden (e.g. `&u8`, `ContainsLifetime`) or anonymous: (e.g. `&'_ u8`, `ContainsLifetime<'_>`). It will also want to know if the lifetime is related to a reference (`&u8`) or a path (`ContainsLifetime`).
This commit is contained in:
parent
553600e0f5
commit
2a5c349f42
@ -5,7 +5,7 @@ use rustc_ast::*;
|
||||
use rustc_errors::ErrorGuaranteed;
|
||||
use rustc_hir::def::{DefKind, Res};
|
||||
use rustc_hir::def_id::{CRATE_DEF_ID, LocalDefId};
|
||||
use rustc_hir::{self as hir, HirId, IsAnonInPath, PredicateOrigin};
|
||||
use rustc_hir::{self as hir, HirId, LifetimeSource, PredicateOrigin};
|
||||
use rustc_index::{IndexSlice, IndexVec};
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_span::edit_distance::find_best_match_for_name;
|
||||
@ -1868,7 +1868,8 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}
|
||||
GenericParamKind::Lifetime => {
|
||||
let lt_id = self.next_node_id();
|
||||
let lifetime = self.new_named_lifetime(id, lt_id, ident, IsAnonInPath::No);
|
||||
let lifetime =
|
||||
self.new_named_lifetime(id, lt_id, ident, LifetimeSource::Other, ident.into());
|
||||
hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
|
||||
lifetime,
|
||||
bounds,
|
||||
@ -1901,7 +1902,11 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
}),
|
||||
WherePredicateKind::RegionPredicate(WhereRegionPredicate { lifetime, bounds }) => {
|
||||
hir::WherePredicateKind::RegionPredicate(hir::WhereRegionPredicate {
|
||||
lifetime: self.lower_lifetime(lifetime),
|
||||
lifetime: self.lower_lifetime(
|
||||
lifetime,
|
||||
LifetimeSource::Other,
|
||||
lifetime.ident.into(),
|
||||
),
|
||||
bounds: self.lower_param_bounds(
|
||||
bounds,
|
||||
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
|
||||
|
@ -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, IsAnonInPath, ItemLocalMap, LangItem, ParamName,
|
||||
TraitCandidate,
|
||||
self as hir, ConstArg, GenericArg, HirId, ItemLocalMap, LangItem, LifetimeSource,
|
||||
LifetimeSyntax, ParamName, TraitCandidate,
|
||||
};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_macros::extension;
|
||||
@ -1079,7 +1079,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
itctx: ImplTraitContext,
|
||||
) -> hir::GenericArg<'hir> {
|
||||
match arg {
|
||||
ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(lt)),
|
||||
ast::GenericArg::Lifetime(lt) => GenericArg::Lifetime(self.lower_lifetime(
|
||||
lt,
|
||||
LifetimeSource::Path { with_angle_brackets: true },
|
||||
lt.ident.into(),
|
||||
)),
|
||||
ast::GenericArg::Type(ty) => {
|
||||
// We cannot just match on `TyKind::Infer` as `(_)` is represented as
|
||||
// `TyKind::Paren(TyKind::Infer)` and should also be lowered to `GenericArg::Infer`
|
||||
@ -1198,35 +1202,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
TyKind::Slice(ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
|
||||
TyKind::Ptr(mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
|
||||
TyKind::Ref(region, mt) => {
|
||||
let region = region.unwrap_or_else(|| {
|
||||
let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
|
||||
self.resolver.get_lifetime_res(t.id)
|
||||
{
|
||||
debug_assert_eq!(start.plus(1), end);
|
||||
start
|
||||
} else {
|
||||
self.next_node_id()
|
||||
};
|
||||
let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
|
||||
Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }
|
||||
});
|
||||
let lifetime = self.lower_lifetime(®ion);
|
||||
let lifetime = self.lower_ty_direct_lifetime(t, *region);
|
||||
hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx))
|
||||
}
|
||||
TyKind::PinnedRef(region, mt) => {
|
||||
let region = region.unwrap_or_else(|| {
|
||||
let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
|
||||
self.resolver.get_lifetime_res(t.id)
|
||||
{
|
||||
debug_assert_eq!(start.plus(1), end);
|
||||
start
|
||||
} else {
|
||||
self.next_node_id()
|
||||
};
|
||||
let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
|
||||
Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id }
|
||||
});
|
||||
let lifetime = self.lower_lifetime(®ion);
|
||||
let lifetime = self.lower_ty_direct_lifetime(t, *region);
|
||||
let kind = hir::TyKind::Ref(lifetime, self.lower_mt(mt, itctx));
|
||||
let span = self.lower_span(t.span);
|
||||
let arg = hir::Ty { kind, span, hir_id: self.next_id() };
|
||||
@ -1302,7 +1282,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
GenericBound::Outlives(lifetime) => {
|
||||
if lifetime_bound.is_none() {
|
||||
lifetime_bound = Some(this.lower_lifetime(lifetime));
|
||||
lifetime_bound = Some(this.lower_lifetime(
|
||||
lifetime,
|
||||
LifetimeSource::Other,
|
||||
lifetime.ident.into(),
|
||||
));
|
||||
}
|
||||
None
|
||||
}
|
||||
@ -1393,6 +1377,31 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) }
|
||||
}
|
||||
|
||||
fn lower_ty_direct_lifetime(
|
||||
&mut self,
|
||||
t: &Ty,
|
||||
region: Option<Lifetime>,
|
||||
) -> &'hir hir::Lifetime {
|
||||
let (region, syntax) = match region {
|
||||
Some(region) => (region, region.ident.into()),
|
||||
|
||||
None => {
|
||||
let id = if let Some(LifetimeRes::ElidedAnchor { start, end }) =
|
||||
self.resolver.get_lifetime_res(t.id)
|
||||
{
|
||||
debug_assert_eq!(start.plus(1), end);
|
||||
start
|
||||
} else {
|
||||
self.next_node_id()
|
||||
};
|
||||
let span = self.tcx.sess.source_map().start_point(t.span).shrink_to_hi();
|
||||
let region = Lifetime { ident: Ident::new(kw::UnderscoreLifetime, span), id };
|
||||
(region, LifetimeSyntax::Hidden)
|
||||
}
|
||||
};
|
||||
self.lower_lifetime(®ion, LifetimeSource::Reference, syntax)
|
||||
}
|
||||
|
||||
/// Lowers a `ReturnPositionOpaqueTy` (`-> impl Trait`) or a `TypeAliasesOpaqueTy` (`type F =
|
||||
/// impl Trait`): this creates the associated Opaque Type (TAIT) definition and then returns a
|
||||
/// HIR type that references the TAIT.
|
||||
@ -1474,9 +1483,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
precise_capturing_args: &[PreciseCapturingArg],
|
||||
) -> &'hir [hir::PreciseCapturingArg<'hir>] {
|
||||
self.arena.alloc_from_iter(precise_capturing_args.iter().map(|arg| match arg {
|
||||
PreciseCapturingArg::Lifetime(lt) => {
|
||||
hir::PreciseCapturingArg::Lifetime(self.lower_lifetime(lt))
|
||||
}
|
||||
PreciseCapturingArg::Lifetime(lt) => hir::PreciseCapturingArg::Lifetime(
|
||||
self.lower_lifetime(lt, LifetimeSource::PreciseCapturing, lt.ident.into()),
|
||||
),
|
||||
PreciseCapturingArg::Arg(path, id) => {
|
||||
let [segment] = path.segments.as_slice() else {
|
||||
panic!();
|
||||
@ -1739,9 +1748,11 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
) -> hir::GenericBound<'hir> {
|
||||
match tpb {
|
||||
GenericBound::Trait(p) => hir::GenericBound::Trait(self.lower_poly_trait_ref(p, itctx)),
|
||||
GenericBound::Outlives(lifetime) => {
|
||||
hir::GenericBound::Outlives(self.lower_lifetime(lifetime))
|
||||
}
|
||||
GenericBound::Outlives(lifetime) => hir::GenericBound::Outlives(self.lower_lifetime(
|
||||
lifetime,
|
||||
LifetimeSource::OutlivesBound,
|
||||
lifetime.ident.into(),
|
||||
)),
|
||||
GenericBound::Use(args, span) => hir::GenericBound::Use(
|
||||
self.lower_precise_capturing_args(args),
|
||||
self.lower_span(*span),
|
||||
@ -1749,12 +1760,28 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
}
|
||||
|
||||
fn lower_lifetime(&mut self, l: &Lifetime) -> &'hir hir::Lifetime {
|
||||
self.new_named_lifetime(l.id, l.id, l.ident, IsAnonInPath::No)
|
||||
fn lower_lifetime(
|
||||
&mut self,
|
||||
l: &Lifetime,
|
||||
source: LifetimeSource,
|
||||
syntax: LifetimeSyntax,
|
||||
) -> &'hir hir::Lifetime {
|
||||
self.new_named_lifetime(l.id, l.id, l.ident, source, syntax)
|
||||
}
|
||||
|
||||
fn lower_lifetime_anon_in_path(&mut self, id: NodeId, span: Span) -> &'hir hir::Lifetime {
|
||||
self.new_named_lifetime(id, id, Ident::new(kw::UnderscoreLifetime, span), IsAnonInPath::Yes)
|
||||
fn lower_lifetime_hidden_in_path(
|
||||
&mut self,
|
||||
id: NodeId,
|
||||
span: Span,
|
||||
with_angle_brackets: bool,
|
||||
) -> &'hir hir::Lifetime {
|
||||
self.new_named_lifetime(
|
||||
id,
|
||||
id,
|
||||
Ident::new(kw::UnderscoreLifetime, span),
|
||||
LifetimeSource::Path { with_angle_brackets },
|
||||
LifetimeSyntax::Hidden,
|
||||
)
|
||||
}
|
||||
|
||||
#[instrument(level = "debug", skip(self))]
|
||||
@ -1763,7 +1790,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
id: NodeId,
|
||||
new_id: NodeId,
|
||||
ident: Ident,
|
||||
is_anon_in_path: IsAnonInPath,
|
||||
source: LifetimeSource,
|
||||
syntax: LifetimeSyntax,
|
||||
) -> &'hir hir::Lifetime {
|
||||
let res = self.resolver.get_lifetime_res(id).unwrap_or(LifetimeRes::Error);
|
||||
let res = match res {
|
||||
@ -1787,17 +1815,13 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
}
|
||||
};
|
||||
|
||||
#[cfg(debug_assertions)]
|
||||
if is_anon_in_path == IsAnonInPath::Yes {
|
||||
debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
|
||||
}
|
||||
|
||||
debug!(?res);
|
||||
self.arena.alloc(hir::Lifetime::new(
|
||||
self.lower_node_id(new_id),
|
||||
self.lower_ident(ident),
|
||||
res,
|
||||
is_anon_in_path,
|
||||
source,
|
||||
syntax,
|
||||
))
|
||||
}
|
||||
|
||||
@ -2389,7 +2413,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
self.next_id(),
|
||||
Ident::new(kw::UnderscoreLifetime, self.lower_span(span)),
|
||||
hir::LifetimeKind::ImplicitObjectLifetimeDefault,
|
||||
IsAnonInPath::No,
|
||||
LifetimeSource::Other,
|
||||
LifetimeSyntax::Hidden,
|
||||
);
|
||||
debug!("elided_dyn_bound: r={:?}", r);
|
||||
self.arena.alloc(r)
|
||||
|
@ -1,10 +1,9 @@
|
||||
use std::sync::Arc;
|
||||
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::GenericArg;
|
||||
use rustc_hir::def::{DefKind, PartialRes, Res};
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{self as hir, GenericArg};
|
||||
use rustc_middle::{span_bug, ty};
|
||||
use rustc_session::parse::add_feature_diagnostics;
|
||||
use rustc_span::{BytePos, DUMMY_SP, DesugaringKind, Ident, Span, Symbol, sym};
|
||||
@ -433,23 +432,27 @@ 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 = if generic_args.span.is_empty() {
|
||||
let (elided_lifetime_span, with_angle_brackets) = if generic_args.span.is_empty() {
|
||||
// If there are no brackets, use the identifier span.
|
||||
// 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)
|
||||
(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))
|
||||
(generic_args.span.with_hi(generic_args.span.lo() + BytePos(1)), true)
|
||||
} 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()
|
||||
(generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo(), true)
|
||||
};
|
||||
|
||||
generic_args.args.insert_many(
|
||||
0,
|
||||
(start..end).map(|id| {
|
||||
let l = self.lower_lifetime_anon_in_path(id, elided_lifetime_span);
|
||||
let l = self.lower_lifetime_hidden_in_path(
|
||||
id,
|
||||
elided_lifetime_span,
|
||||
with_angle_brackets,
|
||||
);
|
||||
GenericArg::Lifetime(l)
|
||||
}),
|
||||
);
|
||||
|
@ -36,43 +36,110 @@ 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 IsAnonInPath {
|
||||
No,
|
||||
Yes,
|
||||
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. `impl Trait + '_`, `impl Trait + 'a`
|
||||
OutlivesBound,
|
||||
|
||||
/// E.g. `impl Trait + use<'_>`, `impl Trait + use<'a>`
|
||||
PreciseCapturing,
|
||||
|
||||
/// Other usages which have not yet been categorized. Feel free to
|
||||
/// add new sources that you find useful.
|
||||
///
|
||||
/// Some non-exhaustive examples:
|
||||
/// - `where T: 'a`
|
||||
/// - `fn(_: dyn Trait + 'a)`
|
||||
Other,
|
||||
}
|
||||
|
||||
/// A lifetime. The valid field combinations are non-obvious. The following
|
||||
/// example shows some of them. See also the comments on `LifetimeKind`.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, HashStable_Generic)]
|
||||
pub enum LifetimeSyntax {
|
||||
/// E.g. `&Type`, `ContainsLifetime`
|
||||
Hidden,
|
||||
|
||||
/// E.g. `&'_ Type`, `ContainsLifetime<'_>`, `impl Trait + '_`, `impl Trait + use<'_>`
|
||||
Anonymous,
|
||||
|
||||
/// E.g. `&'a Type`, `ContainsLifetime<'a>`, `impl Trait + 'a`, `impl Trait + use<'a>`
|
||||
Named,
|
||||
}
|
||||
|
||||
impl From<Ident> for LifetimeSyntax {
|
||||
fn from(ident: Ident) -> Self {
|
||||
let name = ident.name;
|
||||
|
||||
if name == kw::Empty {
|
||||
unreachable!("A lifetime name should never be empty");
|
||||
} else if name == kw::UnderscoreLifetime {
|
||||
LifetimeSyntax::Anonymous
|
||||
} else {
|
||||
debug_assert!(name.as_str().starts_with('\''));
|
||||
LifetimeSyntax::Named
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A lifetime. The valid field combinations are non-obvious and not all
|
||||
/// combinations are possible. The following example shows some of
|
||||
/// them. See also the comments on `LifetimeKind` and `LifetimeSource`.
|
||||
///
|
||||
/// ```
|
||||
/// #[repr(C)]
|
||||
/// struct S<'a>(&'a u32); // res=Param, name='a, IsAnonInPath::No
|
||||
/// struct S<'a>(&'a u32); // res=Param, name='a, source=Reference, syntax=Named
|
||||
/// unsafe extern "C" {
|
||||
/// fn f1(s: S); // res=Param, name='_, IsAnonInPath::Yes
|
||||
/// fn f2(s: S<'_>); // res=Param, name='_, IsAnonInPath::No
|
||||
/// fn f3<'a>(s: S<'a>); // res=Param, name='a, IsAnonInPath::No
|
||||
/// fn f1(s: S); // res=Param, name='_, source=Path, syntax=Hidden
|
||||
/// fn f2(s: S<'_>); // res=Param, name='_, source=Path, syntax=Anonymous
|
||||
/// fn f3<'a>(s: S<'a>); // res=Param, name='a, source=Path, syntax=Named
|
||||
/// }
|
||||
///
|
||||
/// struct St<'a> { x: &'a u32 } // res=Param, name='a, IsAnonInPath::No
|
||||
/// struct St<'a> { x: &'a u32 } // res=Param, name='a, source=Reference, syntax=Named
|
||||
/// fn f() {
|
||||
/// _ = St { x: &0 }; // res=Infer, name='_, IsAnonInPath::Yes
|
||||
/// _ = St::<'_> { x: &0 }; // res=Infer, name='_, IsAnonInPath::No
|
||||
/// _ = St { x: &0 }; // res=Infer, name='_, source=Path, syntax=Hidden
|
||||
/// _ = St::<'_> { x: &0 }; // res=Infer, name='_, source=Path, syntax=Anonymous
|
||||
/// }
|
||||
///
|
||||
/// struct Name<'a>(&'a str); // res=Param, name='a, IsAnonInPath::No
|
||||
/// const A: Name = Name("a"); // res=Static, name='_, IsAnonInPath::Yes
|
||||
/// const B: &str = ""; // res=Static, name='_, IsAnonInPath::No
|
||||
/// static C: &'_ str = ""; // res=Static, name='_, IsAnonInPath::No
|
||||
/// static D: &'static str = ""; // res=Static, name='static, IsAnonInPath::No
|
||||
/// struct Name<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=Named
|
||||
/// const A: Name = Name("a"); // res=Static, name='_, source=Path, syntax=Hidden
|
||||
/// const B: &str = ""; // res=Static, name='_, source=Reference, syntax=Hidden
|
||||
/// static C: &'_ str = ""; // res=Static, name='_, source=Reference, syntax=Anonymous
|
||||
/// static D: &'static str = ""; // res=Static, name='static, source=Reference, syntax=Named
|
||||
///
|
||||
/// trait Tr {}
|
||||
/// fn tr(_: Box<dyn Tr>) {} // res=ImplicitObjectLifetimeDefault, name='_, IsAnonInPath::No
|
||||
/// fn tr(_: Box<dyn Tr>) {} // res=ImplicitObjectLifetimeDefault, name='_, source=Other, syntax=Hidden
|
||||
///
|
||||
/// fn capture_outlives<'a>() ->
|
||||
/// impl FnOnce() + 'a // res=Param, ident='a, source=OutlivesBound, syntax=Named
|
||||
/// {
|
||||
/// || {}
|
||||
/// }
|
||||
///
|
||||
/// fn capture_precise<'a>() ->
|
||||
/// impl FnOnce() + use<'a> // res=Param, ident='a, source=PreciseCapturing, syntax=Named
|
||||
/// {
|
||||
/// || {}
|
||||
/// }
|
||||
///
|
||||
/// // (commented out because these cases trigger errors)
|
||||
/// // struct S1<'a>(&'a str); // res=Param, name='a, IsAnonInPath::No
|
||||
/// // struct S2(S1); // res=Error, name='_, IsAnonInPath::Yes
|
||||
/// // struct S3(S1<'_>); // res=Error, name='_, IsAnonInPath::No
|
||||
/// // struct S4(S1<'a>); // res=Error, name='a, IsAnonInPath::No
|
||||
/// // struct S1<'a>(&'a str); // res=Param, name='a, source=Reference, syntax=Named
|
||||
/// // struct S2(S1); // res=Error, name='_, source=Path, syntax=Hidden
|
||||
/// // struct S3(S1<'_>); // res=Error, name='_, source=Path, syntax=Anonymous
|
||||
/// // struct S4(S1<'a>); // res=Error, name='a, source=Path, syntax=Named
|
||||
/// ```
|
||||
///
|
||||
/// Some combinations that cannot occur are `LifetimeSyntax::Hidden` with
|
||||
/// `LifetimeSource::OutlivesBound` or `LifetimeSource::PreciseCapturing`
|
||||
/// — there's no way to "elide" these lifetimes.
|
||||
#[derive(Debug, Copy, Clone, HashStable_Generic)]
|
||||
pub struct Lifetime {
|
||||
#[stable_hasher(ignore)]
|
||||
@ -86,9 +153,13 @@ pub struct Lifetime {
|
||||
/// Semantics of this lifetime.
|
||||
pub kind: LifetimeKind,
|
||||
|
||||
/// Is the lifetime anonymous and in a path? Used only for error
|
||||
/// suggestions. See `Lifetime::suggestion` for example use.
|
||||
pub is_anon_in_path: IsAnonInPath,
|
||||
/// The context in which the lifetime occurred. See `Lifetime::suggestion`
|
||||
/// for example use.
|
||||
pub source: LifetimeSource,
|
||||
|
||||
/// The syntax that the user used to declare this lifetime. See
|
||||
/// `Lifetime::suggestion` for example use.
|
||||
pub syntax: LifetimeSyntax,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, HashStable_Generic)]
|
||||
@ -185,9 +256,10 @@ impl Lifetime {
|
||||
hir_id: HirId,
|
||||
ident: Ident,
|
||||
kind: LifetimeKind,
|
||||
is_anon_in_path: IsAnonInPath,
|
||||
source: LifetimeSource,
|
||||
syntax: LifetimeSyntax,
|
||||
) -> Lifetime {
|
||||
let lifetime = Lifetime { hir_id, ident, kind, is_anon_in_path };
|
||||
let lifetime = Lifetime { hir_id, ident, kind, source, syntax };
|
||||
|
||||
// Sanity check: elided lifetimes form a strict subset of anonymous lifetimes.
|
||||
#[cfg(debug_assertions)]
|
||||
@ -209,23 +281,44 @@ impl Lifetime {
|
||||
self.ident.name == kw::UnderscoreLifetime
|
||||
}
|
||||
|
||||
pub fn is_syntactically_hidden(&self) -> bool {
|
||||
matches!(self.syntax, LifetimeSyntax::Hidden)
|
||||
}
|
||||
|
||||
pub fn is_syntactically_anonymous(&self) -> bool {
|
||||
matches!(self.syntax, LifetimeSyntax::Anonymous)
|
||||
}
|
||||
|
||||
pub fn is_static(&self) -> bool {
|
||||
self.kind == LifetimeKind::Static
|
||||
}
|
||||
|
||||
pub fn suggestion(&self, new_lifetime: &str) -> (Span, String) {
|
||||
use LifetimeSource::*;
|
||||
use LifetimeSyntax::*;
|
||||
|
||||
debug_assert!(new_lifetime.starts_with('\''));
|
||||
|
||||
match (self.is_anon_in_path, self.ident.span.is_empty()) {
|
||||
match (self.syntax, self.source) {
|
||||
// The user wrote `'a` or `'_`.
|
||||
(Named | Anonymous, _) => (self.ident.span, format!("{new_lifetime}")),
|
||||
|
||||
// The user wrote `Path<T>`, and omitted the `'_,`.
|
||||
(IsAnonInPath::Yes, true) => (self.ident.span, format!("{new_lifetime}, ")),
|
||||
(Hidden, Path { with_angle_brackets: true }) => {
|
||||
(self.ident.span, format!("{new_lifetime}, "))
|
||||
}
|
||||
|
||||
// The user wrote `Path` and omitted the `<'_>`.
|
||||
(IsAnonInPath::Yes, false) => {
|
||||
(Hidden, Path { with_angle_brackets: false }) => {
|
||||
(self.ident.span.shrink_to_hi(), format!("<{new_lifetime}>"))
|
||||
}
|
||||
|
||||
// The user wrote `&type` or `&mut type`.
|
||||
(IsAnonInPath::No, true) => (self.ident.span, format!("{new_lifetime} ")),
|
||||
(Hidden, Reference) => (self.ident.span, format!("{new_lifetime} ")),
|
||||
|
||||
// The user wrote `'a` or `'_`.
|
||||
(IsAnonInPath::No, false) => (self.ident.span, format!("{new_lifetime}")),
|
||||
(Hidden, source) => {
|
||||
unreachable!("can't suggest for a hidden lifetime of {source:?}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -58,7 +58,8 @@ fn trait_object_roundtrips_impl(syntax: TraitObjectSyntax) {
|
||||
hir_id: HirId::INVALID,
|
||||
ident: Ident::new(sym::name, DUMMY_SP),
|
||||
kind: LifetimeKind::Static,
|
||||
is_anon_in_path: IsAnonInPath::No,
|
||||
source: LifetimeSource::Other,
|
||||
syntax: LifetimeSyntax::Hidden,
|
||||
}
|
||||
},
|
||||
syntax,
|
||||
|
@ -9,7 +9,7 @@ use rustc_errors::{
|
||||
use rustc_hir::def::DefKind;
|
||||
use rustc_hir::def_id::{DefId, LocalDefId};
|
||||
use rustc_hir::intravisit::{Visitor, VisitorExt, walk_ty};
|
||||
use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, IsAnonInPath, Node};
|
||||
use rustc_hir::{self as hir, AmbigArg, FnRetTy, GenericParamKind, Node};
|
||||
use rustc_macros::{Diagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::print::{PrintTraitRefExt as _, TraitRefPrintOnlyTraitPath};
|
||||
use rustc_middle::ty::{self, Binder, ClosureKind, FnSig, GenericArg, Region, Ty, TyCtxt};
|
||||
@ -551,19 +551,6 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
|
||||
|
||||
impl<'v> Visitor<'v> for ImplicitLifetimeFinder {
|
||||
fn visit_ty(&mut self, ty: &'v hir::Ty<'v, AmbigArg>) {
|
||||
let make_suggestion = |lifetime: &hir::Lifetime| {
|
||||
if lifetime.is_anon_in_path == IsAnonInPath::Yes
|
||||
&& lifetime.ident.span.is_empty()
|
||||
{
|
||||
format!("{}, ", self.suggestion_param_name)
|
||||
} else if lifetime.ident.name == kw::UnderscoreLifetime
|
||||
&& lifetime.ident.span.is_empty()
|
||||
{
|
||||
format!("{} ", self.suggestion_param_name)
|
||||
} else {
|
||||
self.suggestion_param_name.clone()
|
||||
}
|
||||
};
|
||||
match ty.kind {
|
||||
hir::TyKind::Path(hir::QPath::Resolved(_, path)) => {
|
||||
for segment in path.segments {
|
||||
@ -572,7 +559,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
|
||||
matches!(
|
||||
arg,
|
||||
hir::GenericArg::Lifetime(lifetime)
|
||||
if lifetime.is_anon_in_path == IsAnonInPath::Yes
|
||||
if lifetime.is_syntactically_hidden()
|
||||
)
|
||||
}) {
|
||||
self.suggestions.push((
|
||||
@ -591,10 +578,10 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
|
||||
if let hir::GenericArg::Lifetime(lifetime) = arg
|
||||
&& lifetime.is_anonymous()
|
||||
{
|
||||
self.suggestions.push((
|
||||
lifetime.ident.span,
|
||||
make_suggestion(lifetime),
|
||||
));
|
||||
self.suggestions.push(
|
||||
lifetime
|
||||
.suggestion(&self.suggestion_param_name),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -602,7 +589,7 @@ impl Subdiagnostic for AddLifetimeParamsSuggestion<'_> {
|
||||
}
|
||||
}
|
||||
hir::TyKind::Ref(lifetime, ..) if lifetime.is_anonymous() => {
|
||||
self.suggestions.push((lifetime.ident.span, make_suggestion(lifetime)));
|
||||
self.suggestions.push(lifetime.suggestion(&self.suggestion_param_name));
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user