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