]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/visit_ast.rs
Fix rustc-guide toolstate tracking.
[rust.git] / src / librustdoc / visit_ast.rs
1 //! The Rust AST Visitor. Extracts useful information and massages it into a form
2 //! usable for `clean`.
3
4 use rustc::hir::{self, Node};
5 use rustc::hir::def::{Res, DefKind};
6 use rustc::hir::def_id::{DefId, LOCAL_CRATE};
7 use rustc::middle::privacy::AccessLevel;
8 use rustc::util::nodemap::{FxHashSet, FxHashMap};
9 use syntax::ast;
10 use syntax::attr;
11 use syntax::ext::base::MacroKind;
12 use syntax::source_map::Spanned;
13 use syntax::symbol::sym;
14 use syntax_pos::{self, Span};
15
16 use std::mem;
17
18 use crate::core;
19 use crate::clean::{self, AttributesExt, NestedAttributesExt, def_id_to_path};
20 use crate::doctree::*;
21
22
23 // Looks to me like the first two of these are actually
24 // output parameters, maybe only mutated once; perhaps
25 // better simply to have the visit method return a tuple
26 // containing them?
27
28 // Also, is there some reason that this doesn't use the 'visit'
29 // framework from syntax?.
30
31 pub struct RustdocVisitor<'a, 'tcx> {
32     pub module: Option<Module<'tcx>>,
33     pub cx: &'a core::DocContext<'tcx>,
34     view_item_stack: FxHashSet<hir::HirId>,
35     inlining: bool,
36     /// Are the current module and all of its parents public?
37     inside_public_path: bool,
38     exact_paths: Option<FxHashMap<DefId, Vec<String>>>,
39 }
40
41 impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
42     pub fn new(
43         cx: &'a core::DocContext<'tcx>
44     ) -> RustdocVisitor<'a, 'tcx> {
45         // If the root is re-exported, terminate all recursion.
46         let mut stack = FxHashSet::default();
47         stack.insert(hir::CRATE_HIR_ID);
48         RustdocVisitor {
49             module: None,
50             cx,
51             view_item_stack: stack,
52             inlining: false,
53             inside_public_path: true,
54             exact_paths: Some(FxHashMap::default()),
55         }
56     }
57
58     fn store_path(&mut self, did: DefId) {
59         // We can't use the entry API, as that keeps the mutable borrow of `self` active
60         // when we try to use `cx`.
61         let exact_paths = self.exact_paths.as_mut().unwrap();
62         if exact_paths.get(&did).is_none() {
63             let path = def_id_to_path(self.cx, did, self.cx.crate_name.clone());
64             exact_paths.insert(did, path);
65         }
66     }
67
68     fn stability(&self, id: hir::HirId) -> Option<attr::Stability> {
69         self.cx.tcx.hir().opt_local_def_id(id)
70             .and_then(|def_id| self.cx.tcx.lookup_stability(def_id)).cloned()
71     }
72
73     fn deprecation(&self, id: hir::HirId) -> Option<attr::Deprecation> {
74         self.cx.tcx.hir().opt_local_def_id(id)
75             .and_then(|def_id| self.cx.tcx.lookup_deprecation(def_id))
76     }
77
78     pub fn visit(&mut self, krate: &'tcx hir::Crate) {
79         let mut module = self.visit_mod_contents(krate.span,
80                                               &krate.attrs,
81                                               &Spanned { span: syntax_pos::DUMMY_SP,
82                                                         node: hir::VisibilityKind::Public },
83                                               hir::CRATE_HIR_ID,
84                                               &krate.module,
85                                               None);
86         // Attach the crate's exported macros to the top-level module:
87         module.macros.extend(
88             krate.exported_macros.iter().map(|def| self.visit_local_macro(def, None)),
89         );
90         module.is_crate = true;
91         self.module = Some(module);
92
93         self.cx.renderinfo.borrow_mut().exact_paths = self.exact_paths.take().unwrap();
94     }
95
96     pub fn visit_variant_data(&mut self, item: &'tcx hir::Item,
97                               name: ast::Name, sd: &'tcx hir::VariantData,
98                               generics: &'tcx hir::Generics) -> Struct<'tcx> {
99         debug!("visiting struct");
100         let struct_type = struct_type_from_def(&*sd);
101         Struct {
102             id: item.hir_id,
103             struct_type,
104             name,
105             vis: &item.vis,
106             stab: self.stability(item.hir_id),
107             depr: self.deprecation(item.hir_id),
108             attrs: &item.attrs,
109             generics,
110             fields: sd.fields(),
111             whence: item.span
112         }
113     }
114
115     pub fn visit_union_data(&mut self, item: &'tcx hir::Item,
116                             name: ast::Name, sd: &'tcx hir::VariantData,
117                             generics: &'tcx hir::Generics) -> Union<'tcx> {
118         debug!("visiting union");
119         let struct_type = struct_type_from_def(&*sd);
120         Union {
121             id: item.hir_id,
122             struct_type,
123             name,
124             vis: &item.vis,
125             stab: self.stability(item.hir_id),
126             depr: self.deprecation(item.hir_id),
127             attrs: &item.attrs,
128             generics,
129             fields: sd.fields(),
130             whence: item.span
131         }
132     }
133
134     pub fn visit_enum_def(&mut self, it: &'tcx hir::Item,
135                           name: ast::Name, def: &'tcx hir::EnumDef,
136                           generics: &'tcx hir::Generics) -> Enum<'tcx> {
137         debug!("visiting enum");
138         Enum {
139             name,
140             variants: def.variants.iter().map(|v| Variant {
141                 name: v.node.ident.name,
142                 id: v.node.id,
143                 attrs: &v.node.attrs,
144                 stab: self.stability(v.node.id),
145                 depr: self.deprecation(v.node.id),
146                 def: &v.node.data,
147                 whence: v.span,
148             }).collect(),
149             vis: &it.vis,
150             stab: self.stability(it.hir_id),
151             depr: self.deprecation(it.hir_id),
152             generics,
153             attrs: &it.attrs,
154             id: it.hir_id,
155             whence: it.span,
156         }
157     }
158
159     pub fn visit_fn(&mut self, om: &mut Module<'tcx>, item: &'tcx hir::Item,
160                     name: ast::Name, decl: &'tcx hir::FnDecl,
161                     header: hir::FnHeader,
162                     generics: &'tcx hir::Generics,
163                     body: hir::BodyId) {
164         debug!("visiting fn");
165         let macro_kind = item.attrs.iter().filter_map(|a| {
166             if a.check_name(sym::proc_macro) {
167                 Some(MacroKind::Bang)
168             } else if a.check_name(sym::proc_macro_derive) {
169                 Some(MacroKind::Derive)
170             } else if a.check_name(sym::proc_macro_attribute) {
171                 Some(MacroKind::Attr)
172             } else {
173                 None
174             }
175         }).next();
176         match macro_kind {
177             Some(kind) => {
178                 let name = if kind == MacroKind::Derive {
179                     item.attrs.lists(sym::proc_macro_derive)
180                               .filter_map(|mi| mi.ident())
181                               .next()
182                               .expect("proc-macro derives require a name")
183                               .name
184                 } else {
185                     name
186                 };
187
188                 let mut helpers = Vec::new();
189                 for mi in item.attrs.lists(sym::proc_macro_derive) {
190                     if !mi.check_name(sym::attributes) {
191                         continue;
192                     }
193
194                     if let Some(list) = mi.meta_item_list() {
195                         for inner_mi in list {
196                             if let Some(ident) = inner_mi.ident() {
197                                 helpers.push(ident.name);
198                             }
199                         }
200                     }
201                 }
202
203                 om.proc_macros.push(ProcMacro {
204                     name,
205                     id: item.hir_id,
206                     kind,
207                     helpers,
208                     attrs: &item.attrs,
209                     whence: item.span,
210                     stab: self.stability(item.hir_id),
211                     depr: self.deprecation(item.hir_id),
212                 });
213             }
214             None => {
215                 om.fns.push(Function {
216                     id: item.hir_id,
217                     vis: &item.vis,
218                     stab: self.stability(item.hir_id),
219                     depr: self.deprecation(item.hir_id),
220                     attrs: &item.attrs,
221                     decl,
222                     name,
223                     whence: item.span,
224                     generics,
225                     header,
226                     body,
227                 });
228             }
229         }
230     }
231
232     pub fn visit_mod_contents(&mut self, span: Span, attrs: &'tcx hir::HirVec<ast::Attribute>,
233                               vis: &'tcx hir::Visibility, id: hir::HirId,
234                               m: &'tcx hir::Mod,
235                               name: Option<ast::Name>) -> Module<'tcx> {
236         let mut om = Module::new(name, attrs, vis);
237         om.where_outer = span;
238         om.where_inner = m.inner;
239         om.stab = self.stability(id);
240         om.depr = self.deprecation(id);
241         om.id = self.cx.tcx.hir().hir_to_node_id(id);
242         // Keep track of if there were any private modules in the path.
243         let orig_inside_public_path = self.inside_public_path;
244         self.inside_public_path &= vis.node.is_pub();
245         for i in &m.item_ids {
246             let item = self.cx.tcx.hir().expect_item(i.id);
247             self.visit_item(item, None, &mut om);
248         }
249         self.inside_public_path = orig_inside_public_path;
250         om
251     }
252
253     /// Tries to resolve the target of a `pub use` statement and inlines the
254     /// target if it is defined locally and would not be documented otherwise,
255     /// or when it is specifically requested with `please_inline`.
256     /// (the latter is the case when the import is marked `doc(inline)`)
257     ///
258     /// Cross-crate inlining occurs later on during crate cleaning
259     /// and follows different rules.
260     ///
261     /// Returns `true` if the target has been inlined.
262     fn maybe_inline_local(&mut self,
263                           id: hir::HirId,
264                           res: Res,
265                           renamed: Option<ast::Ident>,
266                           glob: bool,
267                           om: &mut Module<'tcx>,
268                           please_inline: bool) -> bool {
269
270         fn inherits_doc_hidden(cx: &core::DocContext<'_>, mut node: hir::HirId) -> bool {
271             while let Some(id) = cx.tcx.hir().get_enclosing_scope(node) {
272                 node = id;
273                 if cx.tcx.hir().attrs(node)
274                     .lists(sym::doc).has_word(sym::hidden) {
275                     return true;
276                 }
277                 if node == hir::CRATE_HIR_ID {
278                     break;
279                 }
280             }
281             false
282         }
283
284         debug!("maybe_inline_local res: {:?}", res);
285
286         let tcx = self.cx.tcx;
287         let res_did = if let Some(did) = res.opt_def_id() {
288             did
289         } else {
290             return false;
291         };
292
293         let use_attrs = tcx.hir().attrs(id);
294         // Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
295         let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline) ||
296                            use_attrs.lists(sym::doc).has_word(sym::hidden);
297
298         // For cross-crate impl inlining we need to know whether items are
299         // reachable in documentation -- a previously nonreachable item can be
300         // made reachable by cross-crate inlining which we're checking here.
301         // (this is done here because we need to know this upfront).
302         if !res_did.is_local() && !is_no_inline {
303             let attrs = clean::inline::load_attrs(self.cx, res_did);
304             let self_is_hidden = attrs.lists(sym::doc).has_word(sym::hidden);
305             match res {
306                 Res::Def(DefKind::Trait, did) |
307                 Res::Def(DefKind::Struct, did) |
308                 Res::Def(DefKind::Union, did) |
309                 Res::Def(DefKind::Enum, did) |
310                 Res::Def(DefKind::ForeignTy, did) |
311                 Res::Def(DefKind::TyAlias, did) if !self_is_hidden => {
312                     self.cx.renderinfo
313                         .borrow_mut()
314                         .access_levels.map
315                         .insert(did, AccessLevel::Public);
316                 },
317                 Res::Def(DefKind::Mod, did) => if !self_is_hidden {
318                     crate::visit_lib::LibEmbargoVisitor::new(self.cx).visit_mod(did);
319                 },
320                 _ => {},
321             }
322
323             return false
324         }
325
326         let res_hir_id = match tcx.hir().as_local_hir_id(res_did) {
327             Some(n) => n, None => return false
328         };
329
330         let is_private = !self.cx.renderinfo.borrow().access_levels.is_public(res_did);
331         let is_hidden = inherits_doc_hidden(self.cx, res_hir_id);
332
333         // Only inline if requested or if the item would otherwise be stripped.
334         if (!please_inline && !is_private && !is_hidden) || is_no_inline {
335             return false
336         }
337
338         if !self.view_item_stack.insert(res_hir_id) { return false }
339
340         let ret = match tcx.hir().get(res_hir_id) {
341             Node::Item(&hir::Item { node: hir::ItemKind::Mod(ref m), .. }) if glob => {
342                 let prev = mem::replace(&mut self.inlining, true);
343                 for i in &m.item_ids {
344                     let i = self.cx.tcx.hir().expect_item(i.id);
345                     self.visit_item(i, None, om);
346                 }
347                 self.inlining = prev;
348                 true
349             }
350             Node::Item(it) if !glob => {
351                 let prev = mem::replace(&mut self.inlining, true);
352                 self.visit_item(it, renamed, om);
353                 self.inlining = prev;
354                 true
355             }
356             Node::ForeignItem(it) if !glob => {
357                 let prev = mem::replace(&mut self.inlining, true);
358                 self.visit_foreign_item(it, renamed, om);
359                 self.inlining = prev;
360                 true
361             }
362             Node::MacroDef(def) if !glob => {
363                 om.macros.push(self.visit_local_macro(def, renamed.map(|i| i.name)));
364                 true
365             }
366             _ => false,
367         };
368         self.view_item_stack.remove(&res_hir_id);
369         ret
370     }
371
372     pub fn visit_item(&mut self, item: &'tcx hir::Item,
373                       renamed: Option<ast::Ident>, om: &mut Module<'tcx>) {
374         debug!("visiting item {:?}", item);
375         let ident = renamed.unwrap_or(item.ident);
376
377         if item.vis.node.is_pub() {
378             let def_id = self.cx.tcx.hir().local_def_id(item.hir_id);
379             self.store_path(def_id);
380         }
381
382         match item.node {
383             hir::ItemKind::ForeignMod(ref fm) => {
384                 for item in &fm.items {
385                     self.visit_foreign_item(item, None, om);
386                 }
387             }
388             // If we're inlining, skip private items.
389             _ if self.inlining && !item.vis.node.is_pub() => {}
390             hir::ItemKind::GlobalAsm(..) => {}
391             hir::ItemKind::ExternCrate(orig_name) => {
392                 let def_id = self.cx.tcx.hir().local_def_id(item.hir_id);
393                 om.extern_crates.push(ExternCrate {
394                     cnum: self.cx.tcx.extern_mod_stmt_cnum(def_id)
395                                 .unwrap_or(LOCAL_CRATE),
396                     name: ident.name,
397                     path: orig_name.map(|x|x.to_string()),
398                     vis: &item.vis,
399                     attrs: &item.attrs,
400                     whence: item.span,
401                 })
402             }
403             hir::ItemKind::Use(_, hir::UseKind::ListStem) => {}
404             hir::ItemKind::Use(ref path, kind) => {
405                 let is_glob = kind == hir::UseKind::Glob;
406
407                 // Struct and variant constructors and proc macro stubs always show up alongside
408                 // their definitions, we've already processed them so just discard these.
409                 if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = path.res {
410                     return;
411                 }
412
413                 // If there was a private module in the current path then don't bother inlining
414                 // anything as it will probably be stripped anyway.
415                 if item.vis.node.is_pub() && self.inside_public_path {
416                     let please_inline = item.attrs.iter().any(|item| {
417                         match item.meta_item_list() {
418                             Some(ref list) if item.check_name(sym::doc) => {
419                                 list.iter().any(|i| i.check_name(sym::inline))
420                             }
421                             _ => false,
422                         }
423                     });
424                     let ident = if is_glob { None } else { Some(ident) };
425                     if self.maybe_inline_local(item.hir_id,
426                                                path.res,
427                                                ident,
428                                                is_glob,
429                                                om,
430                                                please_inline) {
431                         return;
432                     }
433                 }
434
435                 om.imports.push(Import {
436                     name: ident.name,
437                     id: item.hir_id,
438                     vis: &item.vis,
439                     attrs: &item.attrs,
440                     path,
441                     glob: is_glob,
442                     whence: item.span,
443                 });
444             }
445             hir::ItemKind::Mod(ref m) => {
446                 om.mods.push(self.visit_mod_contents(item.span,
447                                                      &item.attrs,
448                                                      &item.vis,
449                                                      item.hir_id,
450                                                      m,
451                                                      Some(ident.name)));
452             },
453             hir::ItemKind::Enum(ref ed, ref gen) =>
454                 om.enums.push(self.visit_enum_def(item, ident.name, ed, gen)),
455             hir::ItemKind::Struct(ref sd, ref gen) =>
456                 om.structs.push(self.visit_variant_data(item, ident.name, sd, gen)),
457             hir::ItemKind::Union(ref sd, ref gen) =>
458                 om.unions.push(self.visit_union_data(item, ident.name, sd, gen)),
459             hir::ItemKind::Fn(ref fd, header, ref gen, body) =>
460                 self.visit_fn(om, item, ident.name, &**fd, header, gen, body),
461             hir::ItemKind::Ty(ref ty, ref gen) => {
462                 let t = Typedef {
463                     ty,
464                     gen,
465                     name: ident.name,
466                     id: item.hir_id,
467                     attrs: &item.attrs,
468                     whence: item.span,
469                     vis: &item.vis,
470                     stab: self.stability(item.hir_id),
471                     depr: self.deprecation(item.hir_id),
472                 };
473                 om.typedefs.push(t);
474             },
475             hir::ItemKind::Existential(ref exist_ty) => {
476                 let t = Existential {
477                     exist_ty,
478                     name: ident.name,
479                     id: item.hir_id,
480                     attrs: &item.attrs,
481                     whence: item.span,
482                     vis: &item.vis,
483                     stab: self.stability(item.hir_id),
484                     depr: self.deprecation(item.hir_id),
485                 };
486                 om.existentials.push(t);
487             },
488             hir::ItemKind::Static(ref type_, mutability, expr) => {
489                 let s = Static {
490                     type_,
491                     mutability,
492                     expr,
493                     id: item.hir_id,
494                     name: ident.name,
495                     attrs: &item.attrs,
496                     whence: item.span,
497                     vis: &item.vis,
498                     stab: self.stability(item.hir_id),
499                     depr: self.deprecation(item.hir_id),
500                 };
501                 om.statics.push(s);
502             },
503             hir::ItemKind::Const(ref type_, expr) => {
504                 let s = Constant {
505                     type_,
506                     expr,
507                     id: item.hir_id,
508                     name: ident.name,
509                     attrs: &item.attrs,
510                     whence: item.span,
511                     vis: &item.vis,
512                     stab: self.stability(item.hir_id),
513                     depr: self.deprecation(item.hir_id),
514                 };
515                 om.constants.push(s);
516             },
517             hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, ref item_ids) => {
518                 let items = item_ids.iter()
519                                     .map(|ti| self.cx.tcx.hir().trait_item(ti.id))
520                                     .collect();
521                 let t = Trait {
522                     is_auto,
523                     unsafety,
524                     name: ident.name,
525                     items,
526                     generics,
527                     bounds,
528                     id: item.hir_id,
529                     attrs: &item.attrs,
530                     whence: item.span,
531                     vis: &item.vis,
532                     stab: self.stability(item.hir_id),
533                     depr: self.deprecation(item.hir_id),
534                 };
535                 om.traits.push(t);
536             },
537             hir::ItemKind::TraitAlias(ref generics, ref bounds) => {
538                 let t = TraitAlias {
539                     name: ident.name,
540                     generics,
541                     bounds,
542                     id: item.hir_id,
543                     attrs: &item.attrs,
544                     whence: item.span,
545                     vis: &item.vis,
546                     stab: self.stability(item.hir_id),
547                     depr: self.deprecation(item.hir_id),
548                 };
549                 om.trait_aliases.push(t);
550             },
551
552             hir::ItemKind::Impl(unsafety,
553                           polarity,
554                           defaultness,
555                           ref generics,
556                           ref trait_,
557                           ref for_,
558                           ref item_ids) => {
559                 // Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
560                 // them up regardless of where they're located.
561                 if !self.inlining && trait_.is_none() {
562                     let items = item_ids.iter()
563                                         .map(|ii| self.cx.tcx.hir().impl_item(ii.id))
564                                         .collect();
565                     let i = Impl {
566                         unsafety,
567                         polarity,
568                         defaultness,
569                         generics,
570                         trait_,
571                         for_,
572                         items,
573                         attrs: &item.attrs,
574                         id: item.hir_id,
575                         whence: item.span,
576                         vis: &item.vis,
577                         stab: self.stability(item.hir_id),
578                         depr: self.deprecation(item.hir_id),
579                     };
580                     om.impls.push(i);
581                 }
582             },
583         }
584     }
585
586     fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem,
587                       renamed: Option<ast::Ident>, om: &mut Module<'tcx>) {
588         // If inlining we only want to include public functions.
589         if self.inlining && !item.vis.node.is_pub() {
590             return;
591         }
592
593         om.foreigns.push(ForeignItem {
594             id: item.hir_id,
595             name: renamed.unwrap_or(item.ident).name,
596             kind: &item.node,
597             vis: &item.vis,
598             stab: self.stability(item.hir_id),
599             depr: self.deprecation(item.hir_id),
600             attrs: &item.attrs,
601             whence: item.span
602         });
603     }
604
605     // Convert each `exported_macro` into a doc item.
606     fn visit_local_macro(
607         &self,
608         def: &'tcx hir::MacroDef,
609         renamed: Option<ast::Name>
610     ) -> Macro<'tcx> {
611         debug!("visit_local_macro: {}", def.name);
612         let tts = def.body.trees().collect::<Vec<_>>();
613         // Extract the spans of all matchers. They represent the "interface" of the macro.
614         let matchers = tts.chunks(4).map(|arm| arm[0].span()).collect();
615
616         Macro {
617
618             def_id: self.cx.tcx.hir().local_def_id(def.hir_id),
619             attrs: &def.attrs,
620             name: renamed.unwrap_or(def.name),
621             whence: def.span,
622             matchers,
623             stab: self.stability(def.hir_id),
624             depr: self.deprecation(def.hir_id),
625             imported_from: None,
626         }
627     }
628 }