]> git.lizzy.rs Git - rust.git/blob - src/librustdoc/visit_ast.rs
Use ast attributes every where (remove HIR attributes).
[rust.git] / src / librustdoc / visit_ast.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 //! Rust AST Visitor. Extracts useful information and massages it into a form
12 //! usable for clean
13
14 use std::collections::HashSet;
15 use std::mem;
16
17 use syntax::abi;
18 use syntax::ast;
19 use syntax::attr;
20 use syntax::attr::AttrMetaMethods;
21 use syntax::codemap::Span;
22
23 use rustc::front::map as hir_map;
24 use rustc::middle::def_id::DefId;
25 use rustc::middle::stability;
26
27 use rustc_front::hir;
28
29 use core;
30 use doctree::*;
31
32 // looks to me like the first two of these are actually
33 // output parameters, maybe only mutated once; perhaps
34 // better simply to have the visit method return a tuple
35 // containing them?
36
37 // also, is there some reason that this doesn't use the 'visit'
38 // framework from syntax?
39
40 pub struct RustdocVisitor<'a, 'tcx: 'a> {
41     pub module: Module,
42     pub attrs: Vec<ast::Attribute>,
43     pub cx: &'a core::DocContext<'a, 'tcx>,
44     pub analysis: Option<&'a core::CrateAnalysis>,
45     view_item_stack: HashSet<ast::NodeId>,
46     inlining_from_glob: bool,
47 }
48
49 impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
50     pub fn new(cx: &'a core::DocContext<'a, 'tcx>,
51                analysis: Option<&'a core::CrateAnalysis>) -> RustdocVisitor<'a, 'tcx> {
52         // If the root is reexported, terminate all recursion.
53         let mut stack = HashSet::new();
54         stack.insert(ast::CRATE_NODE_ID);
55         RustdocVisitor {
56             module: Module::new(None),
57             attrs: Vec::new(),
58             cx: cx,
59             analysis: analysis,
60             view_item_stack: stack,
61             inlining_from_glob: false,
62         }
63     }
64
65     fn stability(&self, id: ast::NodeId) -> Option<attr::Stability> {
66         self.cx.tcx_opt().and_then(
67             |tcx| stability::lookup(tcx, DefId::local(id)).map(|x| x.clone()))
68     }
69
70     pub fn visit(&mut self, krate: &hir::Crate) {
71         self.attrs = krate.attrs.clone();
72
73         self.module = self.visit_mod_contents(krate.span,
74                                               krate.attrs.clone(),
75                                               hir::Public,
76                                               ast::CRATE_NODE_ID,
77                                               &krate.module,
78                                               None);
79         // attach the crate's exported macros to the top-level module:
80         self.module.macros = krate.exported_macros.iter()
81             .map(|def| self.visit_macro(def)).collect();
82         self.module.is_crate = true;
83     }
84
85     pub fn visit_struct_def(&mut self, item: &hir::Item,
86                             name: ast::Ident, sd: &hir::StructDef,
87                             generics: &hir::Generics) -> Struct {
88         debug!("Visiting struct");
89         let struct_type = struct_type_from_def(&*sd);
90         Struct {
91             id: item.id,
92             struct_type: struct_type,
93             name: name,
94             vis: item.vis,
95             stab: self.stability(item.id),
96             attrs: item.attrs.clone(),
97             generics: generics.clone(),
98             fields: sd.fields.clone(),
99             whence: item.span
100         }
101     }
102
103     pub fn visit_enum_def(&mut self, it: &hir::Item,
104                           name: ast::Ident, def: &hir::EnumDef,
105                           params: &hir::Generics) -> Enum {
106         debug!("Visiting enum");
107         Enum {
108             name: name,
109             variants: def.variants.iter().map(|v| Variant {
110                 name: v.node.name,
111                 attrs: v.node.attrs.clone(),
112                 vis: v.node.vis,
113                 stab: self.stability(v.node.id),
114                 id: v.node.id,
115                 kind: v.node.kind.clone(),
116                 whence: v.span,
117             }).collect(),
118             vis: it.vis,
119             stab: self.stability(it.id),
120             generics: params.clone(),
121             attrs: it.attrs.clone(),
122             id: it.id,
123             whence: it.span,
124         }
125     }
126
127     pub fn visit_fn(&mut self, item: &hir::Item,
128                     name: ast::Ident, fd: &hir::FnDecl,
129                     unsafety: &hir::Unsafety,
130                     constness: hir::Constness,
131                     abi: &abi::Abi,
132                     gen: &hir::Generics) -> Function {
133         debug!("Visiting fn");
134         Function {
135             id: item.id,
136             vis: item.vis,
137             stab: self.stability(item.id),
138             attrs: item.attrs.clone(),
139             decl: fd.clone(),
140             name: name,
141             whence: item.span,
142             generics: gen.clone(),
143             unsafety: *unsafety,
144             constness: constness,
145             abi: *abi,
146         }
147     }
148
149     pub fn visit_mod_contents(&mut self, span: Span, attrs: Vec<ast::Attribute> ,
150                               vis: hir::Visibility, id: ast::NodeId,
151                               m: &hir::Mod,
152                               name: Option<ast::Ident>) -> Module {
153         let mut om = Module::new(name);
154         om.where_outer = span;
155         om.where_inner = m.inner;
156         om.attrs = attrs;
157         om.vis = vis;
158         om.stab = self.stability(id);
159         om.id = id;
160         for i in &m.items {
161             self.visit_item(&**i, None, &mut om);
162         }
163         om
164     }
165
166     fn visit_view_path(&mut self, path: hir::ViewPath_,
167                        om: &mut Module,
168                        id: ast::NodeId,
169                        please_inline: bool) -> Option<hir::ViewPath_> {
170         match path {
171             hir::ViewPathSimple(dst, base) => {
172                 if self.resolve_id(id, Some(dst), false, om, please_inline) {
173                     None
174                 } else {
175                     Some(hir::ViewPathSimple(dst, base))
176                 }
177             }
178             hir::ViewPathList(p, paths) => {
179                 let mine = paths.into_iter().filter(|path| {
180                     !self.resolve_id(path.node.id(), None, false, om,
181                                      please_inline)
182                 }).collect::<Vec<hir::PathListItem>>();
183
184                 if mine.is_empty() {
185                     None
186                 } else {
187                     Some(hir::ViewPathList(p, mine))
188                 }
189             }
190
191             // these are feature gated anyway
192             hir::ViewPathGlob(base) => {
193                 if self.resolve_id(id, None, true, om, please_inline) {
194                     None
195                 } else {
196                     Some(hir::ViewPathGlob(base))
197                 }
198             }
199         }
200
201     }
202
203     fn resolve_id(&mut self, id: ast::NodeId, renamed: Option<ast::Ident>,
204                   glob: bool, om: &mut Module, please_inline: bool) -> bool {
205         let tcx = match self.cx.tcx_opt() {
206             Some(tcx) => tcx,
207             None => return false
208         };
209         let def = tcx.def_map.borrow()[&id].def_id();
210         if !def.is_local() { return false }
211         let analysis = match self.analysis {
212             Some(analysis) => analysis, None => return false
213         };
214         if !please_inline && analysis.public_items.contains(&def.node) {
215             return false
216         }
217         if !self.view_item_stack.insert(def.node) { return false }
218
219         let ret = match tcx.map.get(def.node) {
220             hir_map::NodeItem(it) => {
221                 if glob {
222                     let prev = mem::replace(&mut self.inlining_from_glob, true);
223                     match it.node {
224                         hir::ItemMod(ref m) => {
225                             for i in &m.items {
226                                 self.visit_item(&**i, None, om);
227                             }
228                         }
229                         hir::ItemEnum(..) => {}
230                         _ => { panic!("glob not mapped to a module or enum"); }
231                     }
232                     self.inlining_from_glob = prev;
233                 } else {
234                     self.visit_item(it, renamed, om);
235                 }
236                 true
237             }
238             _ => false,
239         };
240         self.view_item_stack.remove(&id);
241         return ret;
242     }
243
244     pub fn visit_item(&mut self, item: &hir::Item,
245                       renamed: Option<ast::Ident>, om: &mut Module) {
246         debug!("Visiting item {:?}", item);
247         let name = renamed.unwrap_or(item.ident);
248         match item.node {
249             hir::ItemExternCrate(ref p) => {
250                 let path = match *p {
251                     None => None,
252                     Some(x) => Some(x.to_string()),
253                 };
254                 om.extern_crates.push(ExternCrate {
255                     name: name,
256                     path: path,
257                     vis: item.vis,
258                     attrs: item.attrs.clone(),
259                     whence: item.span,
260                 })
261             }
262             hir::ItemUse(ref vpath) => {
263                 let node = vpath.node.clone();
264                 let node = if item.vis == hir::Public {
265                     let please_inline = item.attrs.iter().any(|item| {
266                         match item.meta_item_list() {
267                             Some(list) => {
268                                 list.iter().any(|i| &i.name()[..] == "inline")
269                             }
270                             None => false,
271                         }
272                     });
273                     match self.visit_view_path(node, om, item.id, please_inline) {
274                         None => return,
275                         Some(p) => p
276                     }
277                 } else {
278                     node
279                 };
280                 om.imports.push(Import {
281                     id: item.id,
282                     vis: item.vis,
283                     attrs: item.attrs.clone(),
284                     node: node,
285                     whence: item.span,
286                 });
287             }
288             hir::ItemMod(ref m) => {
289                 om.mods.push(self.visit_mod_contents(item.span,
290                                                      item.attrs.clone(),
291                                                      item.vis,
292                                                      item.id,
293                                                      m,
294                                                      Some(name)));
295             },
296             hir::ItemEnum(ref ed, ref gen) =>
297                 om.enums.push(self.visit_enum_def(item, name, ed, gen)),
298             hir::ItemStruct(ref sd, ref gen) =>
299                 om.structs.push(self.visit_struct_def(item, name, &**sd, gen)),
300             hir::ItemFn(ref fd, ref unsafety, constness, ref abi, ref gen, _) =>
301                 om.fns.push(self.visit_fn(item, name, &**fd, unsafety,
302                                           constness, abi, gen)),
303             hir::ItemTy(ref ty, ref gen) => {
304                 let t = Typedef {
305                     ty: ty.clone(),
306                     gen: gen.clone(),
307                     name: name,
308                     id: item.id,
309                     attrs: item.attrs.clone(),
310                     whence: item.span,
311                     vis: item.vis,
312                     stab: self.stability(item.id),
313                 };
314                 om.typedefs.push(t);
315             },
316             hir::ItemStatic(ref ty, ref mut_, ref exp) => {
317                 let s = Static {
318                     type_: ty.clone(),
319                     mutability: mut_.clone(),
320                     expr: exp.clone(),
321                     id: item.id,
322                     name: name,
323                     attrs: item.attrs.clone(),
324                     whence: item.span,
325                     vis: item.vis,
326                     stab: self.stability(item.id),
327                 };
328                 om.statics.push(s);
329             },
330             hir::ItemConst(ref ty, ref exp) => {
331                 let s = Constant {
332                     type_: ty.clone(),
333                     expr: exp.clone(),
334                     id: item.id,
335                     name: name,
336                     attrs: item.attrs.clone(),
337                     whence: item.span,
338                     vis: item.vis,
339                     stab: self.stability(item.id),
340                 };
341                 om.constants.push(s);
342             },
343             hir::ItemTrait(unsafety, ref gen, ref b, ref items) => {
344                 let t = Trait {
345                     unsafety: unsafety,
346                     name: name,
347                     items: items.clone(),
348                     generics: gen.clone(),
349                     bounds: b.iter().cloned().collect(),
350                     id: item.id,
351                     attrs: item.attrs.clone(),
352                     whence: item.span,
353                     vis: item.vis,
354                     stab: self.stability(item.id),
355                 };
356                 om.traits.push(t);
357             },
358             hir::ItemImpl(unsafety, polarity, ref gen, ref tr, ref ty, ref items) => {
359                 let i = Impl {
360                     unsafety: unsafety,
361                     polarity: polarity,
362                     generics: gen.clone(),
363                     trait_: tr.clone(),
364                     for_: ty.clone(),
365                     items: items.clone(),
366                     attrs: item.attrs.clone(),
367                     id: item.id,
368                     whence: item.span,
369                     vis: item.vis,
370                     stab: self.stability(item.id),
371                 };
372                 // Don't duplicate impls when inlining glob imports, we'll pick
373                 // them up regardless of where they're located.
374                 if !self.inlining_from_glob {
375                     om.impls.push(i);
376                 }
377             },
378             hir::ItemDefaultImpl(unsafety, ref trait_ref) => {
379                 let i = DefaultImpl {
380                     unsafety: unsafety,
381                     trait_: trait_ref.clone(),
382                     id: item.id,
383                     attrs: item.attrs.clone(),
384                     whence: item.span,
385                 };
386                 // see comment above about ItemImpl
387                 if !self.inlining_from_glob {
388                     om.def_traits.push(i);
389                 }
390             }
391             hir::ItemForeignMod(ref fm) => {
392                 om.foreigns.push(fm.clone());
393             }
394         }
395     }
396
397     // convert each exported_macro into a doc item
398     fn visit_macro(&self, def: &hir::MacroDef) -> Macro {
399         Macro {
400             id: def.id,
401             attrs: def.attrs.clone(),
402             name: def.ident,
403             whence: def.span,
404             stab: self.stability(def.id),
405             imported_from: def.imported_from,
406         }
407     }
408 }