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
;
20 use syntax
::attr
::AttrMetaMethods
;
21 use syntax
::codemap
::Span
;
23 use rustc
::front
::map
as hir_map
;
24 use rustc
::middle
::stability
;
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
36 // also, is there some reason that this doesn't use the 'visit'
37 // framework from syntax?
39 pub struct RustdocVisitor
<'a
, 'tcx
: 'a
> {
41 pub attrs
: Vec
<ast
::Attribute
>,
42 pub cx
: &'a core
::DocContext
<'a
, 'tcx
>,
43 pub analysis
: Option
<&'a core
::CrateAnalysis
>,
44 view_item_stack
: HashSet
<ast
::NodeId
>,
45 inlining_from_glob
: bool
,
48 impl<'a
, 'tcx
> RustdocVisitor
<'a
, 'tcx
> {
49 pub fn new(cx
: &'a core
::DocContext
<'a
, 'tcx
>,
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
);
55 module
: Module
::new(None
),
59 view_item_stack
: stack
,
60 inlining_from_glob
: false,
64 fn stability(&self, id
: ast
::NodeId
) -> Option
<attr
::Stability
> {
65 self.cx
.tcx_opt().and_then(|tcx
| {
66 self.cx
.map
.opt_local_def_id(id
)
67 .and_then(|def_id
| stability
::lookup(tcx
, def_id
))
72 pub fn visit(&mut self, krate
: &hir
::Crate
) {
73 self.attrs
= krate
.attrs
.clone();
75 self.module
= self.visit_mod_contents(krate
.span
,
81 // attach the crate's exported macros to the top-level module:
82 self.module
.macros
= krate
.exported_macros
.iter()
83 .map(|def
| self.visit_macro(def
)).collect();
84 self.module
.is_crate
= true;
87 pub fn visit_variant_data(&mut self, item
: &hir
::Item
,
88 name
: ast
::Name
, sd
: &hir
::VariantData
,
89 generics
: &hir
::Generics
) -> Struct
{
90 debug
!("Visiting struct");
91 let struct_type
= struct_type_from_def(&*sd
);
94 struct_type
: struct_type
,
97 stab
: self.stability(item
.id
),
98 attrs
: item
.attrs
.clone(),
99 generics
: generics
.clone(),
100 fields
: sd
.fields().iter().cloned().collect(),
105 pub fn visit_enum_def(&mut self, it
: &hir
::Item
,
106 name
: ast
::Name
, def
: &hir
::EnumDef
,
107 params
: &hir
::Generics
) -> Enum
{
108 debug
!("Visiting enum");
111 variants
: def
.variants
.iter().map(|v
| Variant
{
113 attrs
: v
.node
.attrs
.clone(),
114 stab
: self.stability(v
.node
.data
.id()),
115 def
: v
.node
.data
.clone(),
119 stab
: self.stability(it
.id
),
120 generics
: params
.clone(),
121 attrs
: it
.attrs
.clone(),
127 pub fn visit_fn(&mut self, item
: &hir
::Item
,
128 name
: ast
::Name
, fd
: &hir
::FnDecl
,
129 unsafety
: &hir
::Unsafety
,
130 constness
: hir
::Constness
,
132 gen
: &hir
::Generics
) -> Function
{
133 debug
!("Visiting fn");
137 stab
: self.stability(item
.id
),
138 attrs
: item
.attrs
.clone(),
142 generics
: gen
.clone(),
144 constness
: constness
,
149 pub fn visit_mod_contents(&mut self, span
: Span
, attrs
: Vec
<ast
::Attribute
> ,
150 vis
: hir
::Visibility
, id
: ast
::NodeId
,
152 name
: Option
<ast
::Name
>) -> Module
{
153 let mut om
= Module
::new(name
);
154 om
.where_outer
= span
;
155 om
.where_inner
= m
.inner
;
158 om
.stab
= self.stability(id
);
160 for i
in &m
.item_ids
{
161 let item
= self.cx
.map
.expect_item(i
.id
);
162 self.visit_item(item
, None
, &mut om
);
167 fn visit_view_path(&mut self, path
: hir
::ViewPath_
,
170 please_inline
: bool
) -> Option
<hir
::ViewPath_
> {
172 hir
::ViewPathSimple(dst
, base
) => {
173 if self.resolve_id(id
, Some(dst
), false, om
, please_inline
) {
176 Some(hir
::ViewPathSimple(dst
, base
))
179 hir
::ViewPathList(p
, paths
) => {
180 let mine
= paths
.into_iter().filter(|path
| {
181 !self.resolve_id(path
.node
.id(), None
, false, om
,
183 }).collect
::<Vec
<hir
::PathListItem
>>();
188 Some(hir
::ViewPathList(p
, mine
))
192 // these are feature gated anyway
193 hir
::ViewPathGlob(base
) => {
194 if self.resolve_id(id
, None
, true, om
, please_inline
) {
197 Some(hir
::ViewPathGlob(base
))
204 fn resolve_id(&mut self, id
: ast
::NodeId
, renamed
: Option
<ast
::Name
>,
205 glob
: bool
, om
: &mut Module
, please_inline
: bool
) -> bool
{
206 let tcx
= match self.cx
.tcx_opt() {
210 let def
= tcx
.def_map
.borrow()[&id
].def_id();
211 let def_node_id
= match tcx
.map
.as_local_node_id(def
) {
212 Some(n
) => n
, None
=> return false
214 let analysis
= match self.analysis
{
215 Some(analysis
) => analysis
, None
=> return false
217 if !please_inline
&& analysis
.access_levels
.is_public(def
) {
220 if !self.view_item_stack
.insert(def_node_id
) { return false }
222 let ret
= match tcx
.map
.get(def_node_id
) {
223 hir_map
::NodeItem(it
) => {
225 let prev
= mem
::replace(&mut self.inlining_from_glob
, true);
227 hir
::ItemMod(ref m
) => {
228 for i
in &m
.item_ids
{
229 let i
= self.cx
.map
.expect_item(i
.id
);
230 self.visit_item(i
, None
, om
);
233 hir
::ItemEnum(..) => {}
234 _
=> { panic!("glob not mapped to a module or enum"); }
236 self.inlining_from_glob
= prev
;
238 self.visit_item(it
, renamed
, om
);
244 self.view_item_stack
.remove(&def_node_id
);
248 pub fn visit_item(&mut self, item
: &hir
::Item
,
249 renamed
: Option
<ast
::Name
>, om
: &mut Module
) {
250 debug
!("Visiting item {:?}", item
);
251 let name
= renamed
.unwrap_or(item
.name
);
253 hir
::ItemExternCrate(ref p
) => {
254 let path
= match *p
{
256 Some(x
) => Some(x
.to_string()),
258 om
.extern_crates
.push(ExternCrate
{
262 attrs
: item
.attrs
.clone(),
266 hir
::ItemUse(ref vpath
) => {
267 let node
= vpath
.node
.clone();
268 let node
= if item
.vis
== hir
::Public
{
269 let please_inline
= item
.attrs
.iter().any(|item
| {
270 match item
.meta_item_list() {
272 list
.iter().any(|i
| &i
.name()[..] == "inline")
277 match self.visit_view_path(node
, om
, item
.id
, please_inline
) {
284 om
.imports
.push(Import
{
287 attrs
: item
.attrs
.clone(),
292 hir
::ItemMod(ref m
) => {
293 om
.mods
.push(self.visit_mod_contents(item
.span
,
300 hir
::ItemEnum(ref ed
, ref gen
) =>
301 om
.enums
.push(self.visit_enum_def(item
, name
, ed
, gen
)),
302 hir
::ItemStruct(ref sd
, ref gen
) =>
303 om
.structs
.push(self.visit_variant_data(item
, name
, sd
, gen
)),
304 hir
::ItemFn(ref fd
, ref unsafety
, constness
, ref abi
, ref gen
, _
) =>
305 om
.fns
.push(self.visit_fn(item
, name
, &**fd
, unsafety
,
306 constness
, abi
, gen
)),
307 hir
::ItemTy(ref ty
, ref gen
) => {
313 attrs
: item
.attrs
.clone(),
316 stab
: self.stability(item
.id
),
320 hir
::ItemStatic(ref ty
, ref mut_
, ref exp
) => {
323 mutability
: mut_
.clone(),
327 attrs
: item
.attrs
.clone(),
330 stab
: self.stability(item
.id
),
334 hir
::ItemConst(ref ty
, ref exp
) => {
340 attrs
: item
.attrs
.clone(),
343 stab
: self.stability(item
.id
),
345 om
.constants
.push(s
);
347 hir
::ItemTrait(unsafety
, ref gen
, ref b
, ref items
) => {
351 items
: items
.clone(),
352 generics
: gen
.clone(),
353 bounds
: b
.iter().cloned().collect(),
355 attrs
: item
.attrs
.clone(),
358 stab
: self.stability(item
.id
),
362 hir
::ItemImpl(unsafety
, polarity
, ref gen
, ref tr
, ref ty
, ref items
) => {
366 generics
: gen
.clone(),
369 items
: items
.clone(),
370 attrs
: item
.attrs
.clone(),
374 stab
: self.stability(item
.id
),
376 // Don't duplicate impls when inlining glob imports, we'll pick
377 // them up regardless of where they're located.
378 if !self.inlining_from_glob
{
382 hir
::ItemDefaultImpl(unsafety
, ref trait_ref
) => {
383 let i
= DefaultImpl
{
385 trait_
: trait_ref
.clone(),
387 attrs
: item
.attrs
.clone(),
390 // see comment above about ItemImpl
391 if !self.inlining_from_glob
{
392 om
.def_traits
.push(i
);
395 hir
::ItemForeignMod(ref fm
) => {
396 om
.foreigns
.push(fm
.clone());
401 // convert each exported_macro into a doc item
402 fn visit_macro(&self, def
: &hir
::MacroDef
) -> Macro
{
403 // Extract the spans of all matchers. They represent the "interface" of the macro.
404 let matchers
= def
.body
.chunks(4).map(|arm
| arm
[0].get_span()).collect();
408 attrs
: def
.attrs
.clone(),
412 stab
: self.stability(def
.id
),
413 imported_from
: def
.imported_from
,