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
18 use syntax
::ext
::base
::MacroKind
;
19 use syntax
::source_map
::Spanned
;
20 use syntax_pos
::{self, Span}
;
23 use rustc
::hir
::def
::Def
;
24 use rustc
::hir
::def_id
::{DefId, LOCAL_CRATE}
;
25 use rustc
::middle
::privacy
::AccessLevel
;
26 use rustc
::util
::nodemap
::{FxHashSet, FxHashMap}
;
31 use clean
::{self, AttributesExt, NestedAttributesExt, def_id_to_path}
;
34 // looks to me like the first two of these are actually
35 // output parameters, maybe only mutated once; perhaps
36 // better simply to have the visit method return a tuple
39 // also, is there some reason that this doesn't use the 'visit'
40 // framework from syntax?
42 pub struct RustdocVisitor
<'a
, 'tcx
: 'a
, 'rcx
: 'a
, 'cstore
: 'rcx
> {
44 pub attrs
: hir
::HirVec
<ast
::Attribute
>,
45 pub cx
: &'a core
::DocContext
<'a
, 'tcx
, 'rcx
, 'cstore
>,
46 view_item_stack
: FxHashSet
<ast
::NodeId
>,
48 /// Is the current module and all of its parents public?
49 inside_public_path
: bool
,
50 exact_paths
: Option
<FxHashMap
<DefId
, Vec
<String
>>>,
53 impl<'a
, 'tcx
, 'rcx
, 'cstore
> RustdocVisitor
<'a
, 'tcx
, 'rcx
, 'cstore
> {
55 cx
: &'a core
::DocContext
<'a
, 'tcx
, 'rcx
, 'cstore
>
56 ) -> RustdocVisitor
<'a
, 'tcx
, 'rcx
, 'cstore
> {
57 // If the root is re-exported, terminate all recursion.
58 let mut stack
= FxHashSet
::default();
59 stack
.insert(ast
::CRATE_NODE_ID
);
61 module
: Module
::new(None
),
62 attrs
: hir
::HirVec
::new(),
64 view_item_stack
: stack
,
66 inside_public_path
: true,
67 exact_paths
: Some(FxHashMap
::default()),
71 fn store_path(&mut self, did
: DefId
) {
72 // We can't use the entry api, as that keeps the mutable borrow of self active
73 // when we try to use cx
74 let exact_paths
= self.exact_paths
.as_mut().unwrap();
75 if exact_paths
.get(&did
).is_none() {
76 let path
= def_id_to_path(self.cx
, did
, self.cx
.crate_name
.clone());
77 exact_paths
.insert(did
, path
);
81 fn stability(&self, id
: ast
::NodeId
) -> Option
<attr
::Stability
> {
82 self.cx
.tcx
.hir
.opt_local_def_id(id
)
83 .and_then(|def_id
| self.cx
.tcx
.lookup_stability(def_id
)).cloned()
86 fn deprecation(&self, id
: ast
::NodeId
) -> Option
<attr
::Deprecation
> {
87 self.cx
.tcx
.hir
.opt_local_def_id(id
)
88 .and_then(|def_id
| self.cx
.tcx
.lookup_deprecation(def_id
))
91 pub fn visit(&mut self, krate
: &hir
::Crate
) {
92 self.attrs
= krate
.attrs
.clone();
94 self.module
= self.visit_mod_contents(krate
.span
,
96 Spanned
{ span
: syntax_pos
::DUMMY_SP
,
97 node
: hir
::VisibilityKind
::Public
},
101 // attach the crate's exported macros to the top-level module:
102 let macro_exports
: Vec
<_
> =
103 krate
.exported_macros
.iter().map(|def
| self.visit_local_macro(def
)).collect();
104 self.module
.macros
.extend(macro_exports
);
105 self.module
.is_crate
= true;
107 self.cx
.renderinfo
.borrow_mut().exact_paths
= self.exact_paths
.take().unwrap();
110 pub fn visit_variant_data(&mut self, item
: &hir
::Item
,
111 name
: ast
::Name
, sd
: &hir
::VariantData
,
112 generics
: &hir
::Generics
) -> Struct
{
113 debug
!("Visiting struct");
114 let struct_type
= struct_type_from_def(&*sd
);
119 vis
: item
.vis
.clone(),
120 stab
: self.stability(item
.id
),
121 depr
: self.deprecation(item
.id
),
122 attrs
: item
.attrs
.clone(),
123 generics
: generics
.clone(),
124 fields
: sd
.fields().iter().cloned().collect(),
129 pub fn visit_union_data(&mut self, item
: &hir
::Item
,
130 name
: ast
::Name
, sd
: &hir
::VariantData
,
131 generics
: &hir
::Generics
) -> Union
{
132 debug
!("Visiting union");
133 let struct_type
= struct_type_from_def(&*sd
);
138 vis
: item
.vis
.clone(),
139 stab
: self.stability(item
.id
),
140 depr
: self.deprecation(item
.id
),
141 attrs
: item
.attrs
.clone(),
142 generics
: generics
.clone(),
143 fields
: sd
.fields().iter().cloned().collect(),
148 pub fn visit_enum_def(&mut self, it
: &hir
::Item
,
149 name
: ast
::Name
, def
: &hir
::EnumDef
,
150 params
: &hir
::Generics
) -> Enum
{
151 debug
!("Visiting enum");
154 variants
: def
.variants
.iter().map(|v
| Variant
{
156 attrs
: v
.node
.attrs
.clone(),
157 stab
: self.stability(v
.node
.data
.id()),
158 depr
: self.deprecation(v
.node
.data
.id()),
159 def
: v
.node
.data
.clone(),
163 stab
: self.stability(it
.id
),
164 depr
: self.deprecation(it
.id
),
165 generics
: params
.clone(),
166 attrs
: it
.attrs
.clone(),
172 pub fn visit_fn(&mut self, om
: &mut Module
, item
: &hir
::Item
,
173 name
: ast
::Name
, fd
: &hir
::FnDecl
,
174 header
: hir
::FnHeader
,
177 debug
!("Visiting fn");
178 let macro_kind
= item
.attrs
.iter().filter_map(|a
| {
179 if a
.check_name("proc_macro") {
180 Some(MacroKind
::Bang
)
181 } else if a
.check_name("proc_macro_derive") {
182 Some(MacroKind
::Derive
)
183 } else if a
.check_name("proc_macro_attribute") {
184 Some(MacroKind
::Attr
)
191 let name
= if kind
== MacroKind
::Derive
{
192 item
.attrs
.lists("proc_macro_derive")
193 .filter_map(|mi
| mi
.name())
195 .expect("proc-macro derives require a name")
200 let mut helpers
= Vec
::new();
201 for mi
in item
.attrs
.lists("proc_macro_derive") {
202 if !mi
.check_name("attributes") {
206 if let Some(list
) = mi
.meta_item_list() {
207 for inner_mi
in list
{
208 if let Some(name
) = inner_mi
.name() {
215 om
.proc_macros
.push(ProcMacro
{
220 attrs
: item
.attrs
.clone(),
222 stab
: self.stability(item
.id
),
223 depr
: self.deprecation(item
.id
),
227 om
.fns
.push(Function
{
229 vis
: item
.vis
.clone(),
230 stab
: self.stability(item
.id
),
231 depr
: self.deprecation(item
.id
),
232 attrs
: item
.attrs
.clone(),
236 generics
: gen
.clone(),
244 pub fn visit_mod_contents(&mut self, span
: Span
, attrs
: hir
::HirVec
<ast
::Attribute
>,
245 vis
: hir
::Visibility
, id
: ast
::NodeId
,
247 name
: Option
<ast
::Name
>) -> Module
{
248 let mut om
= Module
::new(name
);
249 om
.where_outer
= span
;
250 om
.where_inner
= m
.inner
;
252 om
.vis
= vis
.clone();
253 om
.stab
= self.stability(id
);
254 om
.depr
= self.deprecation(id
);
256 // Keep track of if there were any private modules in the path.
257 let orig_inside_public_path
= self.inside_public_path
;
258 self.inside_public_path
&= vis
.node
.is_pub();
259 for i
in &m
.item_ids
{
260 let item
= self.cx
.tcx
.hir
.expect_item(i
.id
);
261 self.visit_item(item
, None
, &mut om
);
263 self.inside_public_path
= orig_inside_public_path
;
267 /// Tries to resolve the target of a `pub use` statement and inlines the
268 /// target if it is defined locally and would not be documented otherwise,
269 /// or when it is specifically requested with `please_inline`.
270 /// (the latter is the case when the import is marked `doc(inline)`)
272 /// Cross-crate inlining occurs later on during crate cleaning
273 /// and follows different rules.
275 /// Returns true if the target has been inlined.
276 fn maybe_inline_local(&mut self,
279 renamed
: Option
<ast
::Name
>,
282 please_inline
: bool
) -> bool
{
284 fn inherits_doc_hidden(cx
: &core
::DocContext
, mut node
: ast
::NodeId
) -> bool
{
285 while let Some(id
) = cx
.tcx
.hir
.get_enclosing_scope(node
) {
287 if cx
.tcx
.hir
.attrs(node
).lists("doc").has_word("hidden") {
290 if node
== ast
::CRATE_NODE_ID
{
297 debug
!("maybe_inline_local def: {:?}", def
);
299 let tcx
= self.cx
.tcx
;
303 let def_did
= def
.def_id();
305 let use_attrs
= tcx
.hir
.attrs(id
);
306 // Don't inline doc(hidden) imports so they can be stripped at a later stage.
307 let is_no_inline
= use_attrs
.lists("doc").has_word("no_inline") ||
308 use_attrs
.lists("doc").has_word("hidden");
310 // For cross-crate impl inlining we need to know whether items are
311 // reachable in documentation - a previously nonreachable item can be
312 // made reachable by cross-crate inlining which we're checking here.
313 // (this is done here because we need to know this upfront)
314 if !def_did
.is_local() && !is_no_inline
{
315 let attrs
= clean
::inline
::load_attrs(self.cx
, def_did
);
316 let self_is_hidden
= attrs
.lists("doc").has_word("hidden");
322 Def
::ForeignTy(did
) |
323 Def
::TyAlias(did
) if !self_is_hidden
=> {
327 .insert(did
, AccessLevel
::Public
);
329 Def
::Mod(did
) => if !self_is_hidden
{
330 ::visit_lib
::LibEmbargoVisitor
::new(self.cx
).visit_mod(did
);
338 let def_node_id
= match tcx
.hir
.as_local_node_id(def_did
) {
339 Some(n
) => n
, None
=> return false
342 let is_private
= !self.cx
.renderinfo
.borrow().access_levels
.is_public(def_did
);
343 let is_hidden
= inherits_doc_hidden(self.cx
, def_node_id
);
345 // Only inline if requested or if the item would otherwise be stripped
346 if (!please_inline
&& !is_private
&& !is_hidden
) || is_no_inline
{
350 if !self.view_item_stack
.insert(def_node_id
) { return false }
352 let ret
= match tcx
.hir
.get(def_node_id
) {
353 Node
::Item(&hir
::Item { node: hir::ItemKind::Mod(ref m), .. }
) if glob
=> {
354 let prev
= mem
::replace(&mut self.inlining
, true);
355 for i
in &m
.item_ids
{
356 let i
= self.cx
.tcx
.hir
.expect_item(i
.id
);
357 self.visit_item(i
, None
, om
);
359 self.inlining
= prev
;
362 Node
::Item(it
) if !glob
=> {
363 let prev
= mem
::replace(&mut self.inlining
, true);
364 self.visit_item(it
, renamed
, om
);
365 self.inlining
= prev
;
368 Node
::ForeignItem(it
) if !glob
=> {
369 // generate a fresh `extern {}` block if we want to inline a foreign item.
370 om
.foreigns
.push(hir
::ForeignMod
{
371 abi
: tcx
.hir
.get_foreign_abi(it
.id
),
372 items
: vec
![hir
::ForeignItem
{
373 name
: renamed
.unwrap_or(it
.name
),
381 self.view_item_stack
.remove(&def_node_id
);
385 pub fn visit_item(&mut self, item
: &hir
::Item
,
386 renamed
: Option
<ast
::Name
>, om
: &mut Module
) {
387 debug
!("Visiting item {:?}", item
);
388 let name
= renamed
.unwrap_or(item
.name
);
390 if item
.vis
.node
.is_pub() {
391 let def_id
= self.cx
.tcx
.hir
.local_def_id(item
.id
);
392 self.store_path(def_id
);
396 hir
::ItemKind
::ForeignMod(ref fm
) => {
397 // If inlining we only want to include public functions.
398 om
.foreigns
.push(if self.inlining
{
401 items
: fm
.items
.iter().filter(|i
| i
.vis
.node
.is_pub()).cloned().collect(),
407 // If we're inlining, skip private items.
408 _
if self.inlining
&& !item
.vis
.node
.is_pub() => {}
409 hir
::ItemKind
::GlobalAsm(..) => {}
410 hir
::ItemKind
::ExternCrate(orig_name
) => {
411 let def_id
= self.cx
.tcx
.hir
.local_def_id(item
.id
);
412 om
.extern_crates
.push(ExternCrate
{
413 cnum
: self.cx
.tcx
.extern_mod_stmt_cnum(def_id
)
414 .unwrap_or(LOCAL_CRATE
),
416 path
: orig_name
.map(|x
|x
.to_string()),
417 vis
: item
.vis
.clone(),
418 attrs
: item
.attrs
.clone(),
422 hir
::ItemKind
::Use(_
, hir
::UseKind
::ListStem
) => {}
423 hir
::ItemKind
::Use(ref path
, kind
) => {
424 let is_glob
= kind
== hir
::UseKind
::Glob
;
426 // struct and variant constructors always show up alongside their definitions, we've
427 // already processed them so just discard these.
429 Def
::StructCtor(..) | Def
::VariantCtor(..) | Def
::SelfCtor(..) => return,
433 // If there was a private module in the current path then don't bother inlining
434 // anything as it will probably be stripped anyway.
435 if item
.vis
.node
.is_pub() && self.inside_public_path
{
436 let please_inline
= item
.attrs
.iter().any(|item
| {
437 match item
.meta_item_list() {
438 Some(ref list
) if item
.check_name("doc") => {
439 list
.iter().any(|i
| i
.check_name("inline"))
444 let name
= if is_glob { None }
else { Some(name) }
;
445 if self.maybe_inline_local(item
.id
,
455 om
.imports
.push(Import
{
458 vis
: item
.vis
.clone(),
459 attrs
: item
.attrs
.clone(),
460 path
: (**path
).clone(),
465 hir
::ItemKind
::Mod(ref m
) => {
466 om
.mods
.push(self.visit_mod_contents(item
.span
,
473 hir
::ItemKind
::Enum(ref ed
, ref gen
) =>
474 om
.enums
.push(self.visit_enum_def(item
, name
, ed
, gen
)),
475 hir
::ItemKind
::Struct(ref sd
, ref gen
) =>
476 om
.structs
.push(self.visit_variant_data(item
, name
, sd
, gen
)),
477 hir
::ItemKind
::Union(ref sd
, ref gen
) =>
478 om
.unions
.push(self.visit_union_data(item
, name
, sd
, gen
)),
479 hir
::ItemKind
::Fn(ref fd
, header
, ref gen
, body
) =>
480 self.visit_fn(om
, item
, name
, &**fd
, header
, gen
, body
),
481 hir
::ItemKind
::Ty(ref ty
, ref gen
) => {
487 attrs
: item
.attrs
.clone(),
489 vis
: item
.vis
.clone(),
490 stab
: self.stability(item
.id
),
491 depr
: self.deprecation(item
.id
),
495 hir
::ItemKind
::Existential(ref exist_ty
) => {
496 let t
= Existential
{
497 exist_ty
: exist_ty
.clone(),
500 attrs
: item
.attrs
.clone(),
502 vis
: item
.vis
.clone(),
503 stab
: self.stability(item
.id
),
504 depr
: self.deprecation(item
.id
),
506 om
.existentials
.push(t
);
508 hir
::ItemKind
::Static(ref ty
, ref mut_
, ref exp
) => {
511 mutability
: mut_
.clone(),
515 attrs
: item
.attrs
.clone(),
517 vis
: item
.vis
.clone(),
518 stab
: self.stability(item
.id
),
519 depr
: self.deprecation(item
.id
),
523 hir
::ItemKind
::Const(ref ty
, ref exp
) => {
529 attrs
: item
.attrs
.clone(),
531 vis
: item
.vis
.clone(),
532 stab
: self.stability(item
.id
),
533 depr
: self.deprecation(item
.id
),
535 om
.constants
.push(s
);
537 hir
::ItemKind
::Trait(is_auto
, unsafety
, ref gen
, ref b
, ref item_ids
) => {
538 let items
= item_ids
.iter()
539 .map(|ti
| self.cx
.tcx
.hir
.trait_item(ti
.id
).clone())
546 generics
: gen
.clone(),
547 bounds
: b
.iter().cloned().collect(),
549 attrs
: item
.attrs
.clone(),
551 vis
: item
.vis
.clone(),
552 stab
: self.stability(item
.id
),
553 depr
: self.deprecation(item
.id
),
557 hir
::ItemKind
::TraitAlias(..) => {
558 unimplemented
!("trait objects are not yet implemented")
561 hir
::ItemKind
::Impl(unsafety
,
568 // Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
569 // them up regardless of where they're located.
570 if !self.inlining
&& tr
.is_none() {
571 let items
= item_ids
.iter()
572 .map(|ii
| self.cx
.tcx
.hir
.impl_item(ii
.id
).clone())
578 generics
: gen
.clone(),
582 attrs
: item
.attrs
.clone(),
585 vis
: item
.vis
.clone(),
586 stab
: self.stability(item
.id
),
587 depr
: self.deprecation(item
.id
),
595 // convert each exported_macro into a doc item
596 fn visit_local_macro(&self, def
: &hir
::MacroDef
) -> Macro
{
597 debug
!("visit_local_macro: {}", def
.name
);
598 let tts
= def
.body
.trees().collect
::<Vec
<_
>>();
599 // Extract the spans of all matchers. They represent the "interface" of the macro.
600 let matchers
= tts
.chunks(4).map(|arm
| arm
[0].span()).collect();
603 def_id
: self.cx
.tcx
.hir
.local_def_id(def
.id
),
604 attrs
: def
.attrs
.clone(),
608 stab
: self.stability(def
.id
),
609 depr
: self.deprecation(def
.id
),