rustdoc/passes/
collect_trait_impls.rs1use rustc_data_structures::fx::FxHashSet;
6use rustc_hir::attrs::{AttributeKind, DocAttribute};
7use rustc_hir::def_id::LOCAL_CRATE;
8use rustc_hir::{Attribute, find_attr};
9use rustc_middle::ty;
10
11use super::Pass;
12use crate::clean::*;
13use crate::core::DocContext;
14use crate::formats::cache::Cache;
15use crate::visit::DocVisitor;
16
17pub(crate) const COLLECT_TRAIT_IMPLS: Pass = Pass {
18 name: "collect-trait-impls",
19 run: Some(collect_trait_impls),
20 description: "retrieves trait impls for items in the crate",
21};
22
23pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate {
24 let tcx = cx.tcx;
25 if tcx.dcx().has_errors().is_some() {
28 return krate;
29 }
30
31 let synth_impls = cx.sess().time("collect_synthetic_impls", || {
32 let mut synth = SyntheticImplCollector { cx, impls: Vec::new() };
33 synth.visit_crate(&krate);
34 synth.impls
35 });
36
37 let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
38 let prims: FxHashSet<PrimitiveType> = local_crate.primitives(tcx).map(|(_, p)| p).collect();
39
40 let crate_items = {
41 let mut coll = ItemAndAliasCollector::new(&cx.cache);
42 cx.sess().time("collect_items_for_trait_impls", || coll.visit_crate(&krate));
43 coll.items
44 };
45
46 let mut new_items_external = Vec::new();
47 let mut new_items_local = Vec::new();
48
49 {
51 let _prof_timer = tcx.sess.prof.generic_activity("build_extern_trait_impls");
52 for &cnum in tcx.crates(()) {
53 for &impl_def_id in tcx.trait_impls_in_crate(cnum) {
54 cx.with_param_env(impl_def_id, |cx| {
55 let opt_trait_ref = tcx.impl_opt_trait_ref(impl_def_id);
56 let self_ty = tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip();
57 let self_ty =
58 clean_middle_ty(ty::Binder::dummy(self_ty), cx, Some(impl_def_id), None);
59 if self_ty.is_full_generic()
60 || self_ty
61 .primitive_type()
62 .is_some_and(|primitive| prims.contains(&primitive))
63 || self_ty
64 .def_id(&cx.cache)
65 .is_some_and(|did| crate_items.contains(&ItemId::DefId(did)))
66 || opt_trait_ref.is_some_and(|trait_ref| {
67 crate_items.contains(&ItemId::DefId(trait_ref.def_id()))
68 || Some(trait_ref.def_id()) == tcx.lang_items().deref_trait()
69 || tcx.is_doc_notable_trait(trait_ref.def_id())
70 })
71 {
72 inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
73 }
74 });
75 }
76 }
77 }
78
79 {
81 let _prof_timer = tcx.sess.prof.generic_activity("build_local_trait_impls");
82 let mut attr_buf = Vec::new();
83 for &impl_def_id in tcx.trait_impls_in_crate(LOCAL_CRATE) {
84 let mut parent = Some(tcx.parent(impl_def_id));
85 while let Some(did) = parent {
86 attr_buf.extend(find_attr!(tcx, did, Doc(d) if !d.cfg.is_empty() => {
87 let mut new_attr = DocAttribute::default();
88 new_attr.cfg = d.cfg.clone();
89 Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr)))
90 }));
91 parent = tcx.opt_parent(did);
92 }
93 cx.with_param_env(impl_def_id, |cx| {
94 inline::build_impl(cx, impl_def_id, Some((&attr_buf, None)), &mut new_items_local);
95 });
96 attr_buf.clear();
97 }
98 }
99
100 tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
101 for def_id in PrimitiveType::all_impls(tcx) {
102 if !def_id.is_local() {
104 cx.with_param_env(def_id, |cx| {
105 inline::build_impl(cx, def_id, None, &mut new_items_external);
106 });
107 }
108 }
109 for (prim, did) in PrimitiveType::primitive_locations(tcx) {
110 if did.is_local() {
114 for def_id in prim.impls(tcx).filter(|&def_id| {
115 let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
131 match ty.kind() {
132 ty::Slice(ty) | ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => {
133 matches!(ty.kind(), ty::Param(..))
134 }
135 ty::Tuple(tys) => tys.iter().all(|ty| matches!(ty.kind(), ty::Param(..))),
136 _ => true,
137 }
138 }) {
139 let impls = synthesize_auto_trait_and_blanket_impls(cx, def_id);
140 new_items_external.extend(impls.filter(|i| cx.inlined.insert(i.item_id)));
141 }
142 }
143 }
144 });
145
146 if let ModuleItem(Module { items, .. }) = &mut krate.module.inner.kind {
147 items.extend(synth_impls);
148 items.extend(new_items_external);
149 items.extend(new_items_local);
150 } else {
151 panic!("collect-trait-impls can't run");
152 };
153
154 krate.external_traits.extend(cx.external_traits.drain(..));
155
156 krate
157}
158
159struct SyntheticImplCollector<'a, 'tcx> {
160 cx: &'a mut DocContext<'tcx>,
161 impls: Vec<Item>,
162}
163
164impl DocVisitor<'_> for SyntheticImplCollector<'_, '_> {
165 fn visit_item(&mut self, i: &Item) {
166 if i.is_struct() || i.is_enum() || i.is_union() {
167 if !self.cx.tcx.is_doc_hidden(i.item_id.expect_def_id()) {
169 self.impls.extend(synthesize_auto_trait_and_blanket_impls(
170 self.cx,
171 i.item_id.expect_def_id(),
172 ));
173 }
174 }
175
176 self.visit_item_recur(i)
177 }
178}
179
180struct ItemAndAliasCollector<'cache> {
181 items: FxHashSet<ItemId>,
182 cache: &'cache Cache,
183}
184
185impl<'cache> ItemAndAliasCollector<'cache> {
186 fn new(cache: &'cache Cache) -> Self {
187 ItemAndAliasCollector { items: FxHashSet::default(), cache }
188 }
189}
190
191impl DocVisitor<'_> for ItemAndAliasCollector<'_> {
192 fn visit_item(&mut self, i: &Item) {
193 self.items.insert(i.item_id);
194
195 if let TypeAliasItem(alias) = &i.inner.kind
196 && let Some(did) = alias.type_.def_id(self.cache)
197 {
198 self.items.insert(ItemId::DefId(did));
199 }
200
201 self.visit_item_recur(i)
202 }
203}