]> git.lizzy.rs Git - rust.git/blob - compiler/rustc_ast_lowering/src/lifetime_collector.rs
Rollup merge of #107325 - petrochenkov:hiddoc2, r=GuillaumeGomez
[rust.git] / compiler / rustc_ast_lowering / src / lifetime_collector.rs
1 use super::ResolverAstLoweringExt;
2 use rustc_ast::visit::{self, BoundKind, LifetimeCtxt, Visitor};
3 use rustc_ast::{FnRetTy, GenericBounds, Lifetime, NodeId, PathSegment, PolyTraitRef, Ty, TyKind};
4 use rustc_hir::def::LifetimeRes;
5 use rustc_middle::span_bug;
6 use rustc_middle::ty::ResolverAstLowering;
7 use rustc_span::symbol::{kw, Ident};
8 use rustc_span::Span;
9
10 struct LifetimeCollectVisitor<'ast> {
11     resolver: &'ast ResolverAstLowering,
12     current_binders: Vec<NodeId>,
13     collected_lifetimes: Vec<Lifetime>,
14 }
15
16 impl<'ast> LifetimeCollectVisitor<'ast> {
17     fn new(resolver: &'ast ResolverAstLowering) -> Self {
18         Self { resolver, current_binders: Vec::new(), collected_lifetimes: Vec::new() }
19     }
20
21     fn record_lifetime_use(&mut self, lifetime: Lifetime) {
22         match self.resolver.get_lifetime_res(lifetime.id).unwrap_or(LifetimeRes::Error) {
23             LifetimeRes::Param { binder, .. } | LifetimeRes::Fresh { binder, .. } => {
24                 if !self.current_binders.contains(&binder) {
25                     if !self.collected_lifetimes.contains(&lifetime) {
26                         self.collected_lifetimes.push(lifetime);
27                     }
28                 }
29             }
30             LifetimeRes::Static | LifetimeRes::Error => {
31                 if !self.collected_lifetimes.contains(&lifetime) {
32                     self.collected_lifetimes.push(lifetime);
33                 }
34             }
35             LifetimeRes::Infer => {}
36             res => {
37                 let bug_msg = format!(
38                     "Unexpected lifetime resolution {:?} for {:?} at {:?}",
39                     res, lifetime.ident, lifetime.ident.span
40                 );
41                 span_bug!(lifetime.ident.span, "{}", bug_msg);
42             }
43         }
44     }
45
46     /// This collect lifetimes that are elided, for nodes like `Foo<T>` where there are no explicit
47     /// lifetime nodes. Is equivalent to having "pseudo" nodes introduced for each of the node ids
48     /// in the list start..end.
49     fn record_elided_anchor(&mut self, node_id: NodeId, span: Span) {
50         if let Some(LifetimeRes::ElidedAnchor { start, end }) =
51             self.resolver.get_lifetime_res(node_id)
52         {
53             for i in start..end {
54                 let lifetime = Lifetime { id: i, ident: Ident::new(kw::UnderscoreLifetime, span) };
55                 self.record_lifetime_use(lifetime);
56             }
57         }
58     }
59 }
60
61 impl<'ast> Visitor<'ast> for LifetimeCollectVisitor<'ast> {
62     fn visit_lifetime(&mut self, lifetime: &'ast Lifetime, _: LifetimeCtxt) {
63         self.record_lifetime_use(*lifetime);
64     }
65
66     fn visit_path_segment(&mut self, path_segment: &'ast PathSegment) {
67         self.record_elided_anchor(path_segment.id, path_segment.ident.span);
68         visit::walk_path_segment(self, path_segment);
69     }
70
71     fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef) {
72         self.current_binders.push(t.trait_ref.ref_id);
73
74         visit::walk_poly_trait_ref(self, t);
75
76         self.current_binders.pop();
77     }
78
79     fn visit_ty(&mut self, t: &'ast Ty) {
80         match t.kind {
81             TyKind::BareFn(_) => {
82                 self.current_binders.push(t.id);
83                 visit::walk_ty(self, t);
84                 self.current_binders.pop();
85             }
86             TyKind::Ref(None, _) => {
87                 self.record_elided_anchor(t.id, t.span);
88                 visit::walk_ty(self, t);
89             }
90             _ => {
91                 visit::walk_ty(self, t);
92             }
93         }
94     }
95 }
96
97 pub fn lifetimes_in_ret_ty(resolver: &ResolverAstLowering, ret_ty: &FnRetTy) -> Vec<Lifetime> {
98     let mut visitor = LifetimeCollectVisitor::new(resolver);
99     visitor.visit_fn_ret_ty(ret_ty);
100     visitor.collected_lifetimes
101 }
102
103 pub fn lifetimes_in_bounds(
104     resolver: &ResolverAstLowering,
105     bounds: &GenericBounds,
106 ) -> Vec<Lifetime> {
107     let mut visitor = LifetimeCollectVisitor::new(resolver);
108     for bound in bounds {
109         visitor.visit_param_bound(bound, BoundKind::Bound);
110     }
111     visitor.collected_lifetimes
112 }