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.
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.
11 //! Rust AST Visitor. Extracts useful information and massages it into a form
14 use std::collections::HashSet;
21 use syntax::attr::AttrMetaMethods;
22 use syntax::codemap::Span;
25 use rustc::middle::stability;
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
35 // also, is there some reason that this doesn't use the 'visit'
36 // framework from syntax?
38 pub struct RustdocVisitor<'a, 'tcx: 'a> {
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>,
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);
53 module: Module::new(None),
57 view_item_stack: stack,
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)))
65 pub fn visit(&mut self, krate: &ast::Crate) {
66 self.attrs = krate.attrs.clone();
68 self.module = self.visit_mod_contents(krate.span,
74 // attach the crate's exported macros to the top-level module:
75 self.module.macros = krate.exported_macros.iter()
76 .map(|def| self.visit_macro(def)).collect();
77 self.module.is_crate = true;
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);
87 struct_type: struct_type,
90 stab: self.stability(item.id),
91 attrs: item.attrs.clone(),
92 generics: generics.clone(),
93 fields: sd.fields.clone(),
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");
104 variants: def.variants.iter().map(|v| Variant {
106 attrs: v.node.attrs.clone(),
108 stab: self.stability(v.node.id),
110 kind: v.node.kind.clone(),
114 stab: self.stability(it.id),
115 generics: params.clone(),
116 attrs: it.attrs.clone(),
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");
130 stab: self.stability(item.id),
131 attrs: item.attrs.clone(),
135 generics: gen.clone(),
140 pub fn visit_mod_contents(&mut self, span: Span, attrs: Vec<ast::Attribute> ,
141 vis: ast::Visibility, id: ast::NodeId,
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);
148 om.where_outer = span;
149 om.where_inner = m.inner;
152 om.stab = self.stability(id);
154 for i in m.items.iter() {
155 self.visit_item(&**i, None, &mut om);
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());
164 let please_inline = item.attrs.iter().any(|item| {
165 match item.meta_item_list() {
167 list.iter().any(|i| i.name().get() == "inline")
172 let item = match item.node {
173 ast::ViewItemUse(ref vpath) => {
174 match self.visit_view_path(&**vpath, om, please_inline) {
178 node: ast::ViewItemUse(path),
184 ast::ViewItemExternCrate(..) => item.clone()
186 om.view_items.push(item);
189 fn visit_view_path(&mut self, path: &ast::ViewPath,
191 please_inline: bool) -> Option<P<ast::ViewPath>> {
193 ast::ViewPathSimple(dst, _, id) => {
194 if self.resolve_id(id, Some(dst), false, om, please_inline) {
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,
203 mine.push(path.clone());
207 if mine.len() == 0 { return None }
208 return Some(P(::syntax::codemap::Spanned {
209 node: ast::ViewPathList(p.clone(), mine, b.clone()),
214 // these are feature gated anyway
215 ast::ViewPathGlob(_, id) => {
216 if self.resolve_id(id, None, true, om, please_inline) {
221 Some(P(path.clone()))
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() {
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
235 if !please_inline && analysis.public_items.contains(&def.node) {
238 if !self.view_item_stack.insert(def.node) { return false }
240 let ret = match tcx.map.get(def.node) {
241 ast_map::NodeItem(it) => {
244 ast::ItemMod(ref m) => {
245 for vi in m.view_items.iter() {
246 self.visit_view_item(vi, om);
248 for i in m.items.iter() {
249 self.visit_item(&**i, None, om);
252 ast::ItemEnum(..) => {}
253 _ => { panic!("glob not mapped to a module or enum"); }
256 self.visit_item(it, renamed, om);
262 self.view_item_stack.remove(&id);
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);
271 ast::ItemMod(ref m) => {
272 om.mods.push(self.visit_mod_contents(item.span,
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) => {
291 attrs: item.attrs.clone(),
294 stab: self.stability(item.id),
298 ast::ItemStatic(ref ty, ref mut_, ref exp) => {
301 mutability: mut_.clone(),
305 attrs: item.attrs.clone(),
308 stab: self.stability(item.id),
312 ast::ItemConst(ref ty, ref exp) => {
318 attrs: item.attrs.clone(),
321 stab: self.stability(item.id),
323 om.constants.push(s);
325 ast::ItemTrait(unsafety, ref gen, ref b, ref items) => {
329 items: items.clone(),
330 generics: gen.clone(),
331 bounds: b.iter().map(|x| (*x).clone()).collect(),
333 attrs: item.attrs.clone(),
336 stab: self.stability(item.id),
340 ast::ItemImpl(unsafety, polarity, ref gen, ref tr, ref ty, ref items) => {
344 generics: gen.clone(),
347 items: items.clone(),
348 attrs: item.attrs.clone(),
352 stab: self.stability(item.id),
356 ast::ItemForeignMod(ref fm) => {
357 om.foreigns.push(fm.clone());
360 panic!("rustdoc: macros should be gone, after expansion");
365 // convert each exported_macro into a doc item
366 fn visit_macro(&self, def: &ast::MacroDef) -> Macro {
369 attrs: def.attrs.clone(),
372 stab: self.stability(def.id),