]>
Commit | Line | Data |
---|---|---|
3dfed10e | 1 | use std::mem; |
3dfed10e | 2 | |
353b0b11 | 3 | use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexSet}; |
9ffffee4 | 4 | use rustc_hir::def_id::{CrateNum, DefId, DefIdMap, DefIdSet}; |
923072b8 | 5 | use rustc_middle::ty::{self, TyCtxt}; |
2b03887a | 6 | use rustc_span::Symbol; |
3dfed10e | 7 | |
a2a8927a | 8 | use crate::clean::{self, types::ExternalLocation, ExternalCrate, ItemId, PrimitiveType}; |
3c0e092e | 9 | use crate::core::DocContext; |
3dfed10e XL |
10 | use crate::fold::DocFolder; |
11 | use crate::formats::item_type::ItemType; | |
12 | use crate::formats::Impl; | |
5099ac24 | 13 | use crate::html::format::join_with_double_colon; |
fc512014 | 14 | use crate::html::markdown::short_markdown_summary; |
a2a8927a | 15 | use crate::html::render::search_index::get_function_type_for_search; |
3dfed10e | 16 | use crate::html::render::IndexItem; |
487cf647 | 17 | use crate::visit_lib::RustdocEffectiveVisibilities; |
3dfed10e | 18 | |
fc512014 | 19 | /// This cache is used to store information about the [`clean::Crate`] being |
3dfed10e XL |
20 | /// rendered in order to provide more useful documentation. This contains |
21 | /// information like all implementors of a trait, all traits a type implements, | |
22 | /// documentation for all known traits, etc. | |
23 | /// | |
24 | /// This structure purposefully does not implement `Clone` because it's intended | |
25 | /// to be a fairly large and expensive structure to clone. Instead this adheres | |
94222f64 | 26 | /// to `Send` so it may be stored in an `Arc` instance and shared among the various |
3dfed10e XL |
27 | /// rendering threads. |
28 | #[derive(Default)] | |
923072b8 | 29 | pub(crate) struct Cache { |
3dfed10e | 30 | /// Maps a type ID to all known implementations for that type. This is only |
3c0e092e | 31 | /// recognized for intra-crate [`clean::Type::Path`]s, and is used to print |
3dfed10e XL |
32 | /// out extra documentation on the page of an enum/struct. |
33 | /// | |
34 | /// The values of the map are a list of implementations and documentation | |
35 | /// found on that implementation. | |
9ffffee4 | 36 | pub(crate) impls: DefIdMap<Vec<Impl>>, |
3dfed10e XL |
37 | |
38 | /// Maintains a mapping of local crate `DefId`s to the fully qualified name | |
39 | /// and "short type description" of that node. This is used when generating | |
40 | /// URLs when a type is being linked to. External paths are not located in | |
41 | /// this map because the `External` type itself has all the information | |
42 | /// necessary. | |
923072b8 | 43 | pub(crate) paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>, |
3dfed10e XL |
44 | |
45 | /// Similar to `paths`, but only holds external paths. This is only used for | |
46 | /// generating explicit hyperlinks to other crates. | |
923072b8 | 47 | pub(crate) external_paths: FxHashMap<DefId, (Vec<Symbol>, ItemType)>, |
3dfed10e XL |
48 | |
49 | /// Maps local `DefId`s of exported types to fully qualified paths. | |
50 | /// Unlike 'paths', this mapping ignores any renames that occur | |
51 | /// due to 'use' statements. | |
52 | /// | |
53 | /// This map is used when writing out the special 'implementors' | |
54 | /// javascript file. By using the exact path that the type | |
55 | /// is declared with, we ensure that each path will be identical | |
56 | /// to the path used if the corresponding type is inlined. By | |
57 | /// doing this, we can detect duplicate impls on a trait page, and only display | |
58 | /// the impl for the inlined type. | |
9ffffee4 | 59 | pub(crate) exact_paths: DefIdMap<Vec<Symbol>>, |
3dfed10e XL |
60 | |
61 | /// This map contains information about all known traits of this crate. | |
62 | /// Implementations of a crate should inherit the documentation of the | |
63 | /// parent trait if no extra documentation is specified, and default methods | |
64 | /// should show up in documentation about trait implementations. | |
2b03887a | 65 | pub(crate) traits: FxHashMap<DefId, clean::Trait>, |
3dfed10e XL |
66 | |
67 | /// When rendering traits, it's often useful to be able to list all | |
68 | /// implementors of the trait, and this mapping is exactly, that: a mapping | |
69 | /// of trait ids to the list of known implementors of the trait | |
923072b8 | 70 | pub(crate) implementors: FxHashMap<DefId, Vec<Impl>>, |
3dfed10e XL |
71 | |
72 | /// Cache of where external crate documentation can be found. | |
923072b8 | 73 | pub(crate) extern_locations: FxHashMap<CrateNum, ExternalLocation>, |
3dfed10e XL |
74 | |
75 | /// Cache of where documentation for primitives can be found. | |
923072b8 | 76 | pub(crate) primitive_locations: FxHashMap<clean::PrimitiveType, DefId>, |
3dfed10e XL |
77 | |
78 | // Note that external items for which `doc(hidden)` applies to are shown as | |
79 | // non-reachable while local items aren't. This is because we're reusing | |
2b03887a | 80 | // the effective visibilities from the privacy check pass. |
487cf647 | 81 | pub(crate) effective_visibilities: RustdocEffectiveVisibilities, |
3dfed10e XL |
82 | |
83 | /// The version of the crate being documented, if given from the `--crate-version` flag. | |
923072b8 | 84 | pub(crate) crate_version: Option<String>, |
3dfed10e XL |
85 | |
86 | /// Whether to document private items. | |
87 | /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. | |
923072b8 | 88 | pub(crate) document_private: bool, |
3dfed10e | 89 | |
6a06907d XL |
90 | /// Crates marked with [`#[doc(masked)]`][doc_masked]. |
91 | /// | |
92 | /// [doc_masked]: https://doc.rust-lang.org/nightly/unstable-book/language-features/doc-masked.html | |
923072b8 | 93 | pub(crate) masked_crates: FxHashSet<CrateNum>, |
6a06907d | 94 | |
3dfed10e | 95 | // Private fields only used when initially crawling a crate to build a cache |
5099ac24 | 96 | stack: Vec<Symbol>, |
923072b8 | 97 | parent_stack: Vec<ParentStackItem>, |
3dfed10e | 98 | stripped_mod: bool, |
3dfed10e | 99 | |
923072b8 | 100 | pub(crate) search_index: Vec<IndexItem>, |
3dfed10e XL |
101 | |
102 | // In rare case where a structure is defined in one module but implemented | |
103 | // in another, if the implementing module is parsed before defining module, | |
104 | // then the fully qualified name of the structure isn't presented in `paths` | |
105 | // yet when its implementation methods are being indexed. Caches such methods | |
106 | // and their parent id here and indexes them at the end of crate parsing. | |
923072b8 | 107 | pub(crate) orphan_impl_items: Vec<OrphanImplItem>, |
3dfed10e XL |
108 | |
109 | // Similarly to `orphan_impl_items`, sometimes trait impls are picked up | |
110 | // even though the trait itself is not exported. This can happen if a trait | |
111 | // was defined in function/expression scope, since the impl will be picked | |
112 | // up by `collect-trait-impls` but the trait won't be scraped out in the HIR | |
cdc7bbd5 | 113 | // crawl. In order to prevent crashes when looking for notable traits or |
3dfed10e XL |
114 | // when gathering trait documentation on a type, hold impls here while |
115 | // folding and add them to the cache later on if we find the trait. | |
116 | orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>, | |
117 | ||
cdc7bbd5 XL |
118 | /// All intra-doc links resolved so far. |
119 | /// | |
120 | /// Links are indexed by the DefId of the item they document. | |
353b0b11 | 121 | pub(crate) intra_doc_links: FxHashMap<ItemId, FxIndexSet<clean::ItemLink>>, |
c295e0f8 | 122 | /// Cfg that have been hidden via #![doc(cfg_hide(...))] |
923072b8 | 123 | pub(crate) hidden_cfg: FxHashSet<clean::cfg::Cfg>, |
3dfed10e XL |
124 | } |
125 | ||
6a06907d XL |
126 | /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`. |
127 | struct CacheBuilder<'a, 'tcx> { | |
128 | cache: &'a mut Cache, | |
04454e1e | 129 | /// This field is used to prevent duplicated impl blocks. |
9ffffee4 | 130 | impl_ids: DefIdMap<DefIdSet>, |
6a06907d XL |
131 | tcx: TyCtxt<'tcx>, |
132 | } | |
133 | ||
3dfed10e | 134 | impl Cache { |
487cf647 FG |
135 | pub(crate) fn new(document_private: bool) -> Self { |
136 | Cache { document_private, ..Cache::default() } | |
6a06907d XL |
137 | } |
138 | ||
139 | /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was | |
140 | /// in `krate` due to the data being moved into the `Cache`. | |
923072b8 | 141 | pub(crate) fn populate(cx: &mut DocContext<'_>, mut krate: clean::Crate) -> clean::Crate { |
3c0e092e XL |
142 | let tcx = cx.tcx; |
143 | ||
3dfed10e | 144 | // Crawl the crate to build various caches used for the output |
3c0e092e XL |
145 | debug!(?cx.cache.crate_version); |
146 | cx.cache.traits = krate.external_traits.take(); | |
3dfed10e XL |
147 | |
148 | // Cache where all our extern crates are located | |
149 | // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code | |
3c0e092e XL |
150 | for &crate_num in cx.tcx.crates(()) { |
151 | let e = ExternalCrate { crate_num }; | |
152 | ||
cdc7bbd5 | 153 | let name = e.name(tcx); |
3c0e092e | 154 | let render_options = &cx.render_options; |
a2a8927a | 155 | let extern_url = render_options.extern_html_root_urls.get(name.as_str()).map(|u| &**u); |
3c0e092e XL |
156 | let extern_url_takes_precedence = render_options.extern_html_root_takes_precedence; |
157 | let dst = &render_options.output; | |
158 | let location = e.location(extern_url, extern_url_takes_precedence, dst, tcx); | |
159 | cx.cache.extern_locations.insert(e.crate_num, location); | |
5099ac24 | 160 | cx.cache.external_paths.insert(e.def_id(), (vec![name], ItemType::Module)); |
3dfed10e XL |
161 | } |
162 | ||
c295e0f8 | 163 | // FIXME: avoid this clone (requires implementing Default manually) |
3c0e092e XL |
164 | cx.cache.primitive_locations = PrimitiveType::primitive_locations(tcx).clone(); |
165 | for (prim, &def_id) in &cx.cache.primitive_locations { | |
c295e0f8 XL |
166 | let crate_name = tcx.crate_name(def_id.krate); |
167 | // Recall that we only allow primitive modules to be at the root-level of the crate. | |
168 | // If that restriction is ever lifted, this will have to include the relative paths instead. | |
5099ac24 FG |
169 | cx.cache |
170 | .external_paths | |
171 | .insert(def_id, (vec![crate_name, prim.as_sym()], ItemType::Primitive)); | |
3dfed10e XL |
172 | } |
173 | ||
04454e1e FG |
174 | let (krate, mut impl_ids) = { |
175 | let mut cache_builder = | |
9ffffee4 | 176 | CacheBuilder { tcx, cache: &mut cx.cache, impl_ids: Default::default() }; |
04454e1e FG |
177 | krate = cache_builder.fold_crate(krate); |
178 | (krate, cache_builder.impl_ids) | |
179 | }; | |
3dfed10e | 180 | |
3c0e092e XL |
181 | for (trait_did, dids, impl_) in cx.cache.orphan_trait_impls.drain(..) { |
182 | if cx.cache.traits.contains_key(&trait_did) { | |
3dfed10e | 183 | for did in dids { |
04454e1e FG |
184 | if impl_ids.entry(did).or_default().insert(impl_.def_id()) { |
185 | cx.cache.impls.entry(did).or_default().push(impl_.clone()); | |
186 | } | |
3dfed10e XL |
187 | } |
188 | } | |
189 | } | |
190 | ||
6a06907d | 191 | krate |
3dfed10e XL |
192 | } |
193 | } | |
194 | ||
6a06907d | 195 | impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { |
3dfed10e | 196 | fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { |
04454e1e FG |
197 | if item.item_id.is_local() { |
198 | debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.item_id); | |
3dfed10e XL |
199 | } |
200 | ||
201 | // If this is a stripped module, | |
202 | // we don't want it or its children in the search index. | |
5869c6ff | 203 | let orig_stripped_mod = match *item.kind { |
3dfed10e | 204 | clean::StrippedItem(box clean::ModuleItem(..)) => { |
6a06907d | 205 | mem::replace(&mut self.cache.stripped_mod, true) |
3dfed10e | 206 | } |
6a06907d | 207 | _ => self.cache.stripped_mod, |
3dfed10e XL |
208 | }; |
209 | ||
210 | // If the impl is from a masked crate or references something from a | |
211 | // masked crate then remove it completely. | |
5869c6ff | 212 | if let clean::ImplItem(ref i) = *item.kind { |
04454e1e | 213 | if self.cache.masked_crates.contains(&item.item_id.krate()) |
c295e0f8 XL |
214 | || i.trait_ |
215 | .as_ref() | |
216 | .map_or(false, |t| self.cache.masked_crates.contains(&t.def_id().krate)) | |
3c0e092e XL |
217 | || i.for_ |
218 | .def_id(self.cache) | |
219 | .map_or(false, |d| self.cache.masked_crates.contains(&d.krate)) | |
3dfed10e XL |
220 | { |
221 | return None; | |
222 | } | |
223 | } | |
224 | ||
225 | // Propagate a trait method's documentation to all implementors of the | |
226 | // trait. | |
5869c6ff | 227 | if let clean::TraitItem(ref t) = *item.kind { |
2b03887a | 228 | self.cache.traits.entry(item.item_id.expect_def_id()).or_insert_with(|| (**t).clone()); |
3dfed10e XL |
229 | } |
230 | ||
231 | // Collect all the implementors of traits. | |
9ffffee4 FG |
232 | if let clean::ImplItem(ref i) = *item.kind && |
233 | let Some(trait_) = &i.trait_ && | |
234 | !i.kind.is_blanket() | |
235 | { | |
236 | self.cache | |
237 | .implementors | |
238 | .entry(trait_.def_id()) | |
239 | .or_default() | |
240 | .push(Impl { impl_item: item.clone() }); | |
3dfed10e XL |
241 | } |
242 | ||
243 | // Index this method for searching later on. | |
9c376795 | 244 | if let Some(s) = item.name.or_else(|| { |
04454e1e FG |
245 | if item.is_stripped() { |
246 | None | |
247 | } else if let clean::ImportItem(ref i) = *item.kind && | |
248 | let clean::ImportKind::Simple(s) = i.kind { | |
249 | Some(s) | |
250 | } else { | |
251 | None | |
252 | } | |
253 | }) { | |
5869c6ff | 254 | let (parent, is_inherent_impl_item) = match *item.kind { |
3dfed10e | 255 | clean::StrippedItem(..) => ((None, None), false), |
04454e1e | 256 | clean::AssocConstItem(..) | clean::AssocTypeItem(..) |
923072b8 FG |
257 | if self |
258 | .cache | |
259 | .parent_stack | |
260 | .last() | |
261 | .map_or(false, |parent| parent.is_trait_impl()) => | |
3dfed10e XL |
262 | { |
263 | // skip associated items in trait impls | |
264 | ((None, None), false) | |
265 | } | |
04454e1e FG |
266 | clean::TyMethodItem(..) |
267 | | clean::TyAssocConstItem(..) | |
268 | | clean::TyAssocTypeItem(..) | |
3dfed10e XL |
269 | | clean::StructFieldItem(..) |
270 | | clean::VariantItem(..) => ( | |
271 | ( | |
923072b8 FG |
272 | Some( |
273 | self.cache | |
274 | .parent_stack | |
275 | .last() | |
276 | .expect("parent_stack is empty") | |
277 | .item_id() | |
278 | .expect_def_id(), | |
279 | ), | |
6a06907d | 280 | Some(&self.cache.stack[..self.cache.stack.len() - 1]), |
3dfed10e XL |
281 | ), |
282 | false, | |
283 | ), | |
04454e1e | 284 | clean::MethodItem(..) | clean::AssocConstItem(..) | clean::AssocTypeItem(..) => { |
6a06907d | 285 | if self.cache.parent_stack.is_empty() { |
3dfed10e XL |
286 | ((None, None), false) |
287 | } else { | |
6a06907d | 288 | let last = self.cache.parent_stack.last().expect("parent_stack is empty 2"); |
923072b8 | 289 | let did = match &*last { |
9ffffee4 FG |
290 | ParentStackItem::Impl { |
291 | // impl Trait for &T { fn method(self); } | |
292 | // | |
293 | // When generating a function index with the above shape, we want it | |
294 | // associated with `T`, not with the primitive reference type. It should | |
295 | // show up as `T::method`, rather than `reference::method`, in the search | |
296 | // results page. | |
297 | for_: clean::Type::BorrowedRef { type_, .. }, | |
298 | .. | |
299 | } => type_.def_id(&self.cache), | |
923072b8 FG |
300 | ParentStackItem::Impl { for_, .. } => for_.def_id(&self.cache), |
301 | ParentStackItem::Type(item_id) => item_id.as_def_id(), | |
302 | }; | |
303 | let path = match did.and_then(|did| self.cache.paths.get(&did)) { | |
3dfed10e XL |
304 | // The current stack not necessarily has correlation |
305 | // for where the type was defined. On the other | |
306 | // hand, `paths` always has the right | |
307 | // information if present. | |
9c376795 | 308 | Some((fqp, _)) => Some(&fqp[..fqp.len() - 1]), |
3dfed10e XL |
309 | None => None, |
310 | }; | |
923072b8 | 311 | ((did, path), true) |
3dfed10e XL |
312 | } |
313 | } | |
6a06907d | 314 | _ => ((None, Some(&*self.cache.stack)), false), |
3dfed10e XL |
315 | }; |
316 | ||
317 | match parent { | |
6a06907d | 318 | (parent, Some(path)) if is_inherent_impl_item || !self.cache.stripped_mod => { |
3dfed10e XL |
319 | debug_assert!(!item.is_stripped()); |
320 | ||
321 | // A crate has a module at its root, containing all items, | |
322 | // which should not be indexed. The crate-item itself is | |
323 | // inserted later on when serializing the search-index. | |
04454e1e | 324 | if item.item_id.as_def_id().map_or(false, |idx| !idx.is_crate_root()) { |
136023e0 | 325 | let desc = item.doc_value().map_or_else(String::new, |x| { |
3c0e092e | 326 | short_markdown_summary(x.as_str(), &item.link_names(self.cache)) |
136023e0 | 327 | }); |
487cf647 | 328 | let ty = item.type_(); |
9c376795 FG |
329 | if ty != ItemType::StructField |
330 | || u16::from_str_radix(s.as_str(), 10).is_err() | |
331 | { | |
487cf647 FG |
332 | // In case this is a field from a tuple struct, we don't add it into |
333 | // the search index because its name is something like "0", which is | |
334 | // not useful for rustdoc search. | |
335 | self.cache.search_index.push(IndexItem { | |
336 | ty, | |
9c376795 | 337 | name: s, |
487cf647 FG |
338 | path: join_with_double_colon(path), |
339 | desc, | |
340 | parent, | |
341 | parent_idx: None, | |
342 | search_type: get_function_type_for_search( | |
343 | &item, | |
344 | self.tcx, | |
345 | clean_impl_generics(self.cache.parent_stack.last()).as_ref(), | |
346 | self.cache, | |
347 | ), | |
348 | aliases: item.attrs.get_doc_aliases(), | |
353b0b11 | 349 | deprecation: item.deprecation(self.tcx), |
487cf647 FG |
350 | }); |
351 | } | |
3dfed10e XL |
352 | } |
353 | } | |
354 | (Some(parent), None) if is_inherent_impl_item => { | |
355 | // We have a parent, but we don't know where they're | |
356 | // defined yet. Wait for later to index this item. | |
923072b8 FG |
357 | let impl_generics = clean_impl_generics(self.cache.parent_stack.last()); |
358 | self.cache.orphan_impl_items.push(OrphanImplItem { | |
359 | parent, | |
360 | item: item.clone(), | |
361 | impl_generics, | |
362 | }); | |
3dfed10e XL |
363 | } |
364 | _ => {} | |
365 | } | |
366 | } | |
367 | ||
368 | // Keep track of the fully qualified path for this item. | |
369 | let pushed = match item.name { | |
fc512014 | 370 | Some(n) if !n.is_empty() => { |
5099ac24 | 371 | self.cache.stack.push(n); |
3dfed10e XL |
372 | true |
373 | } | |
374 | _ => false, | |
375 | }; | |
376 | ||
5869c6ff | 377 | match *item.kind { |
3dfed10e XL |
378 | clean::StructItem(..) |
379 | | clean::EnumItem(..) | |
380 | | clean::TypedefItem(..) | |
381 | | clean::TraitItem(..) | |
17df50a5 | 382 | | clean::TraitAliasItem(..) |
3dfed10e XL |
383 | | clean::FunctionItem(..) |
384 | | clean::ModuleItem(..) | |
385 | | clean::ForeignFunctionItem(..) | |
386 | | clean::ForeignStaticItem(..) | |
387 | | clean::ConstantItem(..) | |
388 | | clean::StaticItem(..) | |
389 | | clean::UnionItem(..) | |
390 | | clean::ForeignTypeItem | |
391 | | clean::MacroItem(..) | |
392 | | clean::ProcMacroItem(..) | |
17df50a5 XL |
393 | | clean::VariantItem(..) => { |
394 | if !self.cache.stripped_mod { | |
395 | // Re-exported items mean that the same id can show up twice | |
396 | // in the rustdoc ast that we're looking at. We know, | |
397 | // however, that a re-exported item doesn't show up in the | |
398 | // `public_items` map, so we can skip inserting into the | |
399 | // paths map if there was already an entry present and we're | |
400 | // not a public item. | |
04454e1e | 401 | if !self.cache.paths.contains_key(&item.item_id.expect_def_id()) |
2b03887a FG |
402 | || self |
403 | .cache | |
404 | .effective_visibilities | |
487cf647 | 405 | .is_directly_public(self.tcx, item.item_id.expect_def_id()) |
17df50a5 XL |
406 | { |
407 | self.cache.paths.insert( | |
04454e1e | 408 | item.item_id.expect_def_id(), |
17df50a5 XL |
409 | (self.cache.stack.clone(), item.type_()), |
410 | ); | |
411 | } | |
3dfed10e XL |
412 | } |
413 | } | |
414 | clean::PrimitiveItem(..) => { | |
17df50a5 XL |
415 | self.cache |
416 | .paths | |
04454e1e | 417 | .insert(item.item_id.expect_def_id(), (self.cache.stack.clone(), item.type_())); |
3dfed10e XL |
418 | } |
419 | ||
17df50a5 XL |
420 | clean::ExternCrateItem { .. } |
421 | | clean::ImportItem(..) | |
422 | | clean::OpaqueTyItem(..) | |
423 | | clean::ImplItem(..) | |
424 | | clean::TyMethodItem(..) | |
425 | | clean::MethodItem(..) | |
426 | | clean::StructFieldItem(..) | |
04454e1e | 427 | | clean::TyAssocConstItem(..) |
17df50a5 | 428 | | clean::AssocConstItem(..) |
04454e1e | 429 | | clean::TyAssocTypeItem(..) |
17df50a5 XL |
430 | | clean::AssocTypeItem(..) |
431 | | clean::StrippedItem(..) | |
064997fb | 432 | | clean::KeywordItem => { |
17df50a5 XL |
433 | // FIXME: Do these need handling? |
434 | // The person writing this comment doesn't know. | |
435 | // So would rather leave them to an expert, | |
436 | // as at least the list is better than `_ => {}`. | |
437 | } | |
3dfed10e XL |
438 | } |
439 | ||
923072b8 FG |
440 | // Maintain the parent stack. |
441 | let (item, parent_pushed) = match *item.kind { | |
3dfed10e XL |
442 | clean::TraitItem(..) |
443 | | clean::EnumItem(..) | |
444 | | clean::ForeignTypeItem | |
445 | | clean::StructItem(..) | |
446 | | clean::UnionItem(..) | |
923072b8 FG |
447 | | clean::VariantItem(..) |
448 | | clean::ImplItem(..) => { | |
449 | self.cache.parent_stack.push(ParentStackItem::new(&item)); | |
450 | (self.fold_item_recur(item), true) | |
3dfed10e | 451 | } |
923072b8 | 452 | _ => (self.fold_item_recur(item), false), |
3dfed10e XL |
453 | }; |
454 | ||
455 | // Once we've recursively found all the generics, hoard off all the | |
456 | // implementations elsewhere. | |
5869c6ff | 457 | let ret = if let clean::Item { kind: box clean::ImplItem(ref i), .. } = item { |
fc512014 XL |
458 | // Figure out the id of this impl. This may map to a |
459 | // primitive rather than always to a struct/enum. | |
460 | // Note: matching twice to restrict the lifetime of the `i` borrow. | |
461 | let mut dids = FxHashSet::default(); | |
5869c6ff | 462 | match i.for_ { |
3c0e092e XL |
463 | clean::Type::Path { ref path } |
464 | | clean::BorrowedRef { type_: box clean::Type::Path { ref path }, .. } => { | |
465 | dids.insert(path.def_id()); | |
923072b8 | 466 | if let Some(generics) = path.generics() && |
9ffffee4 | 467 | let ty::Adt(adt, _) = self.tcx.type_of(path.def_id()).subst_identity().kind() && |
923072b8 FG |
468 | adt.is_fundamental() { |
469 | for ty in generics { | |
470 | if let Some(did) = ty.def_id(self.cache) { | |
471 | dids.insert(did); | |
472 | } | |
473 | } | |
474 | } | |
5869c6ff | 475 | } |
136023e0 XL |
476 | clean::DynTrait(ref bounds, _) |
477 | | clean::BorrowedRef { type_: box clean::DynTrait(ref bounds, _), .. } => { | |
c295e0f8 | 478 | dids.insert(bounds[0].trait_.def_id()); |
136023e0 | 479 | } |
5869c6ff | 480 | ref t => { |
6a06907d XL |
481 | let did = t |
482 | .primitive_type() | |
483 | .and_then(|t| self.cache.primitive_locations.get(&t).cloned()); | |
3dfed10e | 484 | |
5869c6ff XL |
485 | if let Some(did) = did { |
486 | dids.insert(did); | |
3dfed10e | 487 | } |
fc512014 | 488 | } |
5869c6ff | 489 | } |
3dfed10e | 490 | |
5869c6ff XL |
491 | if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { |
492 | for bound in generics { | |
3c0e092e | 493 | if let Some(did) = bound.def_id(self.cache) { |
5869c6ff | 494 | dids.insert(did); |
3dfed10e | 495 | } |
3dfed10e | 496 | } |
5869c6ff | 497 | } |
fc512014 | 498 | let impl_item = Impl { impl_item: item }; |
6a06907d | 499 | if impl_item.trait_did().map_or(true, |d| self.cache.traits.contains_key(&d)) { |
fc512014 | 500 | for did in dids { |
04454e1e FG |
501 | if self.impl_ids.entry(did).or_default().insert(impl_item.def_id()) { |
502 | self.cache | |
503 | .impls | |
504 | .entry(did) | |
505 | .or_insert_with(Vec::new) | |
506 | .push(impl_item.clone()); | |
507 | } | |
fc512014 XL |
508 | } |
509 | } else { | |
510 | let trait_did = impl_item.trait_did().expect("no trait did"); | |
6a06907d | 511 | self.cache.orphan_trait_impls.push((trait_did, dids, impl_item)); |
3dfed10e | 512 | } |
fc512014 XL |
513 | None |
514 | } else { | |
515 | Some(item) | |
516 | }; | |
3dfed10e XL |
517 | |
518 | if pushed { | |
6a06907d | 519 | self.cache.stack.pop().expect("stack already empty"); |
3dfed10e XL |
520 | } |
521 | if parent_pushed { | |
6a06907d | 522 | self.cache.parent_stack.pop().expect("parent stack already empty"); |
3dfed10e | 523 | } |
6a06907d | 524 | self.cache.stripped_mod = orig_stripped_mod; |
3dfed10e XL |
525 | ret |
526 | } | |
527 | } | |
923072b8 FG |
528 | |
529 | pub(crate) struct OrphanImplItem { | |
530 | pub(crate) parent: DefId, | |
531 | pub(crate) item: clean::Item, | |
532 | pub(crate) impl_generics: Option<(clean::Type, clean::Generics)>, | |
533 | } | |
534 | ||
535 | /// Information about trait and type parents is tracked while traversing the item tree to build | |
536 | /// the cache. | |
537 | /// | |
538 | /// We don't just store `Item` in there, because `Item` contains the list of children being | |
539 | /// traversed and it would be wasteful to clone all that. We also need the item id, so just | |
540 | /// storing `ItemKind` won't work, either. | |
541 | enum ParentStackItem { | |
542 | Impl { | |
543 | for_: clean::Type, | |
544 | trait_: Option<clean::Path>, | |
545 | generics: clean::Generics, | |
546 | kind: clean::ImplKind, | |
547 | item_id: ItemId, | |
548 | }, | |
549 | Type(ItemId), | |
550 | } | |
551 | ||
552 | impl ParentStackItem { | |
553 | fn new(item: &clean::Item) -> Self { | |
554 | match &*item.kind { | |
064997fb | 555 | clean::ItemKind::ImplItem(box clean::Impl { for_, trait_, generics, kind, .. }) => { |
923072b8 FG |
556 | ParentStackItem::Impl { |
557 | for_: for_.clone(), | |
558 | trait_: trait_.clone(), | |
559 | generics: generics.clone(), | |
560 | kind: kind.clone(), | |
561 | item_id: item.item_id, | |
562 | } | |
563 | } | |
564 | _ => ParentStackItem::Type(item.item_id), | |
565 | } | |
566 | } | |
567 | fn is_trait_impl(&self) -> bool { | |
568 | matches!(self, ParentStackItem::Impl { trait_: Some(..), .. }) | |
569 | } | |
570 | fn item_id(&self) -> ItemId { | |
571 | match self { | |
572 | ParentStackItem::Impl { item_id, .. } => *item_id, | |
573 | ParentStackItem::Type(item_id) => *item_id, | |
574 | } | |
575 | } | |
576 | } | |
577 | ||
578 | fn clean_impl_generics(item: Option<&ParentStackItem>) -> Option<(clean::Type, clean::Generics)> { | |
579 | if let Some(ParentStackItem::Impl { for_, generics, kind: clean::ImplKind::Normal, .. }) = item | |
580 | { | |
581 | Some((for_.clone(), generics.clone())) | |
582 | } else { | |
583 | None | |
584 | } | |
585 | } |