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