@@ -5,8 +5,10 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
55use rustc_hir as hir;
66use rustc_hir:: def:: { DefKind , Res } ;
77use rustc_hir:: def_id:: DefId ;
8+ use rustc_hir:: intravisit:: { walk_item, Visitor } ;
89use rustc_hir:: Node ;
910use rustc_hir:: CRATE_HIR_ID ;
11+ use rustc_middle:: hir:: nested_filter;
1012use rustc_middle:: ty:: TyCtxt ;
1113use rustc_span:: def_id:: { CRATE_DEF_ID , LOCAL_CRATE } ;
1214use rustc_span:: symbol:: { kw, sym, Symbol } ;
@@ -57,29 +59,34 @@ pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: hir::HirId) -> bool
5759 false
5860}
5961
60- // Also, is there some reason that this doesn't use the 'visit'
61- // framework from syntax?.
62-
6362pub ( crate ) struct RustdocVisitor < ' a , ' tcx > {
6463 cx : & ' a mut core:: DocContext < ' tcx > ,
6564 view_item_stack : FxHashSet < hir:: HirId > ,
6665 inlining : bool ,
6766 /// Are the current module and all of its parents public?
6867 inside_public_path : bool ,
6968 exact_paths : FxHashMap < DefId , Vec < Symbol > > ,
69+ modules : Vec < Module < ' tcx > > ,
7070}
7171
7272impl < ' a , ' tcx > RustdocVisitor < ' a , ' tcx > {
7373 pub ( crate ) fn new ( cx : & ' a mut core:: DocContext < ' tcx > ) -> RustdocVisitor < ' a , ' tcx > {
7474 // If the root is re-exported, terminate all recursion.
7575 let mut stack = FxHashSet :: default ( ) ;
7676 stack. insert ( hir:: CRATE_HIR_ID ) ;
77+ let om = Module :: new (
78+ cx. tcx . crate_name ( LOCAL_CRATE ) ,
79+ hir:: CRATE_HIR_ID ,
80+ cx. tcx . hir ( ) . root_module ( ) . spans . inner_span ,
81+ ) ;
82+
7783 RustdocVisitor {
7884 cx,
7985 view_item_stack : stack,
8086 inlining : false ,
8187 inside_public_path : true ,
8288 exact_paths : FxHashMap :: default ( ) ,
89+ modules : vec ! [ om] ,
8390 }
8491 }
8592
@@ -89,12 +96,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
8996 }
9097
9198 pub ( crate ) fn visit ( mut self ) -> Module < ' tcx > {
92- let mut top_level_module = self . visit_mod_contents (
93- hir:: CRATE_HIR_ID ,
94- self . cx . tcx . hir ( ) . root_module ( ) ,
95- self . cx . tcx . crate_name ( LOCAL_CRATE ) ,
96- None ,
97- ) ;
99+ let root_module = self . cx . tcx . hir ( ) . root_module ( ) ;
100+ self . visit_mod_contents ( CRATE_HIR_ID , root_module) ;
101+
102+ let mut top_level_module = self . modules . pop ( ) . unwrap ( ) ;
98103
99104 // `#[macro_export] macro_rules!` items are reexported at the top level of the
100105 // crate, regardless of where they're defined. We want to document the
@@ -109,15 +114,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
109114 // macro in the same module.
110115 let mut inserted = FxHashSet :: default ( ) ;
111116 for export in self . cx . tcx . module_reexports ( CRATE_DEF_ID ) . unwrap_or ( & [ ] ) {
112- if let Res :: Def ( DefKind :: Macro ( _) , def_id) = export. res {
113- if let Some ( local_def_id) = def_id. as_local ( ) {
114- if self . cx . tcx . has_attr ( def_id, sym:: macro_export) {
115- if inserted. insert ( def_id) {
116- let item = self . cx . tcx . hir ( ) . expect_item ( local_def_id) ;
117- top_level_module. items . push ( ( item, None , None ) ) ;
118- }
119- }
120- }
117+ if let Res :: Def ( DefKind :: Macro ( _) , def_id) = export. res &&
118+ let Some ( local_def_id) = def_id. as_local ( ) &&
119+ self . cx . tcx . has_attr ( def_id, sym:: macro_export) &&
120+ inserted. insert ( def_id)
121+ {
122+ let item = self . cx . tcx . hir ( ) . expect_item ( local_def_id) ;
123+ top_level_module. items . push ( ( item, None , None ) ) ;
121124 }
122125 }
123126
@@ -151,36 +154,34 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
151154 top_level_module
152155 }
153156
154- fn visit_mod_contents (
155- & mut self ,
156- id : hir:: HirId ,
157- m : & ' tcx hir:: Mod < ' tcx > ,
158- name : Symbol ,
159- parent_id : Option < hir:: HirId > ,
160- ) -> Module < ' tcx > {
161- let mut om = Module :: new ( name, id, m. spans . inner_span ) ;
157+ /// This method will go through the given module items in two passes:
158+ /// 1. The items which are not glob imports/reexports.
159+ /// 2. The glob imports/reexports.
160+ fn visit_mod_contents ( & mut self , id : hir:: HirId , m : & ' tcx hir:: Mod < ' tcx > ) {
161+ debug ! ( "Going through module {:?}" , m) ;
162162 let def_id = self . cx . tcx . hir ( ) . local_def_id ( id) . to_def_id ( ) ;
163163 // Keep track of if there were any private modules in the path.
164164 let orig_inside_public_path = self . inside_public_path ;
165165 self . inside_public_path &= self . cx . tcx . visibility ( def_id) . is_public ( ) ;
166+
167+ // Reimplementation of `walk_mod` because we need to do it in two passes (explanations in
168+ // the second loop):
166169 for & i in m. item_ids {
167170 let item = self . cx . tcx . hir ( ) . item ( i) ;
168- if matches ! ( item. kind, hir:: ItemKind :: Use ( _, hir:: UseKind :: Glob ) ) {
169- continue ;
171+ if ! matches ! ( item. kind, hir:: ItemKind :: Use ( _, hir:: UseKind :: Glob ) ) {
172+ self . visit_item ( item ) ;
170173 }
171- self . visit_item ( item, None , & mut om, parent_id) ;
172174 }
173175 for & i in m. item_ids {
174176 let item = self . cx . tcx . hir ( ) . item ( i) ;
175177 // To match the way import precedence works, visit glob imports last.
176178 // Later passes in rustdoc will de-duplicate by name and kind, so if glob-
177179 // imported items appear last, then they'll be the ones that get discarded.
178180 if matches ! ( item. kind, hir:: ItemKind :: Use ( _, hir:: UseKind :: Glob ) ) {
179- self . visit_item ( item, None , & mut om , parent_id ) ;
181+ self . visit_item ( item) ;
180182 }
181183 }
182184 self . inside_public_path = orig_inside_public_path;
183- om
184185 }
185186
186187 /// Tries to resolve the target of a `pub use` statement and inlines the
@@ -198,7 +199,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
198199 res : Res ,
199200 renamed : Option < Symbol > ,
200201 glob : bool ,
201- om : & mut Module < ' tcx > ,
202202 please_inline : bool ,
203203 ) -> bool {
204204 debug ! ( "maybe_inline_local res: {:?}" , res) ;
@@ -249,20 +249,20 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
249249 let prev = mem:: replace ( & mut self . inlining , true ) ;
250250 for & i in m. item_ids {
251251 let i = self . cx . tcx . hir ( ) . item ( i) ;
252- self . visit_item ( i, None , om , Some ( id) ) ;
252+ self . visit_item_inner ( i, None , Some ( id) ) ;
253253 }
254254 self . inlining = prev;
255255 true
256256 }
257257 Node :: Item ( it) if !glob => {
258258 let prev = mem:: replace ( & mut self . inlining , true ) ;
259- self . visit_item ( it, renamed, om , Some ( id) ) ;
259+ self . visit_item_inner ( it, renamed, Some ( id) ) ;
260260 self . inlining = prev;
261261 true
262262 }
263263 Node :: ForeignItem ( it) if !glob => {
264264 let prev = mem:: replace ( & mut self . inlining , true ) ;
265- self . visit_foreign_item ( it, renamed, om ) ;
265+ self . visit_foreign_item_inner ( it, renamed) ;
266266 self . inlining = prev;
267267 true
268268 }
@@ -272,13 +272,22 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
272272 ret
273273 }
274274
275- fn visit_item (
275+ #[ inline]
276+ fn add_to_current_mod (
276277 & mut self ,
277278 item : & ' tcx hir:: Item < ' _ > ,
278279 renamed : Option < Symbol > ,
279- om : & mut Module < ' tcx > ,
280280 parent_id : Option < hir:: HirId > ,
281281 ) {
282+ self . modules . last_mut ( ) . unwrap ( ) . items . push ( ( item, renamed, parent_id) )
283+ }
284+
285+ fn visit_item_inner (
286+ & mut self ,
287+ item : & ' tcx hir:: Item < ' _ > ,
288+ renamed : Option < Symbol > ,
289+ parent_id : Option < hir:: HirId > ,
290+ ) -> bool {
282291 debug ! ( "visiting item {:?}" , item) ;
283292 let name = renamed. unwrap_or ( item. ident . name ) ;
284293
@@ -293,7 +302,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
293302 hir:: ItemKind :: ForeignMod { items, .. } => {
294303 for item in items {
295304 let item = self . cx . tcx . hir ( ) . foreign_item ( item. id ) ;
296- self . visit_foreign_item ( item, None , om ) ;
305+ self . visit_foreign_item_inner ( item, None ) ;
297306 }
298307 }
299308 // If we're inlining, skip private items or item reexported as "_".
@@ -326,14 +335,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
326335 res,
327336 ident,
328337 is_glob,
329- om,
330338 please_inline,
331339 ) {
332340 continue ;
333341 }
334342 }
335343
336- om . items . push ( ( item, renamed, parent_id) )
344+ self . add_to_current_mod ( item, renamed, parent_id) ;
337345 }
338346 }
339347 hir:: ItemKind :: Macro ( ref macro_def, _) => {
@@ -353,11 +361,11 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
353361 let nonexported = !self . cx . tcx . has_attr ( def_id, sym:: macro_export) ;
354362
355363 if is_macro_2_0 || nonexported || self . inlining {
356- om . items . push ( ( item, renamed, None ) ) ;
364+ self . add_to_current_mod ( item, renamed, None ) ;
357365 }
358366 }
359367 hir:: ItemKind :: Mod ( ref m) => {
360- om . mods . push ( self . visit_mod_contents ( item. hir_id ( ) , m, name, parent_id ) ) ;
368+ self . enter_mod ( item. hir_id ( ) , m, name) ;
361369 }
362370 hir:: ItemKind :: Fn ( ..)
363371 | hir:: ItemKind :: ExternCrate ( ..)
@@ -368,33 +376,92 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
368376 | hir:: ItemKind :: OpaqueTy ( ..)
369377 | hir:: ItemKind :: Static ( ..)
370378 | hir:: ItemKind :: Trait ( ..)
371- | hir:: ItemKind :: TraitAlias ( ..) => om. items . push ( ( item, renamed, parent_id) ) ,
379+ | hir:: ItemKind :: TraitAlias ( ..) => {
380+ self . add_to_current_mod ( item, renamed, parent_id) ;
381+ }
372382 hir:: ItemKind :: Const ( ..) => {
373383 // Underscore constants do not correspond to a nameable item and
374384 // so are never useful in documentation.
375385 if name != kw:: Underscore {
376- om . items . push ( ( item, renamed, parent_id) ) ;
386+ self . add_to_current_mod ( item, renamed, parent_id) ;
377387 }
378388 }
379389 hir:: ItemKind :: Impl ( impl_) => {
380390 // Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
381391 // them up regardless of where they're located.
382392 if !self . inlining && impl_. of_trait . is_none ( ) {
383- om . items . push ( ( item, None , None ) ) ;
393+ self . add_to_current_mod ( item, None , None ) ;
384394 }
385395 }
386396 }
397+ true
387398 }
388399
389- fn visit_foreign_item (
400+ fn visit_foreign_item_inner (
390401 & mut self ,
391402 item : & ' tcx hir:: ForeignItem < ' _ > ,
392403 renamed : Option < Symbol > ,
393- om : & mut Module < ' tcx > ,
394404 ) {
395405 // If inlining we only want to include public functions.
396406 if !self . inlining || self . cx . tcx . visibility ( item. owner_id ) . is_public ( ) {
397- om . foreigns . push ( ( item, renamed) ) ;
407+ self . modules . last_mut ( ) . unwrap ( ) . foreigns . push ( ( item, renamed) ) ;
398408 }
399409 }
410+
411+ /// This method will create a new module and push it onto the "modules stack" then call
412+ /// `visit_mod_contents`. Once done, it'll remove it from the "modules stack" and instead
413+ /// add into into the list of modules of the current module.
414+ fn enter_mod ( & mut self , id : hir:: HirId , m : & ' tcx hir:: Mod < ' tcx > , name : Symbol ) {
415+ self . modules . push ( Module :: new ( name, id, m. spans . inner_span ) ) ;
416+
417+ self . visit_mod_contents ( id, m) ;
418+
419+ let last = self . modules . pop ( ) . unwrap ( ) ;
420+ self . modules . last_mut ( ) . unwrap ( ) . mods . push ( last) ;
421+ }
422+ }
423+
424+ // We need to implement this visitor so it'll go everywhere and retrieve items we're interested in
425+ // such as impl blocks in const blocks.
426+ impl < ' a , ' tcx > Visitor < ' tcx > for RustdocVisitor < ' a , ' tcx > {
427+ type NestedFilter = nested_filter:: All ;
428+
429+ fn nested_visit_map ( & mut self ) -> Self :: Map {
430+ self . cx . tcx . hir ( )
431+ }
432+
433+ fn visit_item ( & mut self , i : & ' tcx hir:: Item < ' tcx > ) {
434+ let parent_id = if self . modules . len ( ) > 1 {
435+ Some ( self . modules [ self . modules . len ( ) - 2 ] . id )
436+ } else {
437+ None
438+ } ;
439+ if self . visit_item_inner ( i, None , parent_id) {
440+ walk_item ( self , i) ;
441+ }
442+ }
443+
444+ fn visit_mod ( & mut self , _: & hir:: Mod < ' tcx > , _: Span , _: hir:: HirId ) {
445+ // Handled in `visit_item_inner`
446+ }
447+
448+ fn visit_use ( & mut self , _: & hir:: UsePath < ' tcx > , _: hir:: HirId ) {
449+ // Handled in `visit_item_inner`
450+ }
451+
452+ fn visit_path ( & mut self , _: & hir:: Path < ' tcx > , _: hir:: HirId ) {
453+ // Handled in `visit_item_inner`
454+ }
455+
456+ fn visit_label ( & mut self , _: & rustc_ast:: Label ) {
457+ // Unneeded.
458+ }
459+
460+ fn visit_infer ( & mut self , _: & hir:: InferArg ) {
461+ // Unneeded.
462+ }
463+
464+ fn visit_lifetime ( & mut self , _: & hir:: Lifetime ) {
465+ // Unneeded.
466+ }
400467}
0 commit comments