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