@@ -11,6 +11,8 @@ pub(crate) mod types;
1111pub ( crate ) mod utils;
1212
1313use rustc_ast as ast;
14+ use rustc_ast:: token:: { Token , TokenKind } ;
15+ use rustc_ast:: tokenstream:: { TokenStream , TokenTree } ;
1416use rustc_attr as attr;
1517use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexMap , FxIndexSet , IndexEntry } ;
1618use rustc_hir as hir;
@@ -2079,8 +2081,8 @@ impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
20792081 fn visit_item ( & mut self , item : & ' hir hir:: Item < ' hir > ) {
20802082 if self . item . is_none ( )
20812083 && item. ident == self . looking_for
2082- && matches ! ( item. kind, hir:: ItemKind :: Use ( _, _) )
2083- || item. owner_id . def_id == self . target_def_id
2084+ && ( matches ! ( item. kind, hir:: ItemKind :: Use ( _, _) )
2085+ || item. owner_id . def_id == self . target_def_id )
20842086 {
20852087 self . item = Some ( item) ;
20862088 }
@@ -2096,34 +2098,149 @@ fn get_all_import_attributes<'hir>(
20962098 tcx : TyCtxt < ' hir > ,
20972099 target_def_id : LocalDefId ,
20982100 attributes : & mut Vec < ast:: Attribute > ,
2101+ is_inline : bool ,
20992102) {
21002103 let hir_map = tcx. hir ( ) ;
21012104 let mut visitor = OneLevelVisitor :: new ( hir_map, target_def_id) ;
21022105 let mut visited = FxHashSet :: default ( ) ;
21032106 // If the item is an import and has at least a path with two parts, we go into it.
2104- while let hir:: ItemKind :: Use ( path, _) = item. kind &&
2105- path. segments . len ( ) > 1 &&
2106- let hir:: def:: Res :: Def ( _, def_id) = path. segments [ path. segments . len ( ) - 2 ] . res &&
2107- visited. insert ( def_id)
2108- {
2109- if let Some ( hir:: Node :: Item ( parent_item) ) = hir_map. get_if_local ( def_id) {
2110- // We add the attributes from this import into the list.
2111- attributes. extend_from_slice ( hir_map. attrs ( item. hir_id ( ) ) ) ;
2112- // We get the `Ident` we will be looking for into `item`.
2113- let looking_for = path. segments [ path. segments . len ( ) - 1 ] . ident ;
2114- visitor. reset ( looking_for) ;
2115- hir:: intravisit:: walk_item ( & mut visitor, parent_item) ;
2116- if let Some ( i) = visitor. item {
2117- item = i;
2118- } else {
2119- break ;
2107+ while let hir:: ItemKind :: Use ( path, _) = item. kind && visited. insert ( item. hir_id ( ) ) {
2108+ // We add the attributes from this import into the list.
2109+ add_without_unwanted_attributes ( attributes, hir_map. attrs ( item. hir_id ( ) ) , is_inline) ;
2110+
2111+ let def_id = if path. segments . len ( ) > 1 {
2112+ match path. segments [ path. segments . len ( ) - 2 ] . res {
2113+ hir:: def:: Res :: Def ( _, def_id) => def_id,
2114+ _ => break ,
2115+ }
2116+ } else {
2117+ // If the path doesn't have a parent, then the parent is the current module.
2118+ tcx. parent ( item. owner_id . def_id . to_def_id ( ) )
2119+ } ;
2120+
2121+ let Some ( parent) = hir_map. get_if_local ( def_id) else { break } ;
2122+
2123+ // We get the `Ident` we will be looking for into `item`.
2124+ let looking_for = path. segments [ path. segments . len ( ) - 1 ] . ident ;
2125+ visitor. reset ( looking_for) ;
2126+
2127+ match parent {
2128+ hir:: Node :: Item ( parent_item) => {
2129+ hir:: intravisit:: walk_item ( & mut visitor, parent_item) ;
2130+ }
2131+ hir:: Node :: Crate ( m) => {
2132+ hir:: intravisit:: walk_mod (
2133+ & mut visitor,
2134+ m,
2135+ tcx. local_def_id_to_hir_id ( def_id. as_local ( ) . unwrap ( ) ) ,
2136+ ) ;
21202137 }
2138+ _ => break ,
2139+ }
2140+ if let Some ( i) = visitor. item {
2141+ item = i;
21212142 } else {
21222143 break ;
21232144 }
21242145 }
21252146}
21262147
2148+ fn filter_tokens_from_list (
2149+ args_tokens : TokenStream ,
2150+ should_retain : impl Fn ( & TokenTree ) -> bool ,
2151+ ) -> Vec < TokenTree > {
2152+ let mut tokens = Vec :: with_capacity ( args_tokens. len ( ) ) ;
2153+ let mut skip_next_comma = false ;
2154+ for token in args_tokens. into_trees ( ) {
2155+ match token {
2156+ TokenTree :: Token ( Token { kind : TokenKind :: Comma , .. } , _) if skip_next_comma => {
2157+ skip_next_comma = false ;
2158+ }
2159+ token if should_retain ( & token) => {
2160+ skip_next_comma = false ;
2161+ tokens. push ( token) ;
2162+ }
2163+ _ => {
2164+ skip_next_comma = true ;
2165+ }
2166+ }
2167+ }
2168+ tokens
2169+ }
2170+
2171+ /// When inlining items, we merge its attributes (and all the reexports attributes too) with the
2172+ /// final reexport. For example:
2173+ ///
2174+ /// ```ignore (just an example)
2175+ /// #[doc(hidden, cfg(feature = "foo"))]
2176+ /// pub struct Foo;
2177+ ///
2178+ /// #[doc(cfg(feature = "bar"))]
2179+ /// #[doc(hidden, no_inline)]
2180+ /// pub use Foo as Foo1;
2181+ ///
2182+ /// #[doc(inline)]
2183+ /// pub use Foo2 as Bar;
2184+ /// ```
2185+ ///
2186+ /// So `Bar` at the end will have both `cfg(feature = "...")`. However, we don't want to merge all
2187+ /// attributes so we filter out the following ones:
2188+ /// * `doc(inline)`
2189+ /// * `doc(no_inline)`
2190+ /// * `doc(hidden)`
2191+ fn add_without_unwanted_attributes (
2192+ attrs : & mut Vec < ast:: Attribute > ,
2193+ new_attrs : & [ ast:: Attribute ] ,
2194+ is_inline : bool ,
2195+ ) {
2196+ // If it's `#[doc(inline)]`, we don't want all attributes, otherwise we keep everything.
2197+ if !is_inline {
2198+ attrs. extend_from_slice ( new_attrs) ;
2199+ return ;
2200+ }
2201+ for attr in new_attrs {
2202+ let mut attr = attr. clone ( ) ;
2203+ match attr. kind {
2204+ ast:: AttrKind :: Normal ( ref mut normal) => {
2205+ if let [ ident] = & * normal. item . path . segments &&
2206+ let ident = ident. ident . name &&
2207+ ident == sym:: doc
2208+ {
2209+ match normal. item . args {
2210+ ast:: AttrArgs :: Delimited ( ref mut args) => {
2211+ let tokens =
2212+ filter_tokens_from_list ( args. tokens . clone ( ) , |token| {
2213+ !matches ! (
2214+ token,
2215+ TokenTree :: Token (
2216+ Token {
2217+ kind: TokenKind :: Ident (
2218+ sym:: hidden | sym:: inline | sym:: no_inline,
2219+ _,
2220+ ) ,
2221+ ..
2222+ } ,
2223+ _,
2224+ ) ,
2225+ )
2226+ } ) ;
2227+ args. tokens = TokenStream :: new ( tokens) ;
2228+ attrs. push ( attr) ;
2229+ }
2230+ ast:: AttrArgs :: Empty | ast:: AttrArgs :: Eq ( ..) => {
2231+ attrs. push ( attr) ;
2232+ continue ;
2233+ }
2234+ }
2235+ }
2236+ }
2237+ ast:: AttrKind :: DocComment ( ..) => {
2238+ attrs. push ( attr) ;
2239+ }
2240+ }
2241+ }
2242+ }
2243+
21272244fn clean_maybe_renamed_item < ' tcx > (
21282245 cx : & mut DocContext < ' tcx > ,
21292246 item : & hir:: Item < ' tcx > ,
@@ -2212,19 +2329,20 @@ fn clean_maybe_renamed_item<'tcx>(
22122329 {
22132330 // First, we add the attributes from the current import.
22142331 extra_attrs. extend_from_slice ( inline:: load_attrs ( cx, import_id. to_def_id ( ) ) ) ;
2332+ let is_inline = extra_attrs. lists ( sym:: doc) . get_word_attr ( sym:: inline) . is_some ( ) ;
22152333 // Then we get all the various imports' attributes.
2216- get_all_import_attributes ( use_node, cx. tcx , item. owner_id . def_id , & mut extra_attrs) ;
2334+ get_all_import_attributes ( use_node, cx. tcx , item. owner_id . def_id , & mut extra_attrs, is_inline) ;
2335+ add_without_unwanted_attributes ( & mut extra_attrs, inline:: load_attrs ( cx, def_id) , is_inline) ;
2336+ } else {
2337+ // We only keep the item's attributes.
2338+ extra_attrs. extend_from_slice ( inline:: load_attrs ( cx, def_id) ) ;
22172339 }
22182340
2219- let mut item = if !extra_attrs. is_empty ( ) {
2220- extra_attrs. extend_from_slice ( inline:: load_attrs ( cx, def_id) ) ;
2221- let attrs = Attributes :: from_ast ( & extra_attrs) ;
2222- let cfg = extra_attrs. cfg ( cx. tcx , & cx. cache . hidden_cfg ) ;
2341+ let attrs = Attributes :: from_ast ( & extra_attrs) ;
2342+ let cfg = extra_attrs. cfg ( cx. tcx , & cx. cache . hidden_cfg ) ;
22232343
2224- Item :: from_def_id_and_attrs_and_parts ( def_id, Some ( name) , kind, Box :: new ( attrs) , cfg)
2225- } else {
2226- Item :: from_def_id_and_parts ( def_id, Some ( name) , kind, cx)
2227- } ;
2344+ let mut item =
2345+ Item :: from_def_id_and_attrs_and_parts ( def_id, Some ( name) , kind, Box :: new ( attrs) , cfg) ;
22282346 item. inline_stmt_id = import_id. map ( |def_id| def_id. to_def_id ( ) ) ;
22292347 vec ! [ item]
22302348 } )
0 commit comments