1use rustc_data_structures::fx::FxHashSet;
6use rustc_hir::attrs::{AttributeKind, DocAttribute};
7use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LOCAL_CRATE};
8use rustc_hir::{Attribute, find_attr};
9use rustc_middle::ty;
10use tracing::debug;
11
12use super::Pass;
13use crate::clean::*;
14use crate::core::DocContext;
15use crate::formats::cache::Cache;
16use crate::visit::DocVisitor;
17
18pub(crate) const COLLECT_TRAIT_IMPLS: Pass = Pass {
19 name: "collect-trait-impls",
20 run: Some(collect_trait_impls),
21 description: "retrieves trait impls for items in the crate",
22};
23
24pub(crate) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate {
25 let tcx = cx.tcx;
26 if tcx.dcx().has_errors().is_some() {
29 return krate;
30 }
31
32 let synth_impls = cx.sess().time("collect_synthetic_impls", || {
33 let mut synth = SyntheticImplCollector { cx, impls: Vec::new() };
34 synth.visit_crate(&krate);
35 synth.impls
36 });
37
38 let local_crate = ExternalCrate { crate_num: LOCAL_CRATE };
39 let prims: FxHashSet<PrimitiveType> = local_crate.primitives(tcx).map(|(_, p)| p).collect();
40
41 let crate_items = {
42 let mut coll = ItemAndAliasCollector::new(&cx.cache);
43 cx.sess().time("collect_items_for_trait_impls", || coll.visit_crate(&krate));
44 coll.items
45 };
46
47 let mut new_items_external = Vec::new();
48 let mut new_items_local = Vec::new();
49
50 {
52 let _prof_timer = tcx.sess.prof.generic_activity("build_extern_trait_impls");
53 for &cnum in tcx.crates(()) {
54 for &impl_def_id in tcx.trait_impls_in_crate(cnum) {
55 cx.with_param_env(impl_def_id, |cx| {
56 inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
57 });
58 }
59 }
60 }
61
62 {
64 let _prof_timer = tcx.sess.prof.generic_activity("build_local_trait_impls");
65 let mut attr_buf = Vec::new();
66 for &impl_def_id in tcx.trait_impls_in_crate(LOCAL_CRATE) {
67 let mut parent = Some(tcx.parent(impl_def_id));
68 while let Some(did) = parent {
69 attr_buf.extend(find_attr!(tcx, did, Doc(d) if !d.cfg.is_empty() => {
70 let mut new_attr = DocAttribute::default();
71 new_attr.cfg = d.cfg.clone();
72 Attribute::Parsed(AttributeKind::Doc(Box::new(new_attr)))
73 }));
74 parent = tcx.opt_parent(did);
75 }
76 cx.with_param_env(impl_def_id, |cx| {
77 inline::build_impl(cx, impl_def_id, Some((&attr_buf, None)), &mut new_items_local);
78 });
79 attr_buf.clear();
80 }
81 }
82
83 tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
84 for def_id in PrimitiveType::all_impls(tcx) {
85 if !def_id.is_local() {
87 cx.with_param_env(def_id, |cx| {
88 inline::build_impl(cx, def_id, None, &mut new_items_external);
89 });
90 }
91 }
92 for (prim, did) in PrimitiveType::primitive_locations(tcx) {
93 if did.is_local() {
97 for def_id in prim.impls(tcx).filter(|&def_id| {
98 let ty = tcx.type_of(def_id).instantiate_identity();
114 match ty.kind() {
115 ty::Slice(ty) | ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => {
116 matches!(ty.kind(), ty::Param(..))
117 }
118 ty::Tuple(tys) => tys.iter().all(|ty| matches!(ty.kind(), ty::Param(..))),
119 _ => true,
120 }
121 }) {
122 let impls = synthesize_auto_trait_and_blanket_impls(cx, def_id);
123 new_items_external.extend(impls.filter(|i| cx.inlined.insert(i.item_id)));
124 }
125 }
126 }
127 });
128
129 let mut cleaner = BadImplStripper { prims, items: crate_items, cache: &cx.cache };
130 let mut type_did_to_deref_target: DefIdMap<&Type> = DefIdMap::default();
131
132 fn add_deref_target(
134 cx: &DocContext<'_>,
135 map: &DefIdMap<&Type>,
136 cleaner: &mut BadImplStripper<'_>,
137 targets: &mut DefIdSet,
138 type_did: DefId,
139 ) {
140 if let Some(target) = map.get(&type_did) {
141 debug!("add_deref_target: type {:?}, target {:?}", type_did, target);
142 if let Some(target_prim) = target.primitive_type() {
143 cleaner.prims.insert(target_prim);
144 } else if let Some(target_did) = target.def_id(&cx.cache) {
145 if !targets.insert(target_did) {
147 return;
149 }
150 cleaner.items.insert(target_did.into());
151 add_deref_target(cx, map, cleaner, targets, target_did);
152 }
153 }
154 }
155
156 for it in new_items_external.iter().chain(new_items_local.iter()) {
158 if let ImplItem(box Impl { ref for_, ref trait_, ref items, polarity, .. }) = it.kind
159 && trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait()
160 && polarity != ty::ImplPolarity::Negative
161 && cleaner.keep_impl(for_, true)
162 {
163 let target = items
164 .iter()
165 .find_map(|item| match item.kind {
166 AssocTypeItem(ref t, _) => Some(&t.type_),
167 _ => None,
168 })
169 .expect("Deref impl without Target type");
170
171 if let Some(prim) = target.primitive_type() {
172 cleaner.prims.insert(prim);
173 } else if let Some(did) = target.def_id(&cx.cache) {
174 cleaner.items.insert(did.into());
175 }
176 if let Some(for_did) = for_.def_id(&cx.cache)
177 && type_did_to_deref_target.insert(for_did, target).is_none()
178 && cleaner.keep_impl_with_def_id(for_did.into())
182 {
183 let mut targets = DefIdSet::default();
184 targets.insert(for_did);
185 add_deref_target(
186 cx,
187 &type_did_to_deref_target,
188 &mut cleaner,
189 &mut targets,
190 for_did,
191 );
192 }
193 }
194 }
195
196 new_items_external.retain(|it| {
198 if let ImplItem(box Impl { ref for_, ref trait_, ref kind, .. }) = it.kind {
199 cleaner.keep_impl(
200 for_,
201 trait_.as_ref().map(|t| t.def_id()) == tcx.lang_items().deref_trait(),
202 ) || trait_.as_ref().is_some_and(|t| cleaner.keep_impl_with_def_id(t.def_id().into()))
203 || kind.is_blanket()
204 } else {
205 true
206 }
207 });
208
209 if let ModuleItem(Module { items, .. }) = &mut krate.module.inner.kind {
210 items.extend(synth_impls);
211 items.extend(new_items_external);
212 items.extend(new_items_local);
213 } else {
214 panic!("collect-trait-impls can't run");
215 };
216
217 krate.external_traits.extend(cx.external_traits.drain(..));
218
219 krate
220}
221
222struct SyntheticImplCollector<'a, 'tcx> {
223 cx: &'a mut DocContext<'tcx>,
224 impls: Vec<Item>,
225}
226
227impl DocVisitor<'_> for SyntheticImplCollector<'_, '_> {
228 fn visit_item(&mut self, i: &Item) {
229 if i.is_struct() || i.is_enum() || i.is_union() {
230 if !self.cx.tcx.is_doc_hidden(i.item_id.expect_def_id()) {
232 self.impls.extend(synthesize_auto_trait_and_blanket_impls(
233 self.cx,
234 i.item_id.expect_def_id(),
235 ));
236 }
237 }
238
239 self.visit_item_recur(i)
240 }
241}
242
243struct ItemAndAliasCollector<'cache> {
244 items: FxHashSet<ItemId>,
245 cache: &'cache Cache,
246}
247
248impl<'cache> ItemAndAliasCollector<'cache> {
249 fn new(cache: &'cache Cache) -> Self {
250 ItemAndAliasCollector { items: FxHashSet::default(), cache }
251 }
252}
253
254impl DocVisitor<'_> for ItemAndAliasCollector<'_> {
255 fn visit_item(&mut self, i: &Item) {
256 self.items.insert(i.item_id);
257
258 if let TypeAliasItem(alias) = &i.inner.kind
259 && let Some(did) = alias.type_.def_id(self.cache)
260 {
261 self.items.insert(ItemId::DefId(did));
262 }
263
264 self.visit_item_recur(i)
265 }
266}
267
268struct BadImplStripper<'a> {
269 prims: FxHashSet<PrimitiveType>,
270 items: FxHashSet<ItemId>,
271 cache: &'a Cache,
272}
273
274impl BadImplStripper<'_> {
275 fn keep_impl(&self, ty: &Type, is_deref: bool) -> bool {
276 if let Generic(_) = ty {
277 true
279 } else if let Some(prim) = ty.primitive_type() {
280 self.prims.contains(&prim)
281 } else if let Some(did) = ty.def_id(self.cache) {
282 is_deref || self.keep_impl_with_def_id(did.into())
283 } else {
284 false
285 }
286 }
287
288 fn keep_impl_with_def_id(&self, item_id: ItemId) -> bool {
289 self.items.contains(&item_id)
290 }
291}