@@ -11,6 +11,8 @@ pub(crate) mod types;
11
11
pub ( crate ) mod utils;
12
12
13
13
use rustc_ast as ast;
14
+ use rustc_ast:: token:: { Token , TokenKind } ;
15
+ use rustc_ast:: tokenstream:: { TokenStream , TokenTree } ;
14
16
use rustc_attr as attr;
15
17
use rustc_data_structures:: fx:: { FxHashMap , FxHashSet , FxIndexMap , FxIndexSet , IndexEntry } ;
16
18
use rustc_hir as hir;
@@ -2079,8 +2081,8 @@ impl<'hir> hir::intravisit::Visitor<'hir> for OneLevelVisitor<'hir> {
2079
2081
fn visit_item ( & mut self , item : & ' hir hir:: Item < ' hir > ) {
2080
2082
if self . item . is_none ( )
2081
2083
&& 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 )
2084
2086
{
2085
2087
self . item = Some ( item) ;
2086
2088
}
@@ -2096,34 +2098,149 @@ fn get_all_import_attributes<'hir>(
2096
2098
tcx : TyCtxt < ' hir > ,
2097
2099
target_def_id : LocalDefId ,
2098
2100
attributes : & mut Vec < ast:: Attribute > ,
2101
+ is_inline : bool ,
2099
2102
) {
2100
2103
let hir_map = tcx. hir ( ) ;
2101
2104
let mut visitor = OneLevelVisitor :: new ( hir_map, target_def_id) ;
2102
2105
let mut visited = FxHashSet :: default ( ) ;
2103
2106
// 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
+ ) ;
2120
2137
}
2138
+ _ => break ,
2139
+ }
2140
+ if let Some ( i) = visitor. item {
2141
+ item = i;
2121
2142
} else {
2122
2143
break ;
2123
2144
}
2124
2145
}
2125
2146
}
2126
2147
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
+
2127
2244
fn clean_maybe_renamed_item < ' tcx > (
2128
2245
cx : & mut DocContext < ' tcx > ,
2129
2246
item : & hir:: Item < ' tcx > ,
@@ -2212,19 +2329,20 @@ fn clean_maybe_renamed_item<'tcx>(
2212
2329
{
2213
2330
// First, we add the attributes from the current import.
2214
2331
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 ( ) ;
2215
2333
// 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) ) ;
2217
2339
}
2218
2340
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 ) ;
2223
2343
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) ;
2228
2346
item. inline_stmt_id = import_id. map ( |def_id| def_id. to_def_id ( ) ) ;
2229
2347
vec ! [ item]
2230
2348
} )
0 commit comments