1use rustc_data_structures::fx::FxHashSet;
7use rustc_errors::FatalError;
8use rustc_hir::attrs::{AttributeKind, DocAttribute};
9use rustc_hir::def_id::{DefId, LOCAL_CRATE};
10use rustc_hir::{Attribute, find_attr};
11use rustc_middle::ty::{self, Ty, TyCtxt};
12use rustc_span::kw;
13use tracing::debug;
14
15use crate::clean::*;
16use crate::core::DocContext;
17use crate::formats::cache::Cache;
18use crate::visit::DocVisitor;
19
20pub(super) fn collect_trait_impls(mut krate: Crate, cx: &mut DocContext<'_>) -> Crate {
21 let tcx = cx.tcx;
22 if tcx.dcx().has_errors().is_some() {
25 return krate;
26 }
27
28 let synth_impls = cx.sess().time("collect_synthetic_impls", || {
29 let mut synth = SyntheticImplCollector { cx, impls: Vec::new() };
30 synth.visit_crate(&krate);
31 synth.impls
32 });
33
34 let crate_items = {
35 let mut coll = ItemAndAliasCollector::new(&cx.cache);
36 cx.sess().time("collect_items_for_trait_impls", || coll.visit_crate(&krate));
37 coll.items
38 };
39
40 let mut new_items_external = Vec::new();
41 let mut new_items_local = Vec::new();
42
43 {
45 let _prof_timer = tcx.sess.prof.generic_activity("build_extern_trait_impls");
46 for &cnum in tcx.crates(()) {
47 for &impl_def_id in tcx.trait_impls_in_crate(cnum) {
48 let trait_ref = tcx.impl_trait_ref(impl_def_id);
49 debug!("considering extern trait impl {trait_ref:?}");
50 if crate_items.contains(&ItemId::DefId(trait_ref.def_id()))
51 || Some(trait_ref.def_id()) == tcx.lang_items().deref_trait()
52 || tcx.is_doc_notable_trait(trait_ref.def_id())
53 {
54 debug!("-> inlining due to trait");
55 cx.with_param_env(impl_def_id, |cx| {
56 inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
57 });
58 } else {
59 let self_ty = tcx.type_of(impl_def_id).instantiate_identity().skip_norm_wip();
60 debug!(?self_ty);
61 let self_ty_head = SelfTyHead::of(ty::Binder::dummy(self_ty), tcx, impl_def_id);
62 debug!(?self_ty_head);
63 let keep_impl = match self_ty_head {
64 SelfTyHead::Generic => true,
65 SelfTyHead::Item(def_id) => crate_items.contains(&ItemId::DefId(def_id)),
66 SelfTyHead::Primitive | SelfTyHead::Other => false,
67 };
68 if keep_impl {
69 debug!("-> inlining due to self ty");
70 cx.with_param_env(impl_def_id, |cx| {
71 inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
72 });
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 (prim, did) in PrimitiveType::primitive_locations(tcx) {
102 if did.is_local() {
106 for impl_def_id in prim.impls(tcx) {
107 if !impl_def_id.is_local() {
109 cx.with_param_env(impl_def_id, |cx| {
110 inline::build_impl(cx, impl_def_id, None, &mut new_items_external);
111 });
112 }
113 }
114
115 for def_id in prim.impls(tcx).filter(|&def_id| {
117 let ty = tcx.type_of(def_id).instantiate_identity().skip_norm_wip();
133 match ty.kind() {
134 ty::Slice(ty) | ty::Ref(_, ty, _) | ty::RawPtr(ty, _) => {
135 matches!(ty.kind(), ty::Param(..))
136 }
137 ty::Tuple(tys) => tys.iter().all(|ty| matches!(ty.kind(), ty::Param(..))),
138 _ => true,
139 }
140 }) {
141 let impls = synthesize_auto_trait_and_blanket_impls(cx, def_id);
142 new_items_external.extend(impls.filter(|i| cx.inlined.insert(i.item_id)));
143 }
144 }
145 }
146 });
147
148 if let ModuleItem(Module { items, .. }) = &mut krate.module.inner.kind {
149 items.extend(synth_impls);
150 items.extend(new_items_external);
151 items.extend(new_items_local);
152 } else {
153 panic!("collect-trait-impls can't run");
154 };
155
156 krate.external_traits.extend(cx.external_traits.drain(..));
157
158 krate
159}
160
161#[derive(Debug)]
162enum SelfTyHead {
163 Generic,
164 Primitive,
165 Item(DefId),
166 Other,
167}
168
169impl SelfTyHead {
170 fn of<'tcx>(bound_ty: ty::Binder<'tcx, Ty<'tcx>>, tcx: TyCtxt<'tcx>, parent: DefId) -> Self {
182 match *bound_ty.skip_binder().kind() {
183 ty::Never
184 | ty::Bool
185 | ty::Char
186 | ty::Int(..)
187 | ty::Uint(..)
188 | ty::Float(..)
189 | ty::Str
190 | ty::Slice(..)
191 | ty::Array(..)
192 | ty::RawPtr(..)
193 | ty::FnDef(..)
194 | ty::FnPtr(..)
195 | ty::Tuple(_) => Self::Primitive,
196 ty::Pat(ty, _) => Self::of(bound_ty.rebind(ty), tcx, parent),
197 ty::Ref(_, ty, _) => match Self::of(bound_ty.rebind(ty), tcx, parent) {
198 Self::Generic => Self::Primitive,
199 head => head,
200 },
201 ty::UnsafeBinder(_) => Self::Other,
204 ty::Adt(def, _) => Self::Item(def.did()),
205 ty::Foreign(did) => Self::Item(did),
206 ty::Dynamic(obj, _) => {
207 let mut dids = obj.auto_traits();
211 let did = obj
212 .principal_def_id()
213 .or_else(|| dids.next())
214 .unwrap_or_else(|| panic!("found trait object `{obj:?}` with no traits?"));
215 Self::Item(did)
216 }
217
218 ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Projection { def_id }, .. }) => {
219 debug_assert!(!tcx.is_impl_trait_in_trait(def_id));
220 Self::of(bound_ty.rebind(alias_ty.self_ty()), tcx, parent)
221 }
222
223 ty::Alias(_, alias_ty @ ty::AliasTy { kind: ty::Inherent { .. }, .. }) => {
224 let alias_ty = bound_ty.rebind(alias_ty);
225 Self::of(alias_ty.map_bound(|ty| ty.self_ty()), tcx, parent)
226 }
227
228 ty::Alias(_, ty::AliasTy { kind: ty::Free { def_id }, args, .. }) => {
229 if tcx.features().checked_type_aliases() {
230 Self::Item(def_id)
233 } else {
234 let ty = tcx.type_of(def_id).instantiate(tcx, args).skip_norm_wip();
235 Self::of(bound_ty.rebind(ty), tcx, parent)
236 }
237 }
238
239 ty::Param(ref p) => {
240 if p.name == kw::SelfUpper { Self::Other } else { Self::Generic }
245 }
246
247 ty::Bound(_, ref ty) => match ty.kind {
248 ty::BoundTyKind::Param(_) => Self::Generic,
249 ty::BoundTyKind::Anon => panic!("unexpected anonymous bound type variable"),
250 },
251
252 ty::Alias(_, ty::AliasTy { kind: ty::Opaque { .. }, .. }) => {
253 panic!("{bound_ty} should not appear as impl self ty")
254 }
255
256 ty::Closure(..)
257 | ty::CoroutineClosure(..)
258 | ty::Coroutine(..)
259 | ty::Placeholder(..)
260 | ty::CoroutineWitness(..)
261 | ty::Infer(..) => panic!("unexpected impl self ty {bound_ty}"),
262
263 ty::Error(_) => FatalError.raise(),
264 }
265 }
266}
267
268struct SyntheticImplCollector<'a, 'tcx> {
269 cx: &'a mut DocContext<'tcx>,
270 impls: Vec<Item>,
271}
272
273impl DocVisitor<'_> for SyntheticImplCollector<'_, '_> {
274 fn visit_item(&mut self, i: &Item) {
275 if i.is_struct() || i.is_enum() || i.is_union() {
276 let item_def_id = i.item_id.expect_def_id();
277 if (self.cx.document_private()
280 || self.cx.cache.effective_visibilities.is_reachable(self.cx.tcx, item_def_id))
281 && !self.cx.tcx.is_doc_hidden(item_def_id)
282 {
283 self.impls.extend(synthesize_auto_trait_and_blanket_impls(self.cx, item_def_id));
284 }
285 }
286
287 self.visit_item_recur(i)
288 }
289}
290
291struct ItemAndAliasCollector<'cache> {
292 items: FxHashSet<ItemId>,
293 cache: &'cache Cache,
294}
295
296impl<'cache> ItemAndAliasCollector<'cache> {
297 fn new(cache: &'cache Cache) -> Self {
298 ItemAndAliasCollector { items: FxHashSet::default(), cache }
299 }
300}
301
302impl DocVisitor<'_> for ItemAndAliasCollector<'_> {
303 fn visit_item(&mut self, i: &Item) {
304 self.items.insert(i.item_id);
305
306 if let TypeAliasItem(alias) = &i.inner.kind
307 && let Some(did) = alias.type_.def_id(self.cache)
308 {
309 self.items.insert(ItemId::DefId(did));
310 }
311
312 self.visit_item_recur(i)
313 }
314}