]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/collect_trait_impls.rs
Use empty Cache for methods requiring it when filling Cache itself
[rust.git] / src / librustdoc / passes / collect_trait_impls.rs
1 use super::Pass;
2 use crate::clean::*;
3 use crate::core::DocContext;
4 use crate::fold::DocFolder;
5 use crate::formats::cache::Cache;
6
7 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
8 use rustc_hir::def_id::{DefId, LOCAL_CRATE};
9 use rustc_middle::ty::DefIdTree;
10 use rustc_span::symbol::sym;
11
12 crate const COLLECT_TRAIT_IMPLS: Pass = Pass {
13     name: "collect-trait-impls",
14     run: collect_trait_impls,
15     description: "retrieves trait impls for items in the crate",
16 };
17
18 crate fn collect_trait_impls(krate: Crate, cx: &DocContext<'_>) -> Crate {
19     let mut synth = SyntheticImplCollector::new(cx);
20     let mut krate = cx.sess().time("collect_synthetic_impls", || synth.fold_crate(krate));
21
22     let prims: FxHashSet<PrimitiveType> = krate.primitives.iter().map(|p| p.1).collect();
23
24     let crate_items = {
25         let mut coll = ItemCollector::new();
26         krate = cx.sess().time("collect_items_for_trait_impls", || coll.fold_crate(krate));
27         coll.items
28     };
29
30     let mut new_items = Vec::new();
31
32     for &cnum in cx.tcx.crates().iter() {
33         for &(did, _) in cx.tcx.all_trait_implementations(cnum).iter() {
34             cx.tcx.sess.time("build_extern_trait_impl", || {
35                 inline::build_impl(cx, None, did, None, &mut new_items);
36             });
37         }
38     }
39
40     // Also try to inline primitive impls from other crates.
41     for &def_id in PrimitiveType::all_impls(cx.tcx).values().flatten() {
42         if !def_id.is_local() {
43             cx.sess().time("build_primitive_trait_impl", || {
44                 inline::build_impl(cx, None, def_id, None, &mut new_items);
45
46                 // FIXME(eddyb) is this `doc(hidden)` check needed?
47                 if !cx.tcx.get_attrs(def_id).lists(sym::doc).has_word(sym::hidden) {
48                     let self_ty = cx.tcx.type_of(def_id);
49                     let impls = get_auto_trait_and_blanket_impls(cx, self_ty, def_id);
50                     let mut renderinfo = cx.renderinfo.borrow_mut();
51
52                     new_items.extend(impls.filter(|i| renderinfo.inlined.insert(i.def_id)));
53                 }
54             })
55         }
56     }
57
58     // `tcx.crates()` doesn't include the local crate, and `tcx.all_trait_implementations`
59     // doesn't work with it anyway, so pull them from the HIR map instead
60     for &trait_did in cx.tcx.all_traits(LOCAL_CRATE).iter() {
61         for &impl_node in cx.tcx.hir().trait_impls(trait_did) {
62             let impl_did = cx.tcx.hir().local_def_id(impl_node);
63             cx.tcx.sess.time("build_local_trait_impl", || {
64                 let mut extra_attrs = Vec::new();
65                 let mut parent = cx.tcx.parent(impl_did.to_def_id());
66                 while let Some(did) = parent {
67                     extra_attrs.extend(
68                         cx.tcx
69                             .get_attrs(did)
70                             .iter()
71                             .filter(|attr| attr.has_name(sym::doc))
72                             .filter(|attr| {
73                                 if let Some([attr]) = attr.meta_item_list().as_deref() {
74                                     attr.has_name(sym::cfg)
75                                 } else {
76                                     false
77                                 }
78                             })
79                             .cloned(),
80                     );
81                     parent = cx.tcx.parent(did);
82                 }
83                 inline::build_impl(
84                     cx,
85                     None,
86                     impl_did.to_def_id(),
87                     Some(&extra_attrs),
88                     &mut new_items,
89                 );
90             });
91         }
92     }
93
94     let mut cleaner = BadImplStripper { prims, items: crate_items };
95
96     let mut type_did_to_deref_target: FxHashMap<DefId, &Type> = FxHashMap::default();
97     // Gather all type to `Deref` target edges.
98     for it in &new_items {
99         if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind {
100             if trait_.def_id(&cx.cache) == cx.tcx.lang_items().deref_trait() {
101                 let target = items.iter().find_map(|item| match *item.kind {
102                     TypedefItem(ref t, true) => Some(&t.type_),
103                     _ => None,
104                 });
105                 if let (Some(for_did), Some(target)) = (for_.def_id(&cx.cache), target) {
106                     type_did_to_deref_target.insert(for_did, target);
107                 }
108             }
109         }
110     }
111     // Follow all `Deref` targets of included items and recursively add them as valid
112     fn add_deref_target(
113         map: &FxHashMap<DefId, &Type>,
114         cleaner: &mut BadImplStripper,
115         type_did: &DefId,
116         cache: &Cache,
117     ) {
118         if let Some(target) = map.get(type_did) {
119             debug!("add_deref_target: type {:?}, target {:?}", type_did, target);
120             if let Some(target_prim) = target.primitive_type() {
121                 cleaner.prims.insert(target_prim);
122             } else if let Some(target_did) = target.def_id(cache) {
123                 // `impl Deref<Target = S> for S`
124                 if target_did == *type_did {
125                     // Avoid infinite cycles
126                     return;
127                 }
128                 cleaner.items.insert(target_did);
129                 add_deref_target(map, cleaner, &target_did, cache);
130             }
131         }
132     }
133     for type_did in type_did_to_deref_target.keys() {
134         // Since only the `DefId` portion of the `Type` instances is known to be same for both the
135         // `Deref` target type and the impl for type positions, this map of types is keyed by
136         // `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
137         if cleaner.keep_impl_with_def_id(type_did) {
138             add_deref_target(&type_did_to_deref_target, &mut cleaner, type_did, &cx.cache);
139         }
140     }
141
142     new_items.retain(|it| {
143         if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind {
144             cleaner.keep_impl(for_, &cx.cache)
145                 || trait_.as_ref().map_or(false, |t| cleaner.keep_impl(t, &cx.cache))
146                 || blanket_impl.is_some()
147         } else {
148             true
149         }
150     });
151
152     if let Some(ref mut it) = krate.module {
153         if let ModuleItem(Module { ref mut items, .. }) = *it.kind {
154             items.extend(synth.impls);
155             items.extend(new_items);
156         } else {
157             panic!("collect-trait-impls can't run");
158         }
159     } else {
160         panic!("collect-trait-impls can't run");
161     }
162
163     krate
164 }
165
166 struct SyntheticImplCollector<'a, 'tcx> {
167     cx: &'a DocContext<'tcx>,
168     impls: Vec<Item>,
169 }
170
171 impl<'a, 'tcx> SyntheticImplCollector<'a, 'tcx> {
172     fn new(cx: &'a DocContext<'tcx>) -> Self {
173         SyntheticImplCollector { cx, impls: Vec::new() }
174     }
175 }
176
177 impl<'a, 'tcx> DocFolder for SyntheticImplCollector<'a, 'tcx> {
178     fn fold_item(&mut self, i: Item) -> Option<Item> {
179         if i.is_struct() || i.is_enum() || i.is_union() {
180             // FIXME(eddyb) is this `doc(hidden)` check needed?
181             if !self.cx.tcx.get_attrs(i.def_id).lists(sym::doc).has_word(sym::hidden) {
182                 self.cx.sess().time("get_auto_trait_and_blanket_synthetic_impls", || {
183                     self.impls.extend(get_auto_trait_and_blanket_impls(
184                         self.cx,
185                         self.cx.tcx.type_of(i.def_id),
186                         i.def_id,
187                     ));
188                 });
189             }
190         }
191
192         Some(self.fold_item_recur(i))
193     }
194 }
195
196 #[derive(Default)]
197 struct ItemCollector {
198     items: FxHashSet<DefId>,
199 }
200
201 impl ItemCollector {
202     fn new() -> Self {
203         Self::default()
204     }
205 }
206
207 impl DocFolder for ItemCollector {
208     fn fold_item(&mut self, i: Item) -> Option<Item> {
209         self.items.insert(i.def_id);
210
211         Some(self.fold_item_recur(i))
212     }
213 }
214
215 struct BadImplStripper {
216     prims: FxHashSet<PrimitiveType>,
217     items: FxHashSet<DefId>,
218 }
219
220 impl BadImplStripper {
221     fn keep_impl(&self, ty: &Type, cache: &Cache) -> bool {
222         if let Generic(_) = ty {
223             // keep impls made on generics
224             true
225         } else if let Some(prim) = ty.primitive_type() {
226             self.prims.contains(&prim)
227         } else if let Some(did) = ty.def_id(cache) {
228             self.keep_impl_with_def_id(&did)
229         } else {
230             false
231         }
232     }
233
234     fn keep_impl_with_def_id(&self, did: &DefId) -> bool {
235         self.items.contains(did)
236     }
237 }