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