]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/collect_trait_impls.rs
Rollup merge of #89509 - jhpratt:stabilize-const_unreachable_unchecked, r=oli-obk
[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
6 use rustc_data_structures::fx::FxHashSet;
7 use rustc_middle::ty::DefIdTree;
8 use rustc_span::symbol::sym;
9
10 crate const COLLECT_TRAIT_IMPLS: Pass = Pass {
11     name: "collect-trait-impls",
12     run: collect_trait_impls,
13     description: "retrieves trait impls for items in the crate",
14 };
15
16 crate fn collect_trait_impls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
17     let (mut krate, synth_impls) = cx.sess().time("collect_synthetic_impls", || {
18         let mut synth = SyntheticImplCollector { cx, impls: Vec::new() };
19         (synth.fold_crate(krate), synth.impls)
20     });
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.prof.generic_activity("build_extern_trait_impl").run(|| {
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.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
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 impls = get_auto_trait_and_blanket_impls(cx, def_id);
49                     new_items.extend(impls.filter(|i| cx.inlined.insert(i.def_id)));
50                 }
51             });
52         }
53     }
54
55     let mut cleaner = BadImplStripper { prims, items: crate_items };
56
57     // scan through included items ahead of time to splice in Deref targets to the "valid" sets
58     for it in &new_items {
59         if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind {
60             if cleaner.keep_impl(for_)
61                 && trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait()
62             {
63                 let target = items
64                     .iter()
65                     .find_map(|item| match *item.kind {
66                         TypedefItem(ref t, true) => Some(&t.type_),
67                         _ => None,
68                     })
69                     .expect("Deref impl without Target type");
70
71                 if let Some(prim) = target.primitive_type() {
72                     cleaner.prims.insert(prim);
73                 } else if let Some(did) = target.def_id() {
74                     cleaner.items.insert(did.into());
75                 }
76             }
77         }
78     }
79
80     new_items.retain(|it| {
81         if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind {
82             cleaner.keep_impl(for_)
83                 || trait_
84                     .as_ref()
85                     .map_or(false, |t| cleaner.keep_impl_with_def_id(t.def_id().into()))
86                 || blanket_impl.is_some()
87         } else {
88             true
89         }
90     });
91
92     // `tcx.crates(())` doesn't include the local crate, and `tcx.all_trait_implementations`
93     // doesn't work with it anyway, so pull them from the HIR map instead
94     let mut extra_attrs = Vec::new();
95     for &trait_did in cx.tcx.all_traits(()).iter() {
96         for &impl_did in cx.tcx.hir().trait_impls(trait_did) {
97             let impl_did = impl_did.to_def_id();
98             cx.tcx.sess.prof.generic_activity("build_local_trait_impl").run(|| {
99                 let mut parent = cx.tcx.parent(impl_did);
100                 while let Some(did) = parent {
101                     extra_attrs.extend(
102                         cx.tcx
103                             .get_attrs(did)
104                             .iter()
105                             .filter(|attr| attr.has_name(sym::doc))
106                             .filter(|attr| {
107                                 if let Some([attr]) = attr.meta_item_list().as_deref() {
108                                     attr.has_name(sym::cfg)
109                                 } else {
110                                     false
111                                 }
112                             })
113                             .cloned(),
114                     );
115                     parent = cx.tcx.parent(did);
116                 }
117                 inline::build_impl(cx, None, impl_did, Some(&extra_attrs), &mut new_items);
118                 extra_attrs.clear();
119             });
120         }
121     }
122
123     let items = if let ModuleItem(Module { ref mut items, .. }) = *krate.module.kind {
124         items
125     } else {
126         panic!("collect-trait-impls can't run");
127     };
128
129     items.extend(synth_impls);
130     items.extend(new_items);
131     krate
132 }
133
134 struct SyntheticImplCollector<'a, 'tcx> {
135     cx: &'a mut DocContext<'tcx>,
136     impls: Vec<Item>,
137 }
138
139 impl<'a, 'tcx> DocFolder for SyntheticImplCollector<'a, 'tcx> {
140     fn fold_item(&mut self, i: Item) -> Option<Item> {
141         if i.is_struct() || i.is_enum() || i.is_union() {
142             // FIXME(eddyb) is this `doc(hidden)` check needed?
143             if !self
144                 .cx
145                 .tcx
146                 .get_attrs(i.def_id.expect_def_id())
147                 .lists(sym::doc)
148                 .has_word(sym::hidden)
149             {
150                 self.impls
151                     .extend(get_auto_trait_and_blanket_impls(self.cx, i.def_id.expect_def_id()));
152             }
153         }
154
155         Some(self.fold_item_recur(i))
156     }
157 }
158
159 #[derive(Default)]
160 struct ItemCollector {
161     items: FxHashSet<ItemId>,
162 }
163
164 impl ItemCollector {
165     fn new() -> Self {
166         Self::default()
167     }
168 }
169
170 impl DocFolder for ItemCollector {
171     fn fold_item(&mut self, i: Item) -> Option<Item> {
172         self.items.insert(i.def_id);
173
174         Some(self.fold_item_recur(i))
175     }
176 }
177
178 struct BadImplStripper {
179     prims: FxHashSet<PrimitiveType>,
180     items: FxHashSet<ItemId>,
181 }
182
183 impl BadImplStripper {
184     fn keep_impl(&self, ty: &Type) -> bool {
185         if let Generic(_) = ty {
186             // keep impls made on generics
187             true
188         } else if let Some(prim) = ty.primitive_type() {
189             self.prims.contains(&prim)
190         } else if let Some(did) = ty.def_id() {
191             self.keep_impl_with_def_id(did.into())
192         } else {
193             false
194         }
195     }
196
197     fn keep_impl_with_def_id(&self, did: ItemId) -> bool {
198         self.items.contains(&did)
199     }
200 }