11//! Strip all doc(hidden) items from the output.
2+
3+ use rustc_middle:: ty:: TyCtxt ;
24use rustc_span:: symbol:: sym;
35use std:: mem;
46
@@ -7,6 +9,7 @@ use crate::clean::{Item, ItemIdSet, NestedAttributesExt};
79use crate :: core:: DocContext ;
810use crate :: fold:: { strip_item, DocFolder } ;
911use crate :: passes:: { ImplStripper , Pass } ;
12+ use crate :: visit_ast:: inherits_doc_hidden;
1013
1114pub ( crate ) const STRIP_HIDDEN : Pass = Pass {
1215 name : "strip-hidden" ,
@@ -21,7 +24,12 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
2124
2225 // strip all #[doc(hidden)] items
2326 let krate = {
24- let mut stripper = Stripper { retained : & mut retained, update_retained : true } ;
27+ let mut stripper = Stripper {
28+ retained : & mut retained,
29+ update_retained : true ,
30+ tcx : cx. tcx ,
31+ is_in_hidden_item : false ,
32+ } ;
2533 stripper. fold_crate ( krate)
2634 } ;
2735
@@ -36,40 +44,89 @@ pub(crate) fn strip_hidden(krate: clean::Crate, cx: &mut DocContext<'_>) -> clea
3644 stripper. fold_crate ( krate)
3745}
3846
39- struct Stripper < ' a > {
47+ struct Stripper < ' a , ' tcx > {
4048 retained : & ' a mut ItemIdSet ,
4149 update_retained : bool ,
50+ tcx : TyCtxt < ' tcx > ,
51+ is_in_hidden_item : bool ,
52+ }
53+
54+ impl < ' a , ' tcx > Stripper < ' a , ' tcx > {
55+ fn set_is_in_hidden_item_and_fold ( & mut self , is_in_hidden_item : bool , i : Item ) -> Item {
56+ let prev = self . is_in_hidden_item ;
57+ self . is_in_hidden_item |= is_in_hidden_item;
58+ let ret = self . fold_item_recur ( i) ;
59+ self . is_in_hidden_item = prev;
60+ ret
61+ }
62+
63+ /// In case `i` is a non-hidden impl block, then we special-case it by changing the value
64+ /// of `is_in_hidden_item` to `true` because the impl children inherit its visibility.
65+ fn recurse_in_impl ( & mut self , i : Item ) -> Item {
66+ let prev = mem:: replace ( & mut self . is_in_hidden_item , false ) ;
67+ let ret = self . fold_item_recur ( i) ;
68+ self . is_in_hidden_item = prev;
69+ ret
70+ }
4271}
4372
44- impl < ' a > DocFolder for Stripper < ' a > {
73+ impl < ' a , ' tcx > DocFolder for Stripper < ' a , ' tcx > {
4574 fn fold_item ( & mut self , i : Item ) -> Option < Item > {
46- if i. attrs . lists ( sym:: doc) . has_word ( sym:: hidden) {
47- debug ! ( "strip_hidden: stripping {:?} {:?}" , i. type_( ) , i. name) ;
48- // Use a dedicated hidden item for fields, variants, and modules.
49- // We need to keep private fields and variants, so that the docs
50- // can show a placeholder "// some variants omitted". We need to keep
51- // private modules, because they can contain impl blocks, and impl
52- // block privacy is inherited from the type and trait, not from the
53- // module it's defined in. Both of these are marked "stripped," and
54- // not included in the final docs, but since they still have an effect
55- // on the final doc, cannot be completely removed from the Clean IR.
56- match * i. kind {
57- clean:: StructFieldItem ( ..) | clean:: ModuleItem ( ..) | clean:: VariantItem ( ..) => {
58- // We need to recurse into stripped modules to
59- // strip things like impl methods but when doing so
60- // we must not add any items to the `retained` set.
61- let old = mem:: replace ( & mut self . update_retained , false ) ;
62- let ret = strip_item ( self . fold_item_recur ( i) ) ;
63- self . update_retained = old;
64- return Some ( ret) ;
65- }
66- _ => return None ,
75+ let has_doc_hidden = i. attrs . lists ( sym:: doc) . has_word ( sym:: hidden) ;
76+ let is_impl = matches ! ( * i. kind, clean:: ImplItem ( ..) ) ;
77+ let mut is_hidden = has_doc_hidden;
78+ if !is_impl {
79+ is_hidden = self . is_in_hidden_item || has_doc_hidden;
80+ if !is_hidden && i. inline_stmt_id . is_none ( ) {
81+ // We don't need to check if it's coming from a reexport since the reexport itself was
82+ // already checked.
83+ is_hidden = i
84+ . item_id
85+ . as_def_id ( )
86+ . and_then ( |def_id| def_id. as_local ( ) )
87+ . map ( |def_id| inherits_doc_hidden ( self . tcx , def_id) )
88+ . unwrap_or ( false ) ;
6789 }
68- } else {
90+ }
91+ if !is_hidden {
6992 if self . update_retained {
7093 self . retained . insert ( i. item_id ) ;
7194 }
95+ return Some ( if is_impl {
96+ self . recurse_in_impl ( i)
97+ } else {
98+ self . set_is_in_hidden_item_and_fold ( false , i)
99+ } ) ;
100+ }
101+ debug ! ( "strip_hidden: stripping {:?} {:?}" , i. type_( ) , i. name) ;
102+ // Use a dedicated hidden item for fields, variants, and modules.
103+ // We need to keep private fields and variants, so that the docs
104+ // can show a placeholder "// some variants omitted". We need to keep
105+ // private modules, because they can contain impl blocks, and impl
106+ // block privacy is inherited from the type and trait, not from the
107+ // module it's defined in. Both of these are marked "stripped," and
108+ // not included in the final docs, but since they still have an effect
109+ // on the final doc, cannot be completely removed from the Clean IR.
110+ match * i. kind {
111+ clean:: StructFieldItem ( ..) | clean:: ModuleItem ( ..) | clean:: VariantItem ( ..) => {
112+ // We need to recurse into stripped modules to
113+ // strip things like impl methods but when doing so
114+ // we must not add any items to the `retained` set.
115+ let old = mem:: replace ( & mut self . update_retained , false ) ;
116+ let ret = strip_item ( self . set_is_in_hidden_item_and_fold ( true , i) ) ;
117+ self . update_retained = old;
118+ Some ( ret)
119+ }
120+ _ => {
121+ let ret = self . set_is_in_hidden_item_and_fold ( true , i) ;
122+ if has_doc_hidden {
123+ // If the item itself has `#[doc(hidden)]`, then we simply remove it.
124+ None
125+ } else {
126+ // However if it's a "descendant" of a `#[doc(hidden)]` item, then we strip it.
127+ Some ( strip_item ( ret) )
128+ }
129+ }
72130 }
73- Some ( self . fold_item_recur ( i) )
74131 }
75132}
0 commit comments