]>
Commit | Line | Data |
---|---|---|
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 | ||
14 | use std::collections::HashSet; | |
9346a6ac | 15 | use std::mem; |
1a4d82fc JJ |
16 | |
17 | use syntax::abi; | |
18 | use syntax::ast; | |
b039eaaf SL |
19 | use syntax::attr; |
20 | use syntax::attr::AttrMetaMethods; | |
1a4d82fc | 21 | use syntax::codemap::Span; |
1a4d82fc | 22 | |
e9174d1e | 23 | use rustc::front::map as hir_map; |
1a4d82fc JJ |
24 | use rustc::middle::stability; |
25 | ||
e9174d1e SL |
26 | use rustc_front::hir; |
27 | ||
1a4d82fc JJ |
28 | use core; |
29 | use 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 | ||
39 | pub 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 | ||
48 | impl<'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 | } |