]>
Commit | Line | Data |
---|---|---|
3dfed10e XL |
1 | use std::collections::BTreeMap; |
2 | use std::mem; | |
3 | use std::path::{Path, PathBuf}; | |
3dfed10e XL |
4 | |
5 | use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | |
6 | use rustc_hir::def_id::{CrateNum, DefId, CRATE_DEF_INDEX}; | |
7 | use rustc_middle::middle::privacy::AccessLevels; | |
6a06907d | 8 | use rustc_middle::ty::TyCtxt; |
3dfed10e | 9 | use rustc_span::source_map::FileName; |
6a06907d | 10 | use rustc_span::symbol::sym; |
fc512014 | 11 | use rustc_span::Symbol; |
3dfed10e XL |
12 | |
13 | use crate::clean::{self, GetDefId}; | |
3dfed10e XL |
14 | use crate::fold::DocFolder; |
15 | use crate::formats::item_type::ItemType; | |
16 | use crate::formats::Impl; | |
fc512014 | 17 | use crate::html::markdown::short_markdown_summary; |
3dfed10e XL |
18 | use crate::html::render::cache::{extern_location, get_index_search_type, ExternalLocation}; |
19 | use crate::html::render::IndexItem; | |
3dfed10e | 20 | |
fc512014 | 21 | /// This cache is used to store information about the [`clean::Crate`] being |
3dfed10e XL |
22 | /// rendered in order to provide more useful documentation. This contains |
23 | /// information like all implementors of a trait, all traits a type implements, | |
24 | /// documentation for all known traits, etc. | |
25 | /// | |
26 | /// This structure purposefully does not implement `Clone` because it's intended | |
27 | /// to be a fairly large and expensive structure to clone. Instead this adheres | |
28 | /// to `Send` so it may be stored in a `Arc` instance and shared among the various | |
29 | /// rendering threads. | |
30 | #[derive(Default)] | |
fc512014 | 31 | crate struct Cache { |
3dfed10e XL |
32 | /// Maps a type ID to all known implementations for that type. This is only |
33 | /// recognized for intra-crate `ResolvedPath` types, and is used to print | |
34 | /// out extra documentation on the page of an enum/struct. | |
35 | /// | |
36 | /// The values of the map are a list of implementations and documentation | |
37 | /// found on that implementation. | |
fc512014 | 38 | crate impls: FxHashMap<DefId, Vec<Impl>>, |
3dfed10e XL |
39 | |
40 | /// Maintains a mapping of local crate `DefId`s to the fully qualified name | |
41 | /// and "short type description" of that node. This is used when generating | |
42 | /// URLs when a type is being linked to. External paths are not located in | |
43 | /// this map because the `External` type itself has all the information | |
44 | /// necessary. | |
fc512014 | 45 | crate paths: FxHashMap<DefId, (Vec<String>, ItemType)>, |
3dfed10e XL |
46 | |
47 | /// Similar to `paths`, but only holds external paths. This is only used for | |
48 | /// generating explicit hyperlinks to other crates. | |
fc512014 | 49 | crate external_paths: FxHashMap<DefId, (Vec<String>, ItemType)>, |
3dfed10e XL |
50 | |
51 | /// Maps local `DefId`s of exported types to fully qualified paths. | |
52 | /// Unlike 'paths', this mapping ignores any renames that occur | |
53 | /// due to 'use' statements. | |
54 | /// | |
55 | /// This map is used when writing out the special 'implementors' | |
56 | /// javascript file. By using the exact path that the type | |
57 | /// is declared with, we ensure that each path will be identical | |
58 | /// to the path used if the corresponding type is inlined. By | |
59 | /// doing this, we can detect duplicate impls on a trait page, and only display | |
60 | /// the impl for the inlined type. | |
fc512014 | 61 | crate exact_paths: FxHashMap<DefId, Vec<String>>, |
3dfed10e XL |
62 | |
63 | /// This map contains information about all known traits of this crate. | |
64 | /// Implementations of a crate should inherit the documentation of the | |
65 | /// parent trait if no extra documentation is specified, and default methods | |
66 | /// should show up in documentation about trait implementations. | |
6a06907d | 67 | crate traits: FxHashMap<DefId, clean::TraitWithExtraInfo>, |
3dfed10e XL |
68 | |
69 | /// When rendering traits, it's often useful to be able to list all | |
70 | /// implementors of the trait, and this mapping is exactly, that: a mapping | |
71 | /// of trait ids to the list of known implementors of the trait | |
fc512014 | 72 | crate implementors: FxHashMap<DefId, Vec<Impl>>, |
3dfed10e XL |
73 | |
74 | /// Cache of where external crate documentation can be found. | |
fc512014 | 75 | crate extern_locations: FxHashMap<CrateNum, (Symbol, PathBuf, ExternalLocation)>, |
3dfed10e XL |
76 | |
77 | /// Cache of where documentation for primitives can be found. | |
fc512014 | 78 | crate primitive_locations: FxHashMap<clean::PrimitiveType, DefId>, |
3dfed10e XL |
79 | |
80 | // Note that external items for which `doc(hidden)` applies to are shown as | |
81 | // non-reachable while local items aren't. This is because we're reusing | |
82 | // the access levels from the privacy check pass. | |
fc512014 | 83 | crate access_levels: AccessLevels<DefId>, |
3dfed10e XL |
84 | |
85 | /// The version of the crate being documented, if given from the `--crate-version` flag. | |
fc512014 | 86 | crate crate_version: Option<String>, |
3dfed10e XL |
87 | |
88 | /// Whether to document private items. | |
89 | /// This is stored in `Cache` so it doesn't need to be passed through all rustdoc functions. | |
fc512014 | 90 | crate document_private: bool, |
3dfed10e | 91 | |
6a06907d XL |
92 | /// Crates marked with [`#[doc(masked)]`][doc_masked]. |
93 | /// | |
94 | /// [doc_masked]: https://doc.rust-lang.org/nightly/unstable-book/language-features/doc-masked.html | |
95 | crate masked_crates: FxHashSet<CrateNum>, | |
96 | ||
3dfed10e XL |
97 | // Private fields only used when initially crawling a crate to build a cache |
98 | stack: Vec<String>, | |
99 | parent_stack: Vec<DefId>, | |
100 | parent_is_trait_impl: bool, | |
101 | stripped_mod: bool, | |
3dfed10e | 102 | |
fc512014 XL |
103 | crate search_index: Vec<IndexItem>, |
104 | crate deref_trait_did: Option<DefId>, | |
105 | crate deref_mut_trait_did: Option<DefId>, | |
106 | crate owned_box_did: Option<DefId>, | |
3dfed10e XL |
107 | |
108 | // In rare case where a structure is defined in one module but implemented | |
109 | // in another, if the implementing module is parsed before defining module, | |
110 | // then the fully qualified name of the structure isn't presented in `paths` | |
111 | // yet when its implementation methods are being indexed. Caches such methods | |
112 | // and their parent id here and indexes them at the end of crate parsing. | |
fc512014 | 113 | crate orphan_impl_items: Vec<(DefId, clean::Item)>, |
3dfed10e XL |
114 | |
115 | // Similarly to `orphan_impl_items`, sometimes trait impls are picked up | |
116 | // even though the trait itself is not exported. This can happen if a trait | |
117 | // was defined in function/expression scope, since the impl will be picked | |
118 | // up by `collect-trait-impls` but the trait won't be scraped out in the HIR | |
119 | // crawl. In order to prevent crashes when looking for spotlight traits or | |
120 | // when gathering trait documentation on a type, hold impls here while | |
121 | // folding and add them to the cache later on if we find the trait. | |
122 | orphan_trait_impls: Vec<(DefId, FxHashSet<DefId>, Impl)>, | |
123 | ||
124 | /// Aliases added through `#[doc(alias = "...")]`. Since a few items can have the same alias, | |
125 | /// we need the alias element to have an array of items. | |
fc512014 | 126 | crate aliases: BTreeMap<String, Vec<usize>>, |
3dfed10e XL |
127 | } |
128 | ||
6a06907d XL |
129 | /// This struct is used to wrap the `cache` and `tcx` in order to run `DocFolder`. |
130 | struct CacheBuilder<'a, 'tcx> { | |
131 | cache: &'a mut Cache, | |
132 | empty_cache: Cache, | |
133 | tcx: TyCtxt<'tcx>, | |
134 | } | |
135 | ||
3dfed10e | 136 | impl Cache { |
6a06907d XL |
137 | crate fn new(access_levels: AccessLevels<DefId>, document_private: bool) -> Self { |
138 | Cache { access_levels, document_private, ..Cache::default() } | |
139 | } | |
140 | ||
141 | /// Populates the `Cache` with more data. The returned `Crate` will be missing some data that was | |
142 | /// in `krate` due to the data being moved into the `Cache`. | |
143 | crate fn populate( | |
144 | &mut self, | |
145 | mut krate: clean::Crate, | |
146 | tcx: TyCtxt<'_>, | |
3dfed10e XL |
147 | extern_html_root_urls: &BTreeMap<String, String>, |
148 | dst: &Path, | |
6a06907d | 149 | ) -> clean::Crate { |
3dfed10e | 150 | // Crawl the crate to build various caches used for the output |
6a06907d XL |
151 | debug!(?self.crate_version); |
152 | self.traits = krate.external_traits.take(); | |
3dfed10e XL |
153 | |
154 | // Cache where all our extern crates are located | |
155 | // FIXME: this part is specific to HTML so it'd be nice to remove it from the common code | |
156 | for &(n, ref e) in &krate.externs { | |
157 | let src_root = match e.src { | |
158 | FileName::Real(ref p) => match p.local_path().parent() { | |
159 | Some(p) => p.to_path_buf(), | |
160 | None => PathBuf::new(), | |
161 | }, | |
162 | _ => PathBuf::new(), | |
163 | }; | |
fc512014 | 164 | let extern_url = extern_html_root_urls.get(&*e.name.as_str()).map(|u| &**u); |
6a06907d | 165 | self.extern_locations |
fc512014 | 166 | .insert(n, (e.name, src_root, extern_location(e, extern_url, &dst))); |
3dfed10e XL |
167 | |
168 | let did = DefId { krate: n, index: CRATE_DEF_INDEX }; | |
6a06907d | 169 | self.external_paths.insert(did, (vec![e.name.to_string()], ItemType::Module)); |
3dfed10e XL |
170 | } |
171 | ||
172 | // Cache where all known primitives have their documentation located. | |
173 | // | |
174 | // Favor linking to as local extern as possible, so iterate all crates in | |
175 | // reverse topological order. | |
176 | for &(_, ref e) in krate.externs.iter().rev() { | |
fc512014 | 177 | for &(def_id, prim) in &e.primitives { |
6a06907d | 178 | self.primitive_locations.insert(prim, def_id); |
3dfed10e XL |
179 | } |
180 | } | |
fc512014 | 181 | for &(def_id, prim) in &krate.primitives { |
6a06907d | 182 | self.primitive_locations.insert(prim, def_id); |
3dfed10e XL |
183 | } |
184 | ||
6a06907d | 185 | self.stack.push(krate.name.to_string()); |
5869c6ff | 186 | |
6a06907d | 187 | krate = CacheBuilder { tcx, cache: self, empty_cache: Cache::default() }.fold_crate(krate); |
3dfed10e | 188 | |
6a06907d XL |
189 | for (trait_did, dids, impl_) in self.orphan_trait_impls.drain(..) { |
190 | if self.traits.contains_key(&trait_did) { | |
3dfed10e | 191 | for did in dids { |
6a06907d | 192 | self.impls.entry(did).or_default().push(impl_.clone()); |
3dfed10e XL |
193 | } |
194 | } | |
195 | } | |
196 | ||
6a06907d | 197 | krate |
3dfed10e XL |
198 | } |
199 | } | |
200 | ||
6a06907d | 201 | impl<'a, 'tcx> DocFolder for CacheBuilder<'a, 'tcx> { |
3dfed10e XL |
202 | fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { |
203 | if item.def_id.is_local() { | |
204 | debug!("folding {} \"{:?}\", id {:?}", item.type_(), item.name, item.def_id); | |
205 | } | |
206 | ||
207 | // If this is a stripped module, | |
208 | // we don't want it or its children in the search index. | |
5869c6ff | 209 | let orig_stripped_mod = match *item.kind { |
3dfed10e | 210 | clean::StrippedItem(box clean::ModuleItem(..)) => { |
6a06907d | 211 | mem::replace(&mut self.cache.stripped_mod, true) |
3dfed10e | 212 | } |
6a06907d | 213 | _ => self.cache.stripped_mod, |
3dfed10e XL |
214 | }; |
215 | ||
216 | // If the impl is from a masked crate or references something from a | |
217 | // masked crate then remove it completely. | |
5869c6ff | 218 | if let clean::ImplItem(ref i) = *item.kind { |
6a06907d XL |
219 | if self.cache.masked_crates.contains(&item.def_id.krate) |
220 | || i.trait_.def_id().map_or(false, |d| self.cache.masked_crates.contains(&d.krate)) | |
221 | || i.for_.def_id().map_or(false, |d| self.cache.masked_crates.contains(&d.krate)) | |
3dfed10e XL |
222 | { |
223 | return None; | |
224 | } | |
225 | } | |
226 | ||
227 | // Propagate a trait method's documentation to all implementors of the | |
228 | // trait. | |
5869c6ff | 229 | if let clean::TraitItem(ref t) = *item.kind { |
6a06907d XL |
230 | self.cache.traits.entry(item.def_id).or_insert_with(|| clean::TraitWithExtraInfo { |
231 | trait_: t.clone(), | |
232 | is_spotlight: item.attrs.has_doc_flag(sym::spotlight), | |
233 | }); | |
3dfed10e XL |
234 | } |
235 | ||
236 | // Collect all the implementors of traits. | |
5869c6ff | 237 | if let clean::ImplItem(ref i) = *item.kind { |
3dfed10e XL |
238 | if let Some(did) = i.trait_.def_id() { |
239 | if i.blanket_impl.is_none() { | |
6a06907d XL |
240 | self.cache |
241 | .implementors | |
3dfed10e XL |
242 | .entry(did) |
243 | .or_default() | |
244 | .push(Impl { impl_item: item.clone() }); | |
245 | } | |
246 | } | |
247 | } | |
248 | ||
249 | // Index this method for searching later on. | |
250 | if let Some(ref s) = item.name { | |
5869c6ff | 251 | let (parent, is_inherent_impl_item) = match *item.kind { |
3dfed10e XL |
252 | clean::StrippedItem(..) => ((None, None), false), |
253 | clean::AssocConstItem(..) | clean::TypedefItem(_, true) | |
6a06907d | 254 | if self.cache.parent_is_trait_impl => |
3dfed10e XL |
255 | { |
256 | // skip associated items in trait impls | |
257 | ((None, None), false) | |
258 | } | |
259 | clean::AssocTypeItem(..) | |
260 | | clean::TyMethodItem(..) | |
261 | | clean::StructFieldItem(..) | |
262 | | clean::VariantItem(..) => ( | |
263 | ( | |
6a06907d XL |
264 | Some(*self.cache.parent_stack.last().expect("parent_stack is empty")), |
265 | Some(&self.cache.stack[..self.cache.stack.len() - 1]), | |
3dfed10e XL |
266 | ), |
267 | false, | |
268 | ), | |
269 | clean::MethodItem(..) | clean::AssocConstItem(..) => { | |
6a06907d | 270 | if self.cache.parent_stack.is_empty() { |
3dfed10e XL |
271 | ((None, None), false) |
272 | } else { | |
6a06907d | 273 | let last = self.cache.parent_stack.last().expect("parent_stack is empty 2"); |
3dfed10e | 274 | let did = *last; |
6a06907d | 275 | let path = match self.cache.paths.get(&did) { |
3dfed10e XL |
276 | // The current stack not necessarily has correlation |
277 | // for where the type was defined. On the other | |
278 | // hand, `paths` always has the right | |
279 | // information if present. | |
280 | Some(&( | |
281 | ref fqp, | |
282 | ItemType::Trait | |
283 | | ItemType::Struct | |
284 | | ItemType::Union | |
285 | | ItemType::Enum, | |
286 | )) => Some(&fqp[..fqp.len() - 1]), | |
6a06907d | 287 | Some(..) => Some(&*self.cache.stack), |
3dfed10e XL |
288 | None => None, |
289 | }; | |
290 | ((Some(*last), path), true) | |
291 | } | |
292 | } | |
6a06907d | 293 | _ => ((None, Some(&*self.cache.stack)), false), |
3dfed10e XL |
294 | }; |
295 | ||
296 | match parent { | |
6a06907d | 297 | (parent, Some(path)) if is_inherent_impl_item || !self.cache.stripped_mod => { |
3dfed10e XL |
298 | debug_assert!(!item.is_stripped()); |
299 | ||
300 | // A crate has a module at its root, containing all items, | |
301 | // which should not be indexed. The crate-item itself is | |
302 | // inserted later on when serializing the search-index. | |
303 | if item.def_id.index != CRATE_DEF_INDEX { | |
6a06907d | 304 | self.cache.search_index.push(IndexItem { |
3dfed10e XL |
305 | ty: item.type_(), |
306 | name: s.to_string(), | |
307 | path: path.join("::"), | |
fc512014 XL |
308 | desc: item |
309 | .doc_value() | |
5869c6ff | 310 | .map_or_else(String::new, |x| short_markdown_summary(&x.as_str())), |
3dfed10e XL |
311 | parent, |
312 | parent_idx: None, | |
6a06907d | 313 | search_type: get_index_search_type(&item, &self.empty_cache, self.tcx), |
3dfed10e XL |
314 | }); |
315 | ||
316 | for alias in item.attrs.get_doc_aliases() { | |
6a06907d XL |
317 | self.cache |
318 | .aliases | |
3dfed10e XL |
319 | .entry(alias.to_lowercase()) |
320 | .or_insert(Vec::new()) | |
6a06907d | 321 | .push(self.cache.search_index.len() - 1); |
3dfed10e XL |
322 | } |
323 | } | |
324 | } | |
325 | (Some(parent), None) if is_inherent_impl_item => { | |
326 | // We have a parent, but we don't know where they're | |
327 | // defined yet. Wait for later to index this item. | |
6a06907d | 328 | self.cache.orphan_impl_items.push((parent, item.clone())); |
3dfed10e XL |
329 | } |
330 | _ => {} | |
331 | } | |
332 | } | |
333 | ||
334 | // Keep track of the fully qualified path for this item. | |
335 | let pushed = match item.name { | |
fc512014 | 336 | Some(n) if !n.is_empty() => { |
6a06907d | 337 | self.cache.stack.push(n.to_string()); |
3dfed10e XL |
338 | true |
339 | } | |
340 | _ => false, | |
341 | }; | |
342 | ||
5869c6ff | 343 | match *item.kind { |
3dfed10e XL |
344 | clean::StructItem(..) |
345 | | clean::EnumItem(..) | |
346 | | clean::TypedefItem(..) | |
347 | | clean::TraitItem(..) | |
348 | | clean::FunctionItem(..) | |
349 | | clean::ModuleItem(..) | |
350 | | clean::ForeignFunctionItem(..) | |
351 | | clean::ForeignStaticItem(..) | |
352 | | clean::ConstantItem(..) | |
353 | | clean::StaticItem(..) | |
354 | | clean::UnionItem(..) | |
355 | | clean::ForeignTypeItem | |
356 | | clean::MacroItem(..) | |
357 | | clean::ProcMacroItem(..) | |
358 | | clean::VariantItem(..) | |
6a06907d | 359 | if !self.cache.stripped_mod => |
3dfed10e XL |
360 | { |
361 | // Re-exported items mean that the same id can show up twice | |
362 | // in the rustdoc ast that we're looking at. We know, | |
363 | // however, that a re-exported item doesn't show up in the | |
364 | // `public_items` map, so we can skip inserting into the | |
365 | // paths map if there was already an entry present and we're | |
366 | // not a public item. | |
6a06907d XL |
367 | if !self.cache.paths.contains_key(&item.def_id) |
368 | || self.cache.access_levels.is_public(item.def_id) | |
3dfed10e | 369 | { |
6a06907d | 370 | self.cache.paths.insert(item.def_id, (self.cache.stack.clone(), item.type_())); |
3dfed10e XL |
371 | } |
372 | } | |
373 | clean::PrimitiveItem(..) => { | |
6a06907d | 374 | self.cache.paths.insert(item.def_id, (self.cache.stack.clone(), item.type_())); |
3dfed10e XL |
375 | } |
376 | ||
377 | _ => {} | |
378 | } | |
379 | ||
380 | // Maintain the parent stack | |
6a06907d | 381 | let orig_parent_is_trait_impl = self.cache.parent_is_trait_impl; |
5869c6ff | 382 | let parent_pushed = match *item.kind { |
3dfed10e XL |
383 | clean::TraitItem(..) |
384 | | clean::EnumItem(..) | |
385 | | clean::ForeignTypeItem | |
386 | | clean::StructItem(..) | |
387 | | clean::UnionItem(..) | |
388 | | clean::VariantItem(..) => { | |
6a06907d XL |
389 | self.cache.parent_stack.push(item.def_id); |
390 | self.cache.parent_is_trait_impl = false; | |
3dfed10e XL |
391 | true |
392 | } | |
393 | clean::ImplItem(ref i) => { | |
6a06907d | 394 | self.cache.parent_is_trait_impl = i.trait_.is_some(); |
3dfed10e XL |
395 | match i.for_ { |
396 | clean::ResolvedPath { did, .. } => { | |
6a06907d | 397 | self.cache.parent_stack.push(did); |
3dfed10e XL |
398 | true |
399 | } | |
400 | ref t => { | |
401 | let prim_did = t | |
402 | .primitive_type() | |
6a06907d | 403 | .and_then(|t| self.cache.primitive_locations.get(&t).cloned()); |
3dfed10e XL |
404 | match prim_did { |
405 | Some(did) => { | |
6a06907d | 406 | self.cache.parent_stack.push(did); |
3dfed10e XL |
407 | true |
408 | } | |
409 | None => false, | |
410 | } | |
411 | } | |
412 | } | |
413 | } | |
414 | _ => false, | |
415 | }; | |
416 | ||
417 | // Once we've recursively found all the generics, hoard off all the | |
418 | // implementations elsewhere. | |
fc512014 | 419 | let item = self.fold_item_recur(item); |
5869c6ff | 420 | let ret = if let clean::Item { kind: box clean::ImplItem(ref i), .. } = item { |
fc512014 XL |
421 | // Figure out the id of this impl. This may map to a |
422 | // primitive rather than always to a struct/enum. | |
423 | // Note: matching twice to restrict the lifetime of the `i` borrow. | |
424 | let mut dids = FxHashSet::default(); | |
5869c6ff XL |
425 | match i.for_ { |
426 | clean::ResolvedPath { did, .. } | |
427 | | clean::BorrowedRef { type_: box clean::ResolvedPath { did, .. }, .. } => { | |
428 | dids.insert(did); | |
429 | } | |
430 | ref t => { | |
6a06907d XL |
431 | let did = t |
432 | .primitive_type() | |
433 | .and_then(|t| self.cache.primitive_locations.get(&t).cloned()); | |
3dfed10e | 434 | |
5869c6ff XL |
435 | if let Some(did) = did { |
436 | dids.insert(did); | |
3dfed10e | 437 | } |
fc512014 | 438 | } |
5869c6ff | 439 | } |
3dfed10e | 440 | |
5869c6ff XL |
441 | if let Some(generics) = i.trait_.as_ref().and_then(|t| t.generics()) { |
442 | for bound in generics { | |
443 | if let Some(did) = bound.def_id() { | |
444 | dids.insert(did); | |
3dfed10e | 445 | } |
3dfed10e | 446 | } |
5869c6ff | 447 | } |
fc512014 | 448 | let impl_item = Impl { impl_item: item }; |
6a06907d | 449 | if impl_item.trait_did().map_or(true, |d| self.cache.traits.contains_key(&d)) { |
fc512014 | 450 | for did in dids { |
6a06907d | 451 | self.cache.impls.entry(did).or_insert(vec![]).push(impl_item.clone()); |
fc512014 XL |
452 | } |
453 | } else { | |
454 | let trait_did = impl_item.trait_did().expect("no trait did"); | |
6a06907d | 455 | self.cache.orphan_trait_impls.push((trait_did, dids, impl_item)); |
3dfed10e | 456 | } |
fc512014 XL |
457 | None |
458 | } else { | |
459 | Some(item) | |
460 | }; | |
3dfed10e XL |
461 | |
462 | if pushed { | |
6a06907d | 463 | self.cache.stack.pop().expect("stack already empty"); |
3dfed10e XL |
464 | } |
465 | if parent_pushed { | |
6a06907d | 466 | self.cache.parent_stack.pop().expect("parent stack already empty"); |
3dfed10e | 467 | } |
6a06907d XL |
468 | self.cache.stripped_mod = orig_stripped_mod; |
469 | self.cache.parent_is_trait_impl = orig_parent_is_trait_impl; | |
3dfed10e XL |
470 | ret |
471 | } | |
472 | } |