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