use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor};
use rustc_ast::{GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind};
use rustc_data_structures::fx::FxIndexSet;
use rustc_hir::def::{DefKind, LifetimeRes, Res};
use rustc_middle::span_bug;
use rustc_middle::ty::ResolverAstLowering;
use rustc_span::symbol::{kw, Ident};
use rustc_span::Span;
use super::ResolverAstLoweringExt;
struct LifetimeCollectVisitor<'ast> {
resolver: &'ast ResolverAstLowering,
current_binders: Vec<NodeId>,
collected_lifetimes: FxIndexSet<Lifetime>,
}
impl<'ast> LifetimeCollectVisitor<'ast> {
fn new(resolver: &'ast ResolverAstLowering) -> Self {
Self { resolver, current_binders: Vec::new(), collected_lifetimes: FxIndexSet::default() }
}
fn record_lifetime_use(&mut self, lifetime: Lifetime) {
match self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error) {
LifetimeRes::Param { binder, .. } | LifetimeRes::Fresh { binder, .. } => {
if !self.current_binders.contains(&binder) {
self.collected_lifetimes.insert(lifetime);
}
}
LifetimeRes::Static | LifetimeRes::Error => {
self.collected_lifetimes.insert(lifetime);
}
LifetimeRes::Infer => {}
res => {
let bug_msg = format!(
"Unexpected lifetime resolution {:?} for {:?} at {:?}",
res, lifetime.ident, lifetime.ident.span
);
span_bug!(lifetime.ident.span, "{}", bug_msg);
}
}
}
fn record_elided_anchor(&mut self, node_id: NodeId, span: Span) {
if let Some(LifetimeRes::ElidedAnchor { start, end }) =
self.resolver.get_lifetime_res(node_id)
{
for i in start..end {
let lifetime = Lifetime { id: i, ident: Ident::new(kw::UnderscoreLifetime, span) };
self.record_lifetime_use(lifetime);
}
}
}
}
impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) {
self.record_lifetime_use(*lifetime);
}
fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) {
self.record_elided_anchor(path_segment.id, path_segment.ident.span);
visit::walk_path_segment(self, path_segment);
}
fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) {
self.current_binders.push(t.trait_ref.ref_id);
visit::walk_poly_trait_ref(self, t);
self.current_binders.pop();
}
fn visit_ty(&mut self, t: &'ast Ty) {
match &t.kind {
TyKind::Path(None, _) => {
if let Some(partial_res) = self.resolver.get_partial_res(t.id)
&& let Some(Res::Def(DefKind::Trait | DefKind::TraitAlias, _)) =
partial_res.full_res()
{
self.current_binders.push(t.id);
visit::walk_ty(self, t);
self.current_binders.pop();
} else {
visit::walk_ty(self, t);
}
}
TyKind::BareFn(_) => {
self.current_binders.push(t.id);
visit::walk_ty(self, t);
self.current_binders.pop();
}
TyKind::Ref(None, _) => {
self.record_elided_anchor(t.id, t.span);
visit::walk_ty(self, t);
}
_ => {
visit::walk_ty(self, t);
}
}
}
}
pub(crate) fn lifetimes_in_bounds(
resolver: &ResolverAstLowering,
bounds: &GenericBounds,
) -> FxIndexSet<Lifetime> {
let mut visitor = LifetimeCollectVisitor::new(resolver);
for bound in bounds {
visitor.visit_param_bound(bound, BoundKind::Bound);
}
visitor.collected_lifetimes
}