1 //! The Rust AST Visitor. Extracts useful information and massages it into a form
2 //! usable for `clean`.
4 use rustc
::hir
::{self, Node}
;
5 use rustc
::hir
::def
::Def
;
6 use rustc
::hir
::def_id
::{DefId, LOCAL_CRATE}
;
7 use rustc
::middle
::privacy
::AccessLevel
;
8 use rustc
::util
::nodemap
::{FxHashSet, FxHashMap}
;
11 use syntax
::ext
::base
::MacroKind
;
12 use syntax
::source_map
::Spanned
;
13 use syntax_pos
::{self, Span}
;
18 use clean
::{self, AttributesExt, NestedAttributesExt, def_id_to_path}
;
21 // Looks to me like the first two of these are actually
22 // output parameters, maybe only mutated once; perhaps
23 // better simply to have the visit method return a tuple
26 // Also, is there some reason that this doesn't use the 'visit'
27 // framework from syntax?.
29 pub struct RustdocVisitor
<'a
, 'tcx
: 'a
, 'rcx
: 'a
> {
31 pub attrs
: hir
::HirVec
<ast
::Attribute
>,
32 pub cx
: &'a core
::DocContext
<'a
, 'tcx
, 'rcx
>,
33 view_item_stack
: FxHashSet
<ast
::NodeId
>,
35 /// Are the current module and all of its parents public?
36 inside_public_path
: bool
,
37 exact_paths
: Option
<FxHashMap
<DefId
, Vec
<String
>>>,
40 impl<'a
, 'tcx
, 'rcx
> RustdocVisitor
<'a
, 'tcx
, 'rcx
> {
42 cx
: &'a core
::DocContext
<'a
, 'tcx
, 'rcx
>
43 ) -> RustdocVisitor
<'a
, 'tcx
, 'rcx
> {
44 // If the root is re-exported, terminate all recursion.
45 let mut stack
= FxHashSet
::default();
46 stack
.insert(ast
::CRATE_NODE_ID
);
48 module
: Module
::new(None
),
49 attrs
: hir
::HirVec
::new(),
51 view_item_stack
: stack
,
53 inside_public_path
: true,
54 exact_paths
: Some(FxHashMap
::default()),
58 fn store_path(&mut self, did
: DefId
) {
59 // We can't use the entry API, as that keeps the mutable borrow of `self` active
60 // when we try to use `cx`.
61 let exact_paths
= self.exact_paths
.as_mut().unwrap();
62 if exact_paths
.get(&did
).is_none() {
63 let path
= def_id_to_path(self.cx
, did
, self.cx
.crate_name
.clone());
64 exact_paths
.insert(did
, path
);
68 fn stability(&self, id
: ast
::NodeId
) -> Option
<attr
::Stability
> {
69 self.cx
.tcx
.hir().opt_local_def_id(id
)
70 .and_then(|def_id
| self.cx
.tcx
.lookup_stability(def_id
)).cloned()
73 fn deprecation(&self, id
: ast
::NodeId
) -> Option
<attr
::Deprecation
> {
74 self.cx
.tcx
.hir().opt_local_def_id(id
)
75 .and_then(|def_id
| self.cx
.tcx
.lookup_deprecation(def_id
))
78 pub fn visit(&mut self, krate
: &hir
::Crate
) {
79 self.attrs
= krate
.attrs
.clone();
81 self.module
= self.visit_mod_contents(krate
.span
,
83 Spanned
{ span
: syntax_pos
::DUMMY_SP
,
84 node
: hir
::VisibilityKind
::Public
},
88 // Attach the crate's exported macros to the top-level module:
89 let macro_exports
: Vec
<_
> =
90 krate
.exported_macros
.iter().map(|def
| self.visit_local_macro(def
, None
)).collect();
91 self.module
.macros
.extend(macro_exports
);
92 self.module
.is_crate
= true;
94 self.cx
.renderinfo
.borrow_mut().exact_paths
= self.exact_paths
.take().unwrap();
97 pub fn visit_variant_data(&mut self, item
: &hir
::Item
,
98 name
: ast
::Name
, sd
: &hir
::VariantData
,
99 generics
: &hir
::Generics
) -> Struct
{
100 debug
!("Visiting struct");
101 let struct_type
= struct_type_from_def(&*sd
);
106 vis
: item
.vis
.clone(),
107 stab
: self.stability(item
.id
),
108 depr
: self.deprecation(item
.id
),
109 attrs
: item
.attrs
.clone(),
110 generics
: generics
.clone(),
111 fields
: sd
.fields().iter().cloned().collect(),
116 pub fn visit_union_data(&mut self, item
: &hir
::Item
,
117 name
: ast
::Name
, sd
: &hir
::VariantData
,
118 generics
: &hir
::Generics
) -> Union
{
119 debug
!("Visiting union");
120 let struct_type
= struct_type_from_def(&*sd
);
125 vis
: item
.vis
.clone(),
126 stab
: self.stability(item
.id
),
127 depr
: self.deprecation(item
.id
),
128 attrs
: item
.attrs
.clone(),
129 generics
: generics
.clone(),
130 fields
: sd
.fields().iter().cloned().collect(),
135 pub fn visit_enum_def(&mut self, it
: &hir
::Item
,
136 name
: ast
::Name
, def
: &hir
::EnumDef
,
137 params
: &hir
::Generics
) -> Enum
{
138 debug
!("Visiting enum");
141 variants
: def
.variants
.iter().map(|v
| Variant
{
142 name
: v
.node
.ident
.name
,
143 attrs
: v
.node
.attrs
.clone(),
144 stab
: self.stability(v
.node
.data
.id()),
145 depr
: self.deprecation(v
.node
.data
.id()),
146 def
: v
.node
.data
.clone(),
150 stab
: self.stability(it
.id
),
151 depr
: self.deprecation(it
.id
),
152 generics
: params
.clone(),
153 attrs
: it
.attrs
.clone(),
159 pub fn visit_fn(&mut self, om
: &mut Module
, item
: &hir
::Item
,
160 name
: ast
::Name
, fd
: &hir
::FnDecl
,
161 header
: hir
::FnHeader
,
164 debug
!("Visiting fn");
165 let macro_kind
= item
.attrs
.iter().filter_map(|a
| {
166 if a
.check_name("proc_macro") {
167 Some(MacroKind
::Bang
)
168 } else if a
.check_name("proc_macro_derive") {
169 Some(MacroKind
::Derive
)
170 } else if a
.check_name("proc_macro_attribute") {
171 Some(MacroKind
::Attr
)
178 let name
= if kind
== MacroKind
::Derive
{
179 item
.attrs
.lists("proc_macro_derive")
180 .filter_map(|mi
| mi
.name())
182 .expect("proc-macro derives require a name")
187 let mut helpers
= Vec
::new();
188 for mi
in item
.attrs
.lists("proc_macro_derive") {
189 if !mi
.check_name("attributes") {
193 if let Some(list
) = mi
.meta_item_list() {
194 for inner_mi
in list
{
195 if let Some(name
) = inner_mi
.name() {
202 om
.proc_macros
.push(ProcMacro
{
207 attrs
: item
.attrs
.clone(),
209 stab
: self.stability(item
.id
),
210 depr
: self.deprecation(item
.id
),
214 om
.fns
.push(Function
{
216 vis
: item
.vis
.clone(),
217 stab
: self.stability(item
.id
),
218 depr
: self.deprecation(item
.id
),
219 attrs
: item
.attrs
.clone(),
223 generics
: gen
.clone(),
231 pub fn visit_mod_contents(&mut self, span
: Span
, attrs
: hir
::HirVec
<ast
::Attribute
>,
232 vis
: hir
::Visibility
, id
: ast
::NodeId
,
234 name
: Option
<ast
::Name
>) -> Module
{
235 let mut om
= Module
::new(name
);
236 om
.where_outer
= span
;
237 om
.where_inner
= m
.inner
;
239 om
.vis
= vis
.clone();
240 om
.stab
= self.stability(id
);
241 om
.depr
= self.deprecation(id
);
243 // Keep track of if there were any private modules in the path.
244 let orig_inside_public_path
= self.inside_public_path
;
245 self.inside_public_path
&= vis
.node
.is_pub();
246 for i
in &m
.item_ids
{
247 let item
= self.cx
.tcx
.hir().expect_item(i
.id
);
248 self.visit_item(item
, None
, &mut om
);
250 self.inside_public_path
= orig_inside_public_path
;
254 /// Tries to resolve the target of a `pub use` statement and inlines the
255 /// target if it is defined locally and would not be documented otherwise,
256 /// or when it is specifically requested with `please_inline`.
257 /// (the latter is the case when the import is marked `doc(inline)`)
259 /// Cross-crate inlining occurs later on during crate cleaning
260 /// and follows different rules.
262 /// Returns true if the target has been inlined.
263 fn maybe_inline_local(&mut self,
266 renamed
: Option
<ast
::Ident
>,
269 please_inline
: bool
) -> bool
{
271 fn inherits_doc_hidden(cx
: &core
::DocContext
, mut node
: ast
::NodeId
) -> bool
{
272 while let Some(id
) = cx
.tcx
.hir().get_enclosing_scope(node
) {
274 if cx
.tcx
.hir().attrs(node
).lists("doc").has_word("hidden") {
277 if node
== ast
::CRATE_NODE_ID
{
284 debug
!("maybe_inline_local def: {:?}", def
);
286 let tcx
= self.cx
.tcx
;
290 let def_did
= def
.def_id();
292 let use_attrs
= tcx
.hir().attrs(id
);
293 // Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
294 let is_no_inline
= use_attrs
.lists("doc").has_word("no_inline") ||
295 use_attrs
.lists("doc").has_word("hidden");
297 // For cross-crate impl inlining we need to know whether items are
298 // reachable in documentation -- a previously nonreachable item can be
299 // made reachable by cross-crate inlining which we're checking here.
300 // (this is done here because we need to know this upfront).
301 if !def_did
.is_local() && !is_no_inline
{
302 let attrs
= clean
::inline
::load_attrs(self.cx
, def_did
);
303 let self_is_hidden
= attrs
.lists("doc").has_word("hidden");
309 Def
::ForeignTy(did
) |
310 Def
::TyAlias(did
) if !self_is_hidden
=> {
314 .insert(did
, AccessLevel
::Public
);
316 Def
::Mod(did
) => if !self_is_hidden
{
317 ::visit_lib
::LibEmbargoVisitor
::new(self.cx
).visit_mod(did
);
325 let def_node_id
= match tcx
.hir().as_local_node_id(def_did
) {
326 Some(n
) => n
, None
=> return false
329 let is_private
= !self.cx
.renderinfo
.borrow().access_levels
.is_public(def_did
);
330 let is_hidden
= inherits_doc_hidden(self.cx
, def_node_id
);
332 // Only inline if requested or if the item would otherwise be stripped.
333 if (!please_inline
&& !is_private
&& !is_hidden
) || is_no_inline
{
337 if !self.view_item_stack
.insert(def_node_id
) { return false }
339 let ret
= match tcx
.hir().get(def_node_id
) {
340 Node
::Item(&hir
::Item { node: hir::ItemKind::Mod(ref m), .. }
) if glob
=> {
341 let prev
= mem
::replace(&mut self.inlining
, true);
342 for i
in &m
.item_ids
{
343 let i
= self.cx
.tcx
.hir().expect_item(i
.id
);
344 self.visit_item(i
, None
, om
);
346 self.inlining
= prev
;
349 Node
::Item(it
) if !glob
=> {
350 let prev
= mem
::replace(&mut self.inlining
, true);
351 self.visit_item(it
, renamed
, om
);
352 self.inlining
= prev
;
355 Node
::ForeignItem(it
) if !glob
=> {
356 // Generate a fresh `extern {}` block if we want to inline a foreign item.
357 om
.foreigns
.push(hir
::ForeignMod
{
358 abi
: tcx
.hir().get_foreign_abi(it
.id
),
359 items
: vec
![hir
::ForeignItem
{
360 ident
: renamed
.unwrap_or(it
.ident
),
366 Node
::MacroDef(def
) if !glob
=> {
367 om
.macros
.push(self.visit_local_macro(def
, renamed
.map(|i
| i
.name
)));
372 self.view_item_stack
.remove(&def_node_id
);
376 pub fn visit_item(&mut self, item
: &hir
::Item
,
377 renamed
: Option
<ast
::Ident
>, om
: &mut Module
) {
378 debug
!("Visiting item {:?}", item
);
379 let ident
= renamed
.unwrap_or(item
.ident
);
381 if item
.vis
.node
.is_pub() {
382 let def_id
= self.cx
.tcx
.hir().local_def_id(item
.id
);
383 self.store_path(def_id
);
387 hir
::ItemKind
::ForeignMod(ref fm
) => {
388 // If inlining we only want to include public functions.
389 om
.foreigns
.push(if self.inlining
{
392 items
: fm
.items
.iter().filter(|i
| i
.vis
.node
.is_pub()).cloned().collect(),
398 // If we're inlining, skip private items.
399 _
if self.inlining
&& !item
.vis
.node
.is_pub() => {}
400 hir
::ItemKind
::GlobalAsm(..) => {}
401 hir
::ItemKind
::ExternCrate(orig_name
) => {
402 let def_id
= self.cx
.tcx
.hir().local_def_id(item
.id
);
403 om
.extern_crates
.push(ExternCrate
{
404 cnum
: self.cx
.tcx
.extern_mod_stmt_cnum(def_id
)
405 .unwrap_or(LOCAL_CRATE
),
407 path
: orig_name
.map(|x
|x
.to_string()),
408 vis
: item
.vis
.clone(),
409 attrs
: item
.attrs
.clone(),
413 hir
::ItemKind
::Use(_
, hir
::UseKind
::ListStem
) => {}
414 hir
::ItemKind
::Use(ref path
, kind
) => {
415 let is_glob
= kind
== hir
::UseKind
::Glob
;
417 // Struct and variant constructors and proc macro stubs always show up alongside
418 // their definitions, we've already processed them so just discard these.
420 Def
::StructCtor(..) | Def
::VariantCtor(..) | Def
::SelfCtor(..) |
421 Def
::Macro(_
, MacroKind
::ProcMacroStub
) => return,
425 // If there was a private module in the current path then don't bother inlining
426 // anything as it will probably be stripped anyway.
427 if item
.vis
.node
.is_pub() && self.inside_public_path
{
428 let please_inline
= item
.attrs
.iter().any(|item
| {
429 match item
.meta_item_list() {
430 Some(ref list
) if item
.check_name("doc") => {
431 list
.iter().any(|i
| i
.check_name("inline"))
436 let ident
= if is_glob { None }
else { Some(ident) }
;
437 if self.maybe_inline_local(item
.id
,
447 om
.imports
.push(Import
{
450 vis
: item
.vis
.clone(),
451 attrs
: item
.attrs
.clone(),
452 path
: (**path
).clone(),
457 hir
::ItemKind
::Mod(ref m
) => {
458 om
.mods
.push(self.visit_mod_contents(item
.span
,
465 hir
::ItemKind
::Enum(ref ed
, ref gen
) =>
466 om
.enums
.push(self.visit_enum_def(item
, ident
.name
, ed
, gen
)),
467 hir
::ItemKind
::Struct(ref sd
, ref gen
) =>
468 om
.structs
.push(self.visit_variant_data(item
, ident
.name
, sd
, gen
)),
469 hir
::ItemKind
::Union(ref sd
, ref gen
) =>
470 om
.unions
.push(self.visit_union_data(item
, ident
.name
, sd
, gen
)),
471 hir
::ItemKind
::Fn(ref fd
, header
, ref gen
, body
) =>
472 self.visit_fn(om
, item
, ident
.name
, &**fd
, header
, gen
, body
),
473 hir
::ItemKind
::Ty(ref ty
, ref gen
) => {
479 attrs
: item
.attrs
.clone(),
481 vis
: item
.vis
.clone(),
482 stab
: self.stability(item
.id
),
483 depr
: self.deprecation(item
.id
),
487 hir
::ItemKind
::Existential(ref exist_ty
) => {
488 let t
= Existential
{
489 exist_ty
: exist_ty
.clone(),
492 attrs
: item
.attrs
.clone(),
494 vis
: item
.vis
.clone(),
495 stab
: self.stability(item
.id
),
496 depr
: self.deprecation(item
.id
),
498 om
.existentials
.push(t
);
500 hir
::ItemKind
::Static(ref ty
, ref mut_
, ref exp
) => {
503 mutability
: mut_
.clone(),
507 attrs
: item
.attrs
.clone(),
509 vis
: item
.vis
.clone(),
510 stab
: self.stability(item
.id
),
511 depr
: self.deprecation(item
.id
),
515 hir
::ItemKind
::Const(ref ty
, ref exp
) => {
521 attrs
: item
.attrs
.clone(),
523 vis
: item
.vis
.clone(),
524 stab
: self.stability(item
.id
),
525 depr
: self.deprecation(item
.id
),
527 om
.constants
.push(s
);
529 hir
::ItemKind
::Trait(is_auto
, unsafety
, ref gen
, ref b
, ref item_ids
) => {
530 let items
= item_ids
.iter()
531 .map(|ti
| self.cx
.tcx
.hir().trait_item(ti
.id
).clone())
538 generics
: gen
.clone(),
539 bounds
: b
.iter().cloned().collect(),
541 attrs
: item
.attrs
.clone(),
543 vis
: item
.vis
.clone(),
544 stab
: self.stability(item
.id
),
545 depr
: self.deprecation(item
.id
),
549 hir
::ItemKind
::TraitAlias(..) => {
550 unimplemented
!("trait objects are not yet implemented")
553 hir
::ItemKind
::Impl(unsafety
,
560 // Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
561 // them up regardless of where they're located.
562 if !self.inlining
&& tr
.is_none() {
563 let items
= item_ids
.iter()
564 .map(|ii
| self.cx
.tcx
.hir().impl_item(ii
.id
).clone())
570 generics
: gen
.clone(),
574 attrs
: item
.attrs
.clone(),
577 vis
: item
.vis
.clone(),
578 stab
: self.stability(item
.id
),
579 depr
: self.deprecation(item
.id
),
587 // Convert each `exported_macro` into a doc item.
588 fn visit_local_macro(
591 renamed
: Option
<ast
::Name
>
593 debug
!("visit_local_macro: {}", def
.name
);
594 let tts
= def
.body
.trees().collect
::<Vec
<_
>>();
595 // Extract the spans of all matchers. They represent the "interface" of the macro.
596 let matchers
= tts
.chunks(4).map(|arm
| arm
[0].span()).collect();
599 def_id
: self.cx
.tcx
.hir().local_def_id(def
.id
),
600 attrs
: def
.attrs
.clone(),
601 name
: renamed
.unwrap_or(def
.name
),
604 stab
: self.stability(def
.id
),
605 depr
: self.deprecation(def
.id
),