rustdoc/passes/
collect_trait_impls.rs1use rustc_data_structures::fx::FxHashSet;
6use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE};
7use rustc_middle::ty;
8use rustc_span::symbol::sym;
9use tracing::debug;
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).iter().map(|p| p.1).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 inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
56 });
57 }
58 }
59 }
60
61 {
63 let _prof_timer = tcx.sess.prof.generic_activity("build_local_trait_impls");
64 let mut attr_buf = Vec::new();
65 for &impl_def_id in tcx.trait_impls_in_crate(LOCAL_CRATE) {
66 let mut parent = Some(tcx.parent(impl_def_id));
67 while let Some(did) = parent {
68 attr_buf.extend(
69 tcx.get_attrs(did, sym::doc)
70 .filter(|attr| {
71 if let Some([attr]) = attr.meta_item_list().as_deref() {
72 attr.has_name(sym::cfg)
73 } else {
74 false
75 }
76 })
77 .cloned(),
78 );
79 parent = tcx.opt_parent(did);
80 }
81 cx.with_param_env(impl_def_id, |cx| {
82 inline::build_impl(cx, impl_def_id, Some((&attr_buf, None)), &mut new_items_local);
83 });
84 attr_buf.clear();
85 }
86 }
87
88 tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
89 for def_id in PrimitiveType::all_impls(tcx) {
90 if !def_id.is_local() {
92 cx.with_param_env(def_id, |cx| {
93 inline::build_impl(cx, def_id, None, &mut new_items_external);
94 });
95 }
96 }
97 for (prim, did) in PrimitiveType::primitive_locations(tcx) {
98 if did.is_local() {
102 for def_id in prim.impls(tcx).filter(|def_id| {
103 let ty = tcx.type_of(def_id).instantiate_identity();
119 match ty.kind() {
120 ty::Slice(ty) | ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => {
121 matches!(ty.kind(), ty::Param(..))
122 }
123 ty::Tuple(tys) => tys.iter().all(|ty| matches!(ty.kind(), ty::Param(..))),
124 _ => true,
125 }
126 }) {
127 let impls = synthesize_auto_trait_and_blanket_impls(cx, def_id);
128 new_items_external.extend(impls.filter(|i| cx.inlined.insert(i.item_id)));
129 }
130 }
131 }
132 });
133
134 let mut cleaner = BadImplStripper { prims, items: crate_items, cache: &cx.cache };
135 let mut type_did_to_deref_target: DefIdMap<&Type> = DefIdMap::default();
136
137 fn add_deref_target(
139 cx: &DocContext<'_>,
140 map: &DefIdMap<&Type>,
141 cleaner: &mut BadImplStripper<'_>,
142 targets: &mut DefIdSet,
143 type_did: DefId,
144 ) {
145 if let Some(target) = map.get(&type_did) {
146 debug!("add_deref_target: type {:?}, target {:?}", type_did, target);
147 if let Some(target_prim) = target.primitive_type() {
148 cleaner.prims.insert(target_prim);
149 } else if let Some(target_did) = target.def_id(&cx.cache) {
150 if !targets.insert(target_did) {
152 return;
154 }
155 cleaner.items.insert(target_did.into());
156 add_deref_target(cx, map, cleaner, targets, target_did);
157 }
158 }
159 }
160
161 for it in new_items_external.iter().chain(new_items_local.iter()) {
163 if let ImplItem(box Impl { ref for_, ref trait_, ref items, .. }) = it.kind
164 && trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait()
165 && cleaner.keep_impl(for_, true)
166 {
167 let target = items
168 .iter()
169 .find_map(|item| match item.kind {
170 AssocTypeItem(ref t, _) => Some(&t.type_),
171 _ => None,
172 })
173 .expect("Deref impl without Target type");
174
175 if let Some(prim) = target.primitive_type() {
176 cleaner.prims.insert(prim);
177 } else if let Some(did) = target.def_id(&cx.cache) {
178 cleaner.items.insert(did.into());
179 }
180 if let Some(for_did) = for_.def_id(&cx.cache) {
181 if type_did_to_deref_target.insert(for_did, target).is_none() {
182 if cleaner.keep_impl_with_def_id(for_did.into()) {
186 let mut targets = DefIdSet::default();
187 targets.insert(for_did);
188 add_deref_target(
189 cx,
190 &type_did_to_deref_target,
191 &mut cleaner,
192 &mut targets,
193 for_did,
194 );
195 }
196 }
197 }
198 }
199 }
200
201 new_items_external.retain(|it| {
203 if let ImplItem(box Impl { ref for_, ref trait_, ref kind, .. }) = it.kind {
204 cleaner.keep_impl(
205 for_,
206 trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait(),
207 ) || trait_.as_ref().is_some_and(|t| cleaner.keep_impl_with_def_id(t.def_id().into()))
208 || kind.is_blanket()
209 } else {
210 true
211 }
212 });
213
214 if let ModuleItem(Module { items, .. }) = &mut krate.module.inner.kind {
215 items.extend(synth_impls);
216 items.extend(new_items_external);
217 items.extend(new_items_local);
218 } else {
219 panic!("collect-trait-impls can't run");
220 };
221
222 krate.external_traits.extend(cx.external_traits.drain(..));
223
224 krate
225}
226
227struct SyntheticImplCollector<'a, 'tcx> {
228 cx: &'a mut DocContext<'tcx>,
229 impls: Vec<Item>,
230}
231
232impl DocVisitor<'_> for SyntheticImplCollector<'_, '_> {
233 fn visit_item(&mut self, i: &Item) {
234 if i.is_struct() || i.is_enum() || i.is_union() {
235 if !self.cx.tcx.is_doc_hidden(i.item_id.expect_def_id()) {
237 self.impls.extend(synthesize_auto_trait_and_blanket_impls(
238 self.cx,
239 i.item_id.expect_def_id(),
240 ));
241 }
242 }
243
244 self.visit_item_recur(i)
245 }
246}
247
248struct ItemAndAliasCollector<'cache> {
249 items: FxHashSet<ItemId>,
250 cache: &'cache Cache,
251}
252
253impl<'cache> ItemAndAliasCollector<'cache> {
254 fn new(cache: &'cache Cache) -> Self {
255 ItemAndAliasCollector { items: FxHashSet::default(), cache }
256 }
257}
258
259impl DocVisitor<'_> for ItemAndAliasCollector<'_> {
260 fn visit_item(&mut self, i: &Item) {
261 self.items.insert(i.item_id);
262
263 if let TypeAliasItem(alias) = &i.inner.kind
264 && let Some(did) = alias.type_.def_id(self.cache)
265 {
266 self.items.insert(ItemId::DefId(did));
267 }
268
269 self.visit_item_recur(i)
270 }
271}
272
273struct BadImplStripper<'a> {
274 prims: FxHashSet<PrimitiveType>,
275 items: FxHashSet<ItemId>,
276 cache: &'a Cache,
277}
278
279impl BadImplStripper<'_> {
280 fn keep_impl(&self, ty: &Type, is_deref: bool) -> bool {
281 if let Generic(_) = ty {
282 true
284 } else if let Some(prim) = ty.primitive_type() {
285 self.prims.contains(&prim)
286 } else if let Some(did) = ty.def_id(self.cache) {
287 is_deref || self.keep_impl_with_def_id(did.into())
288 } else {
289 false
290 }
291 }
292
293 fn keep_impl_with_def_id(&self, item_id: ItemId) -> bool {
294 self.items.contains(&item_id)
295 }
296}