]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/clean/inline.rs
Removed some unnecessary RefCells from resolve
[rust.git] / src / librustdoc / clean / inline.rs
1 // Copyright 2012-2013 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! Support for inlining external documentation into the current AST.
12
13 use syntax::ast;
14 use syntax::ast_util;
15 use syntax::attr::AttrMetaMethods;
16
17 use rustc::metadata::csearch;
18 use rustc::metadata::decoder;
19 use rustc::middle::def;
20 use rustc::middle::ty;
21 use rustc::middle::subst;
22 use rustc::middle::stability;
23
24 use core::DocContext;
25 use doctree;
26 use clean;
27
28 use super::Clean;
29
30 /// Attempt to inline the definition of a local node id into this AST.
31 ///
32 /// This function will fetch the definition of the id specified, and if it is
33 /// from another crate it will attempt to inline the documentation from the
34 /// other crate into this crate.
35 ///
36 /// This is primarily used for `pub use` statements which are, in general,
37 /// implementation details. Inlining the documentation should help provide a
38 /// better experience when reading the documentation in this use case.
39 ///
40 /// The returned value is `None` if the `id` could not be inlined, and `Some`
41 /// of a vector of items if it was successfully expanded.
42 pub fn try_inline(cx: &DocContext, id: ast::NodeId, into: Option<ast::Ident>)
43                   -> Option<Vec<clean::Item>> {
44     let tcx = match cx.tcx_opt() {
45         Some(tcx) => tcx,
46         None => return None,
47     };
48     let def = match tcx.def_map.borrow().find(&id) {
49         Some(def) => *def,
50         None => return None,
51     };
52     let did = def.def_id();
53     if ast_util::is_local(did) { return None }
54     try_inline_def(cx, tcx, def).map(|vec| {
55         vec.into_iter().map(|mut item| {
56             match into {
57                 Some(into) if item.name.is_some() => {
58                     item.name = Some(into.clean(cx));
59                 }
60                 _ => {}
61             }
62             item
63         }).collect()
64     })
65 }
66
67 fn try_inline_def(cx: &DocContext, tcx: &ty::ctxt,
68                   def: def::Def) -> Option<Vec<clean::Item>> {
69     let mut ret = Vec::new();
70     let did = def.def_id();
71     let inner = match def {
72         def::DefTrait(did) => {
73             record_extern_fqn(cx, did, clean::TypeTrait);
74             clean::TraitItem(build_external_trait(cx, tcx, did))
75         }
76         def::DefFn(did, style) => {
77             // If this function is a tuple struct constructor, we just skip it
78             if csearch::get_tuple_struct_definition_if_ctor(&tcx.sess.cstore,
79                                                             did).is_some() {
80                 return None
81             }
82             record_extern_fqn(cx, did, clean::TypeFunction);
83             clean::FunctionItem(build_external_function(cx, tcx, did, style))
84         }
85         def::DefStruct(did) => {
86             record_extern_fqn(cx, did, clean::TypeStruct);
87             ret.extend(build_impls(cx, tcx, did).into_iter());
88             clean::StructItem(build_struct(cx, tcx, did))
89         }
90         def::DefTy(did, false) => {
91             record_extern_fqn(cx, did, clean::TypeTypedef);
92             ret.extend(build_impls(cx, tcx, did).into_iter());
93             build_type(cx, tcx, did)
94         }
95         def::DefTy(did, true) => {
96             record_extern_fqn(cx, did, clean::TypeEnum);
97             ret.extend(build_impls(cx, tcx, did).into_iter());
98             build_type(cx, tcx, did)
99         }
100         // Assume that the enum type is reexported next to the variant, and
101         // variants don't show up in documentation specially.
102         def::DefVariant(..) => return Some(Vec::new()),
103         def::DefMod(did) => {
104             record_extern_fqn(cx, did, clean::TypeModule);
105             clean::ModuleItem(build_module(cx, tcx, did))
106         }
107         def::DefStatic(did, mtbl) => {
108             record_extern_fqn(cx, did, clean::TypeStatic);
109             clean::StaticItem(build_static(cx, tcx, did, mtbl))
110         }
111         _ => return None,
112     };
113     let fqn = csearch::get_item_path(tcx, did);
114     cx.inlined.borrow_mut().as_mut().unwrap().insert(did);
115     ret.push(clean::Item {
116         source: clean::Span::empty(),
117         name: Some(fqn.last().unwrap().to_string()),
118         attrs: load_attrs(cx, tcx, did),
119         inner: inner,
120         visibility: Some(ast::Public),
121         stability: stability::lookup(tcx, did).clean(cx),
122         def_id: did,
123     });
124     Some(ret)
125 }
126
127 pub fn load_attrs(cx: &DocContext, tcx: &ty::ctxt,
128                   did: ast::DefId) -> Vec<clean::Attribute> {
129     let mut attrs = Vec::new();
130     csearch::get_item_attrs(&tcx.sess.cstore, did, |v| {
131         attrs.extend(v.into_iter().map(|a| {
132             a.clean(cx)
133         }));
134     });
135     attrs
136 }
137
138 /// Record an external fully qualified name in the external_paths cache.
139 ///
140 /// These names are used later on by HTML rendering to generate things like
141 /// source links back to the original item.
142 pub fn record_extern_fqn(cx: &DocContext, did: ast::DefId, kind: clean::TypeKind) {
143     match cx.tcx_opt() {
144         Some(tcx) => {
145             let fqn = csearch::get_item_path(tcx, did);
146             let fqn = fqn.into_iter().map(|i| i.to_string()).collect();
147             cx.external_paths.borrow_mut().as_mut().unwrap().insert(did, (fqn, kind));
148         }
149         None => {}
150     }
151 }
152
153 pub fn build_external_trait(cx: &DocContext, tcx: &ty::ctxt,
154                             did: ast::DefId) -> clean::Trait {
155     let def = ty::lookup_trait_def(tcx, did);
156     let trait_items = ty::trait_items(tcx, did).clean(cx);
157     let provided = ty::provided_trait_methods(tcx, did);
158     let mut items = trait_items.into_iter().map(|trait_item| {
159         if provided.iter().any(|a| a.def_id == trait_item.def_id) {
160             clean::ProvidedMethod(trait_item)
161         } else {
162             clean::RequiredMethod(trait_item)
163         }
164     });
165     let trait_def = ty::lookup_trait_def(tcx, did);
166     let bounds = trait_def.bounds.clean(cx);
167     clean::Trait {
168         generics: (&def.generics, subst::TypeSpace).clean(cx),
169         items: items.collect(),
170         bounds: bounds,
171     }
172 }
173
174 fn build_external_function(cx: &DocContext, tcx: &ty::ctxt,
175                            did: ast::DefId,
176                            style: ast::FnStyle) -> clean::Function {
177     let t = ty::lookup_item_type(tcx, did);
178     clean::Function {
179         decl: match ty::get(t.ty).sty {
180             ty::ty_bare_fn(ref f) => (did, &f.sig).clean(cx),
181             _ => fail!("bad function"),
182         },
183         generics: (&t.generics, subst::FnSpace).clean(cx),
184         fn_style: style,
185     }
186 }
187
188 fn build_struct(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId) -> clean::Struct {
189     use syntax::parse::token::special_idents::unnamed_field;
190
191     let t = ty::lookup_item_type(tcx, did);
192     let fields = ty::lookup_struct_fields(tcx, did);
193
194     clean::Struct {
195         struct_type: match fields.as_slice() {
196             [] => doctree::Unit,
197             [ref f] if f.name == unnamed_field.name => doctree::Newtype,
198             [ref f, ..] if f.name == unnamed_field.name => doctree::Tuple,
199             _ => doctree::Plain,
200         },
201         generics: (&t.generics, subst::TypeSpace).clean(cx),
202         fields: fields.clean(cx),
203         fields_stripped: false,
204     }
205 }
206
207 fn build_type(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId) -> clean::ItemEnum {
208     let t = ty::lookup_item_type(tcx, did);
209     match ty::get(t.ty).sty {
210         ty::ty_enum(edid, _) if !csearch::is_typedef(&tcx.sess.cstore, did) => {
211             return clean::EnumItem(clean::Enum {
212                 generics: (&t.generics, subst::TypeSpace).clean(cx),
213                 variants_stripped: false,
214                 variants: ty::enum_variants(tcx, edid).clean(cx),
215             })
216         }
217         _ => {}
218     }
219
220     clean::TypedefItem(clean::Typedef {
221         type_: t.ty.clean(cx),
222         generics: (&t.generics, subst::TypeSpace).clean(cx),
223     })
224 }
225
226 fn build_impls(cx: &DocContext, tcx: &ty::ctxt,
227                did: ast::DefId) -> Vec<clean::Item> {
228     ty::populate_implementations_for_type_if_necessary(tcx, did);
229     let mut impls = Vec::new();
230
231     match tcx.inherent_impls.borrow().find(&did) {
232         None => {}
233         Some(i) => {
234             impls.extend(i.iter().map(|&did| { build_impl(cx, tcx, did) }));
235         }
236     }
237
238     // If this is the first time we've inlined something from this crate, then
239     // we inline *all* impls from the crate into this crate. Note that there's
240     // currently no way for us to filter this based on type, and we likely need
241     // many impls for a variety of reasons.
242     //
243     // Primarily, the impls will be used to populate the documentation for this
244     // type being inlined, but impls can also be used when generating
245     // documentation for primitives (no way to find those specifically).
246     if cx.populated_crate_impls.borrow_mut().insert(did.krate) {
247         csearch::each_top_level_item_of_crate(&tcx.sess.cstore,
248                                               did.krate,
249                                               |def, _, _| {
250             populate_impls(cx, tcx, def, &mut impls)
251         });
252
253         fn populate_impls(cx: &DocContext, tcx: &ty::ctxt,
254                           def: decoder::DefLike,
255                           impls: &mut Vec<Option<clean::Item>>) {
256             match def {
257                 decoder::DlImpl(did) => impls.push(build_impl(cx, tcx, did)),
258                 decoder::DlDef(def::DefMod(did)) => {
259                     csearch::each_child_of_item(&tcx.sess.cstore,
260                                                 did,
261                                                 |def, _, _| {
262                         populate_impls(cx, tcx, def, impls)
263                     })
264                 }
265                 _ => {}
266             }
267         }
268     }
269
270     impls.into_iter().filter_map(|a| a).collect()
271 }
272
273 fn build_impl(cx: &DocContext, tcx: &ty::ctxt,
274               did: ast::DefId) -> Option<clean::Item> {
275     if !cx.inlined.borrow_mut().as_mut().unwrap().insert(did) {
276         return None
277     }
278
279     let associated_trait = csearch::get_impl_trait(tcx, did);
280     // If this is an impl for a #[doc(hidden)] trait, be sure to not inline it.
281     match associated_trait {
282         Some(ref t) => {
283             let trait_attrs = load_attrs(cx, tcx, t.def_id);
284             if trait_attrs.iter().any(|a| is_doc_hidden(a)) {
285                 return None
286             }
287         }
288         None => {}
289     }
290
291     let attrs = load_attrs(cx, tcx, did);
292     let ty = ty::lookup_item_type(tcx, did);
293     let trait_items = csearch::get_impl_items(&tcx.sess.cstore, did)
294             .iter()
295             .filter_map(|did| {
296         let did = did.def_id();
297         let impl_item = ty::impl_or_trait_item(tcx, did);
298         match impl_item {
299             ty::MethodTraitItem(method) => {
300                 if method.vis != ast::Public && associated_trait.is_none() {
301                     return None
302                 }
303                 let mut item = method.clean(cx);
304                 item.inner = match item.inner.clone() {
305                     clean::TyMethodItem(clean::TyMethod {
306                         fn_style, decl, self_, generics
307                     }) => {
308                         clean::MethodItem(clean::Method {
309                             fn_style: fn_style,
310                             decl: decl,
311                             self_: self_,
312                             generics: generics,
313                         })
314                     }
315                     _ => fail!("not a tymethod"),
316                 };
317                 Some(item)
318             }
319             ty::TypeTraitItem(_) => {
320                 // FIXME(pcwalton): Implement.
321                 None
322             }
323         }
324     }).collect();
325     return Some(clean::Item {
326         inner: clean::ImplItem(clean::Impl {
327             derived: clean::detect_derived(attrs.as_slice()),
328             trait_: associated_trait.clean(cx).map(|bound| {
329                 match bound {
330                     clean::TraitBound(ty) => ty,
331                     clean::RegionBound => unreachable!(),
332                 }
333             }),
334             for_: ty.ty.clean(cx),
335             generics: (&ty.generics, subst::TypeSpace).clean(cx),
336             items: trait_items,
337         }),
338         source: clean::Span::empty(),
339         name: None,
340         attrs: attrs,
341         visibility: Some(ast::Inherited),
342         stability: stability::lookup(tcx, did).clean(cx),
343         def_id: did,
344     });
345
346     fn is_doc_hidden(a: &clean::Attribute) -> bool {
347         match *a {
348             clean::List(ref name, ref inner) if name.as_slice() == "doc" => {
349                 inner.iter().any(|a| {
350                     match *a {
351                         clean::Word(ref s) => s.as_slice() == "hidden",
352                         _ => false,
353                     }
354                 })
355             }
356             _ => false
357         }
358     }
359 }
360
361 fn build_module(cx: &DocContext, tcx: &ty::ctxt,
362                 did: ast::DefId) -> clean::Module {
363     let mut items = Vec::new();
364     fill_in(cx, tcx, did, &mut items);
365     return clean::Module {
366         items: items,
367         is_crate: false,
368     };
369
370     // FIXME: this doesn't handle reexports inside the module itself.
371     //        Should they be handled?
372     fn fill_in(cx: &DocContext, tcx: &ty::ctxt, did: ast::DefId,
373                items: &mut Vec<clean::Item>) {
374         csearch::each_child_of_item(&tcx.sess.cstore, did, |def, _, vis| {
375             match def {
376                 decoder::DlDef(def::DefForeignMod(did)) => {
377                     fill_in(cx, tcx, did, items);
378                 }
379                 decoder::DlDef(def) if vis == ast::Public => {
380                     match try_inline_def(cx, tcx, def) {
381                         Some(i) => items.extend(i.into_iter()),
382                         None => {}
383                     }
384                 }
385                 decoder::DlDef(..) => {}
386                 // All impls were inlined above
387                 decoder::DlImpl(..) => {}
388                 decoder::DlField => fail!("unimplemented field"),
389             }
390         });
391     }
392 }
393
394 fn build_static(cx: &DocContext, tcx: &ty::ctxt,
395                 did: ast::DefId,
396                 mutable: bool) -> clean::Static {
397     clean::Static {
398         type_: ty::lookup_item_type(tcx, did).ty.clean(cx),
399         mutability: if mutable {clean::Mutable} else {clean::Immutable},
400         expr: "\n\n\n".to_string(), // trigger the "[definition]" links
401     }
402 }