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