]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/passes/collect_trait_impls.rs
Rollup merge of #86776 - tmiasko:v0-skip-layout-query, r=petrochenkov
[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.into());
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_) && trait_.def_id() == cx.tcx.lang_items().deref_trait() {
61                 let target = items
62                     .iter()
63                     .find_map(|item| match *item.kind {
64                         TypedefItem(ref t, true) => Some(&t.type_),
65                         _ => None,
66                     })
67                     .expect("Deref impl without Target type");
68
69                 if let Some(prim) = target.primitive_type() {
70                     cleaner.prims.insert(prim);
71                 } else if let Some(did) = target.def_id() {
72                     cleaner.items.insert(did.into());
73                 }
74             }
75         }
76     }
77
78     new_items.retain(|it| {
79         if let ImplItem(Impl { ref for_, ref trait_, ref blanket_impl, .. }) = *it.kind {
80             cleaner.keep_impl(for_)
81                 || trait_.as_ref().map_or(false, |t| cleaner.keep_impl(t))
82                 || blanket_impl.is_some()
83         } else {
84             true
85         }
86     });
87
88     // `tcx.crates(())` doesn't include the local crate, and `tcx.all_trait_implementations`
89     // doesn't work with it anyway, so pull them from the HIR map instead
90     let mut extra_attrs = Vec::new();
91     for &trait_did in cx.tcx.all_traits(()).iter() {
92         for &impl_did in cx.tcx.hir().trait_impls(trait_did) {
93             let impl_did = impl_did.to_def_id();
94             cx.tcx.sess.prof.generic_activity("build_local_trait_impl").run(|| {
95                 let mut parent = cx.tcx.parent(impl_did);
96                 while let Some(did) = parent {
97                     extra_attrs.extend(
98                         cx.tcx
99                             .get_attrs(did)
100                             .iter()
101                             .filter(|attr| attr.has_name(sym::doc))
102                             .filter(|attr| {
103                                 if let Some([attr]) = attr.meta_item_list().as_deref() {
104                                     attr.has_name(sym::cfg)
105                                 } else {
106                                     false
107                                 }
108                             })
109                             .cloned(),
110                     );
111                     parent = cx.tcx.parent(did);
112                 }
113                 inline::build_impl(cx, None, impl_did, Some(&extra_attrs), &mut new_items);
114                 extra_attrs.clear();
115             });
116         }
117     }
118
119     let items = if let ModuleItem(Module { ref mut items, .. }) = *krate.module.kind {
120         items
121     } else {
122         panic!("collect-trait-impls can't run");
123     };
124
125     items.extend(synth_impls);
126     items.extend(new_items);
127     krate
128 }
129
130 struct SyntheticImplCollector<'a, 'tcx> {
131     cx: &'a mut DocContext<'tcx>,
132     impls: Vec<Item>,
133 }
134
135 impl<'a, 'tcx> DocFolder for SyntheticImplCollector<'a, 'tcx> {
136     fn fold_item(&mut self, i: Item) -> Option<Item> {
137         if i.is_struct() || i.is_enum() || i.is_union() {
138             // FIXME(eddyb) is this `doc(hidden)` check needed?
139             if !self.cx.tcx.get_attrs(i.def_id.expect_real()).lists(sym::doc).has_word(sym::hidden)
140             {
141                 self.impls
142                     .extend(get_auto_trait_and_blanket_impls(self.cx, i.def_id.expect_real()));
143             }
144         }
145
146         Some(self.fold_item_recur(i))
147     }
148 }
149
150 #[derive(Default)]
151 struct ItemCollector {
152     items: FxHashSet<FakeDefId>,
153 }
154
155 impl ItemCollector {
156     fn new() -> Self {
157         Self::default()
158     }
159 }
160
161 impl DocFolder for ItemCollector {
162     fn fold_item(&mut self, i: Item) -> Option<Item> {
163         self.items.insert(i.def_id);
164
165         Some(self.fold_item_recur(i))
166     }
167 }
168
169 struct BadImplStripper {
170     prims: FxHashSet<PrimitiveType>,
171     items: FxHashSet<FakeDefId>,
172 }
173
174 impl BadImplStripper {
175     fn keep_impl(&self, ty: &Type) -> bool {
176         if let Generic(_) = ty {
177             // keep impls made on generics
178             true
179         } else if let Some(prim) = ty.primitive_type() {
180             self.prims.contains(&prim)
181         } else if let Some(did) = ty.def_id() {
182             self.keep_impl_with_def_id(did.into())
183         } else {
184             false
185         }
186     }
187
188     fn keep_impl_with_def_id(&self, did: FakeDefId) -> bool {
189         self.items.contains(&did)
190     }
191 }