]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/clean/inline.rs
Don't render const keyword on stable
[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 std::iter::once;
14
15 use syntax::ast;
16 use syntax::ext::base::{MacroKind, SyntaxExtension};
17 use syntax_pos::Span;
18
19 use rustc::hir;
20 use rustc::hir::def::{Def, CtorKind};
21 use rustc::hir::def_id::DefId;
22 use rustc_metadata::cstore::LoadedMacro;
23 use rustc::ty;
24 use rustc::util::nodemap::FxHashSet;
25
26 use core::{DocContext, DocAccessLevels};
27 use doctree;
28 use clean::{
29     self,
30     GetDefId,
31     ToSource,
32 };
33
34 use super::Clean;
35
36 /// Attempt to inline a definition into this AST.
37 ///
38 /// This function will fetch the definition specified, and if it is
39 /// from another crate it will attempt to inline the documentation
40 /// from the other crate into this crate.
41 ///
42 /// This is primarily used for `pub use` statements which are, in general,
43 /// implementation details. Inlining the documentation should help provide a
44 /// better experience when reading the documentation in this use case.
45 ///
46 /// The returned value is `None` if the definition could not be inlined,
47 /// and `Some` of a vector of items if it was successfully expanded.
48 pub fn try_inline(cx: &DocContext, def: Def, name: ast::Name, visited: &mut FxHashSet<DefId>)
49                   -> Option<Vec<clean::Item>> {
50     if def == Def::Err { return None }
51     let did = def.def_id();
52     if did.is_local() { return None }
53     let mut ret = Vec::new();
54     let inner = match def {
55         Def::Trait(did) => {
56             record_extern_fqn(cx, did, clean::TypeKind::Trait);
57             ret.extend(build_impls(cx, did));
58             clean::TraitItem(build_external_trait(cx, did))
59         }
60         Def::Fn(did) => {
61             record_extern_fqn(cx, did, clean::TypeKind::Function);
62             clean::FunctionItem(build_external_function(cx, did))
63         }
64         Def::Struct(did) => {
65             record_extern_fqn(cx, did, clean::TypeKind::Struct);
66             ret.extend(build_impls(cx, did));
67             clean::StructItem(build_struct(cx, did))
68         }
69         Def::Union(did) => {
70             record_extern_fqn(cx, did, clean::TypeKind::Union);
71             ret.extend(build_impls(cx, did));
72             clean::UnionItem(build_union(cx, did))
73         }
74         Def::TyAlias(did) => {
75             record_extern_fqn(cx, did, clean::TypeKind::Typedef);
76             ret.extend(build_impls(cx, did));
77             clean::TypedefItem(build_type_alias(cx, did), false)
78         }
79         Def::Enum(did) => {
80             record_extern_fqn(cx, did, clean::TypeKind::Enum);
81             ret.extend(build_impls(cx, did));
82             clean::EnumItem(build_enum(cx, did))
83         }
84         Def::ForeignTy(did) => {
85             record_extern_fqn(cx, did, clean::TypeKind::Foreign);
86             ret.extend(build_impls(cx, did));
87             clean::ForeignTypeItem
88         }
89         // Never inline enum variants but leave them shown as re-exports.
90         Def::Variant(..) => return None,
91         // Assume that enum variants and struct types are re-exported next to
92         // their constructors.
93         Def::VariantCtor(..) |
94         Def::StructCtor(..) |
95         Def::SelfCtor(..) => return Some(Vec::new()),
96         Def::Mod(did) => {
97             record_extern_fqn(cx, did, clean::TypeKind::Module);
98             clean::ModuleItem(build_module(cx, did, visited))
99         }
100         Def::Static(did, mtbl) => {
101             record_extern_fqn(cx, did, clean::TypeKind::Static);
102             clean::StaticItem(build_static(cx, did, mtbl))
103         }
104         Def::Const(did) => {
105             record_extern_fqn(cx, did, clean::TypeKind::Const);
106             clean::ConstantItem(build_const(cx, did))
107         }
108         // FIXME: proc-macros don't propagate attributes or spans across crates, so they look empty
109         Def::Macro(did, MacroKind::Bang) => {
110             let mac = build_macro(cx, did, name);
111             if let clean::MacroItem(..) = mac {
112                 record_extern_fqn(cx, did, clean::TypeKind::Macro);
113                 mac
114             } else {
115                 return None;
116             }
117         }
118         _ => return None,
119     };
120     cx.renderinfo.borrow_mut().inlined.insert(did);
121     ret.push(clean::Item {
122         source: cx.tcx.def_span(did).clean(cx),
123         name: Some(name.clean(cx)),
124         attrs: load_attrs(cx, did),
125         inner,
126         visibility: Some(clean::Public),
127         stability: cx.tcx.lookup_stability(did).clean(cx),
128         deprecation: cx.tcx.lookup_deprecation(did).clean(cx),
129         def_id: did,
130     });
131     Some(ret)
132 }
133
134 pub fn try_inline_glob(cx: &DocContext, def: Def, visited: &mut FxHashSet<DefId>)
135     -> Option<Vec<clean::Item>>
136 {
137     if def == Def::Err { return None }
138     let did = def.def_id();
139     if did.is_local() { return None }
140
141     match def {
142         Def::Mod(did) => {
143             let m = build_module(cx, did, visited);
144             Some(m.items)
145         }
146         // glob imports on things like enums aren't inlined even for local exports, so just bail
147         _ => None,
148     }
149 }
150
151 pub fn load_attrs(cx: &DocContext, did: DefId) -> clean::Attributes {
152     cx.tcx.get_attrs(did).clean(cx)
153 }
154
155 /// Record an external fully qualified name in the external_paths cache.
156 ///
157 /// These names are used later on by HTML rendering to generate things like
158 /// source links back to the original item.
159 pub fn record_extern_fqn(cx: &DocContext, did: DefId, kind: clean::TypeKind) {
160     let mut crate_name = cx.tcx.crate_name(did.krate).to_string();
161     if did.is_local() {
162         crate_name = cx.crate_name.clone().unwrap_or(crate_name);
163     }
164
165     let relative = cx.tcx.def_path(did).data.into_iter().filter_map(|elem| {
166         // extern blocks have an empty name
167         let s = elem.data.to_string();
168         if !s.is_empty() {
169             Some(s)
170         } else {
171             None
172         }
173     });
174     let fqn = if let clean::TypeKind::Macro = kind {
175         vec![crate_name, relative.last().expect("relative was empty")]
176     } else {
177         once(crate_name).chain(relative).collect()
178     };
179
180     if did.is_local() {
181         cx.renderinfo.borrow_mut().exact_paths.insert(did, fqn);
182     } else {
183         cx.renderinfo.borrow_mut().external_paths.insert(did, (fqn, kind));
184     }
185 }
186
187 pub fn build_external_trait(cx: &DocContext, did: DefId) -> clean::Trait {
188     let auto_trait = cx.tcx.trait_def(did).has_auto_impl;
189     let trait_items = cx.tcx.associated_items(did).map(|item| item.clean(cx)).collect();
190     let predicates = cx.tcx.predicates_of(did);
191     let generics = (cx.tcx.generics_of(did), &predicates).clean(cx);
192     let generics = filter_non_trait_generics(did, generics);
193     let (generics, supertrait_bounds) = separate_supertrait_bounds(generics);
194     let is_spotlight = load_attrs(cx, did).has_doc_flag("spotlight");
195     let is_auto = cx.tcx.trait_is_auto(did);
196     clean::Trait {
197         auto: auto_trait,
198         unsafety: cx.tcx.trait_def(did).unsafety,
199         generics,
200         items: trait_items,
201         bounds: supertrait_bounds,
202         is_spotlight,
203         is_auto,
204     }
205 }
206
207 fn build_external_function(cx: &DocContext, did: DefId) -> clean::Function {
208     let sig = cx.tcx.fn_sig(did);
209
210     let constness = if cx.tcx.is_min_const_fn(did) {
211         hir::Constness::Const
212     } else {
213         hir::Constness::NotConst
214     };
215
216     let predicates = cx.tcx.predicates_of(did);
217     clean::Function {
218         decl: (did, sig).clean(cx),
219         generics: (cx.tcx.generics_of(did), &predicates).clean(cx),
220         header: hir::FnHeader {
221             unsafety: sig.unsafety(),
222             abi: sig.abi(),
223             constness,
224             asyncness: hir::IsAsync::NotAsync,
225         }
226     }
227 }
228
229 fn build_enum(cx: &DocContext, did: DefId) -> clean::Enum {
230     let predicates = cx.tcx.predicates_of(did);
231
232     clean::Enum {
233         generics: (cx.tcx.generics_of(did), &predicates).clean(cx),
234         variants_stripped: false,
235         variants: cx.tcx.adt_def(did).variants.clean(cx),
236     }
237 }
238
239 fn build_struct(cx: &DocContext, did: DefId) -> clean::Struct {
240     let predicates = cx.tcx.predicates_of(did);
241     let variant = cx.tcx.adt_def(did).non_enum_variant();
242
243     clean::Struct {
244         struct_type: match variant.ctor_kind {
245             CtorKind::Fictive => doctree::Plain,
246             CtorKind::Fn => doctree::Tuple,
247             CtorKind::Const => doctree::Unit,
248         },
249         generics: (cx.tcx.generics_of(did), &predicates).clean(cx),
250         fields: variant.fields.clean(cx),
251         fields_stripped: false,
252     }
253 }
254
255 fn build_union(cx: &DocContext, did: DefId) -> clean::Union {
256     let predicates = cx.tcx.predicates_of(did);
257     let variant = cx.tcx.adt_def(did).non_enum_variant();
258
259     clean::Union {
260         struct_type: doctree::Plain,
261         generics: (cx.tcx.generics_of(did), &predicates).clean(cx),
262         fields: variant.fields.clean(cx),
263         fields_stripped: false,
264     }
265 }
266
267 fn build_type_alias(cx: &DocContext, did: DefId) -> clean::Typedef {
268     let predicates = cx.tcx.predicates_of(did);
269
270     clean::Typedef {
271         type_: cx.tcx.type_of(did).clean(cx),
272         generics: (cx.tcx.generics_of(did), &predicates).clean(cx),
273     }
274 }
275
276 pub fn build_impls(cx: &DocContext, did: DefId) -> Vec<clean::Item> {
277     let tcx = cx.tcx;
278     let mut impls = Vec::new();
279
280     for &did in tcx.inherent_impls(did).iter() {
281         build_impl(cx, did, &mut impls);
282     }
283
284     impls
285 }
286
287 pub fn build_impl(cx: &DocContext, did: DefId, ret: &mut Vec<clean::Item>) {
288     if !cx.renderinfo.borrow_mut().inlined.insert(did) {
289         return
290     }
291
292     let attrs = load_attrs(cx, did);
293     let tcx = cx.tcx;
294     let associated_trait = tcx.impl_trait_ref(did);
295
296     // Only inline impl if the implemented trait is
297     // reachable in rustdoc generated documentation
298     if !did.is_local() {
299         if let Some(traitref) = associated_trait {
300             if !cx.renderinfo.borrow().access_levels.is_doc_reachable(traitref.def_id) {
301                 return
302             }
303         }
304     }
305
306     let for_ = if let Some(nodeid) = tcx.hir().as_local_node_id(did) {
307         match tcx.hir().expect_item(nodeid).node {
308             hir::ItemKind::Impl(.., ref t, _) => {
309                 t.clean(cx)
310             }
311             _ => panic!("did given to build_impl was not an impl"),
312         }
313     } else {
314         tcx.type_of(did).clean(cx)
315     };
316
317     // Only inline impl if the implementing type is
318     // reachable in rustdoc generated documentation
319     if !did.is_local() {
320         if let Some(did) = for_.def_id() {
321             if !cx.renderinfo.borrow().access_levels.is_doc_reachable(did) {
322                 return
323             }
324         }
325     }
326
327     let predicates = tcx.predicates_of(did);
328     let (trait_items, generics) = if let Some(nodeid) = tcx.hir().as_local_node_id(did) {
329         match tcx.hir().expect_item(nodeid).node {
330             hir::ItemKind::Impl(.., ref gen, _, _, ref item_ids) => {
331                 (
332                     item_ids.iter()
333                             .map(|ii| tcx.hir().impl_item(ii.id).clean(cx))
334                             .collect::<Vec<_>>(),
335                     gen.clean(cx),
336                 )
337             }
338             _ => panic!("did given to build_impl was not an impl"),
339         }
340     } else {
341         (
342             tcx.associated_items(did).filter_map(|item| {
343                 if associated_trait.is_some() || item.vis == ty::Visibility::Public {
344                     Some(item.clean(cx))
345                 } else {
346                     None
347                 }
348             }).collect::<Vec<_>>(),
349             (tcx.generics_of(did), &predicates).clean(cx),
350         )
351     };
352     let polarity = tcx.impl_polarity(did);
353     let trait_ = associated_trait.clean(cx).map(|bound| {
354         match bound {
355             clean::GenericBound::TraitBound(polyt, _) => polyt.trait_,
356             clean::GenericBound::Outlives(..) => unreachable!(),
357         }
358     });
359     if trait_.def_id() == tcx.lang_items().deref_trait() {
360         super::build_deref_target_impls(cx, &trait_items, ret);
361     }
362     if let Some(trait_did) = trait_.def_id() {
363         record_extern_trait(cx, trait_did);
364     }
365
366     let provided = trait_.def_id().map(|did| {
367         tcx.provided_trait_methods(did)
368            .into_iter()
369            .map(|meth| meth.ident.to_string())
370            .collect()
371     }).unwrap_or_default();
372
373     debug!("build_impl: impl {:?} for {:?}", trait_.def_id(), for_.def_id());
374
375     ret.push(clean::Item {
376         inner: clean::ImplItem(clean::Impl {
377             unsafety: hir::Unsafety::Normal,
378             generics,
379             provided_trait_methods: provided,
380             trait_,
381             for_,
382             items: trait_items,
383             polarity: Some(polarity.clean(cx)),
384             synthetic: false,
385             blanket_impl: None,
386         }),
387         source: tcx.def_span(did).clean(cx),
388         name: None,
389         attrs,
390         visibility: Some(clean::Inherited),
391         stability: tcx.lookup_stability(did).clean(cx),
392         deprecation: tcx.lookup_deprecation(did).clean(cx),
393         def_id: did,
394     });
395 }
396
397 fn build_module(cx: &DocContext, did: DefId, visited: &mut FxHashSet<DefId>) -> clean::Module {
398     let mut items = Vec::new();
399     fill_in(cx, did, &mut items, visited);
400     return clean::Module {
401         items,
402         is_crate: false,
403     };
404
405     fn fill_in(cx: &DocContext, did: DefId, items: &mut Vec<clean::Item>,
406                visited: &mut FxHashSet<DefId>) {
407         // If we're re-exporting a re-export it may actually re-export something in
408         // two namespaces, so the target may be listed twice. Make sure we only
409         // visit each node at most once.
410         for &item in cx.tcx.item_children(did).iter() {
411             let def_id = item.def.def_id();
412             if item.vis == ty::Visibility::Public {
413                 if did == def_id || !visited.insert(def_id) { continue }
414                 if let Some(i) = try_inline(cx, item.def, item.ident.name, visited) {
415                     items.extend(i)
416                 }
417             }
418         }
419     }
420 }
421
422 pub fn print_inlined_const(cx: &DocContext, did: DefId) -> String {
423     if let Some(node_id) = cx.tcx.hir().as_local_node_id(did) {
424         cx.tcx.hir().node_to_pretty_string(node_id)
425     } else {
426         cx.tcx.rendered_const(did)
427     }
428 }
429
430 fn build_const(cx: &DocContext, did: DefId) -> clean::Constant {
431     clean::Constant {
432         type_: cx.tcx.type_of(did).clean(cx),
433         expr: print_inlined_const(cx, did)
434     }
435 }
436
437 fn build_static(cx: &DocContext, did: DefId, mutable: bool) -> clean::Static {
438     clean::Static {
439         type_: cx.tcx.type_of(did).clean(cx),
440         mutability: if mutable {clean::Mutable} else {clean::Immutable},
441         expr: "\n\n\n".to_string(), // trigger the "[definition]" links
442     }
443 }
444
445 fn build_macro(cx: &DocContext, did: DefId, name: ast::Name) -> clean::ItemEnum {
446     let imported_from = cx.tcx.original_crate_name(did.krate);
447     match cx.cstore.load_macro_untracked(did, cx.sess()) {
448         LoadedMacro::MacroDef(def) => {
449             let matchers: hir::HirVec<Span> = if let ast::ItemKind::MacroDef(ref def) = def.node {
450                 let tts: Vec<_> = def.stream().into_trees().collect();
451                 tts.chunks(4).map(|arm| arm[0].span()).collect()
452             } else {
453                 unreachable!()
454             };
455
456             let source = format!("macro_rules! {} {{\n{}}}",
457                                  name.clean(cx),
458                                  matchers.iter().map(|span| {
459                                      format!("    {} => {{ ... }};\n", span.to_src(cx))
460                                  }).collect::<String>());
461
462             clean::MacroItem(clean::Macro {
463                 source,
464                 imported_from: Some(imported_from).clean(cx),
465             })
466         }
467         LoadedMacro::ProcMacro(ext) => {
468             let helpers = match &*ext {
469                 &SyntaxExtension::ProcMacroDerive(_, ref syms, ..) => { syms.clean(cx) }
470                 _ => Vec::new(),
471             };
472
473             clean::ProcMacroItem(clean::ProcMacro {
474                 kind: ext.kind(),
475                 helpers,
476             })
477         }
478     }
479
480 }
481
482 /// A trait's generics clause actually contains all of the predicates for all of
483 /// its associated types as well. We specifically move these clauses to the
484 /// associated types instead when displaying, so when we're generating the
485 /// generics for the trait itself we need to be sure to remove them.
486 /// We also need to remove the implied "recursive" Self: Trait bound.
487 ///
488 /// The inverse of this filtering logic can be found in the `Clean`
489 /// implementation for `AssociatedType`
490 fn filter_non_trait_generics(trait_did: DefId, mut g: clean::Generics) -> clean::Generics {
491     for pred in &mut g.where_predicates {
492         match *pred {
493             clean::WherePredicate::BoundPredicate {
494                 ty: clean::Generic(ref s),
495                 ref mut bounds
496             } if *s == "Self" => {
497                 bounds.retain(|bound| {
498                     match *bound {
499                         clean::GenericBound::TraitBound(clean::PolyTrait {
500                             trait_: clean::ResolvedPath { did, .. },
501                             ..
502                         }, _) => did != trait_did,
503                         _ => true
504                     }
505                 });
506             }
507             _ => {}
508         }
509     }
510
511     g.where_predicates.retain(|pred| {
512         match *pred {
513             clean::WherePredicate::BoundPredicate {
514                 ty: clean::QPath {
515                     self_type: box clean::Generic(ref s),
516                     trait_: box clean::ResolvedPath { did, .. },
517                     name: ref _name,
518                 }, ref bounds
519             } => !(*s == "Self" && did == trait_did) && !bounds.is_empty(),
520             _ => true,
521         }
522     });
523     g
524 }
525
526 /// Supertrait bounds for a trait are also listed in the generics coming from
527 /// the metadata for a crate, so we want to separate those out and create a new
528 /// list of explicit supertrait bounds to render nicely.
529 fn separate_supertrait_bounds(mut g: clean::Generics)
530                               -> (clean::Generics, Vec<clean::GenericBound>) {
531     let mut ty_bounds = Vec::new();
532     g.where_predicates.retain(|pred| {
533         match *pred {
534             clean::WherePredicate::BoundPredicate {
535                 ty: clean::Generic(ref s),
536                 ref bounds
537             } if *s == "Self" => {
538                 ty_bounds.extend(bounds.iter().cloned());
539                 false
540             }
541             _ => true,
542         }
543     });
544     (g, ty_bounds)
545 }
546
547 pub fn record_extern_trait(cx: &DocContext, did: DefId) {
548     if did.is_local() {
549         return;
550     }
551
552     {
553         let external_traits = cx.external_traits.lock();
554         if external_traits.borrow().contains_key(&did) ||
555             cx.active_extern_traits.borrow().contains(&did)
556         {
557             return;
558         }
559     }
560
561     cx.active_extern_traits.borrow_mut().push(did);
562
563     debug!("record_extern_trait: {:?}", did);
564     let trait_ = build_external_trait(cx, did);
565
566     {
567         let external_traits = cx.external_traits.lock();
568         external_traits.borrow_mut().insert(did, trait_);
569     }
570     cx.active_extern_traits.borrow_mut().remove_item(&did);
571 }