@@ -32,11 +32,28 @@ pub(crate) struct Module<'hir> {
3232 pub ( crate ) def_id : LocalDefId ,
3333 pub ( crate ) renamed : Option < Symbol > ,
3434 pub ( crate ) import_id : Option < LocalDefId > ,
35- /// The key is the item `ItemId` and the value is: (item, renamed, import_id).
35+ /// The key is the item `ItemId` and the value is: (item, renamed, Vec< import_id> ).
3636 /// We use `FxIndexMap` to keep the insert order.
37+ ///
38+ /// `import_id` needs to be a `Vec` because we live in a dark world where you can have code
39+ /// like:
40+ ///
41+ /// ```
42+ /// mod raw {
43+ /// pub fn foo() {}
44+ /// }
45+ ///
46+ /// /// Foobar
47+ /// pub use raw::foo;
48+ ///
49+ /// pub use raw::*;
50+ /// ```
51+ ///
52+ /// So in this case, we don't want to have two items but just one with attributes from both
53+ /// imports to be merged.
3754 pub ( crate ) items : FxIndexMap <
3855 ( LocalDefId , Option < Symbol > ) ,
39- ( & ' hir hir:: Item < ' hir > , Option < Symbol > , Option < LocalDefId > ) ,
56+ ( & ' hir hir:: Item < ' hir > , Option < Symbol > , Vec < LocalDefId > ) ,
4057 > ,
4158 /// Same as for `items`.
4259 pub ( crate ) inlined_foreigns : FxIndexMap < ( DefId , Option < Symbol > ) , ( Res , LocalDefId ) > ,
@@ -145,7 +162,9 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
145162 {
146163 let item = self . cx . tcx . hir_expect_item ( local_def_id) ;
147164 let ( ident, _, _) = item. expect_macro ( ) ;
148- top_level_module. items . insert ( ( local_def_id, Some ( ident. name ) ) , ( item, None , None ) ) ;
165+ top_level_module
166+ . items
167+ . insert ( ( local_def_id, Some ( ident. name ) ) , ( item, None , Vec :: new ( ) ) ) ;
149168 }
150169 }
151170
@@ -227,7 +246,6 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
227246 ) -> bool {
228247 debug ! ( "maybe_inline_local (renamed: {renamed:?}) res: {res:?}" ) ;
229248
230- let glob = renamed. is_none ( ) ;
231249 if renamed == Some ( kw:: Underscore ) {
232250 // We never inline `_` reexports.
233251 return false ;
@@ -252,14 +270,15 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
252270 return false ;
253271 }
254272
273+ let is_glob = renamed. is_none ( ) ;
255274 let is_hidden = !document_hidden && tcx. is_doc_hidden ( ori_res_did) ;
256275 let Some ( res_did) = ori_res_did. as_local ( ) else {
257276 // For cross-crate impl inlining we need to know whether items are
258277 // reachable in documentation -- a previously unreachable item can be
259278 // made reachable by cross-crate inlining which we're checking here.
260279 // (this is done here because we need to know this upfront).
261280 crate :: visit_lib:: lib_embargo_visit_item ( self . cx , ori_res_did) ;
262- if is_hidden || glob {
281+ if is_hidden || is_glob {
263282 return false ;
264283 }
265284 // We store inlined foreign items otherwise, it'd mean that the `use` item would be kept
@@ -307,10 +326,10 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
307326 // Bang macros are handled a bit on their because of how they are handled by the
308327 // compiler. If they have `#[doc(hidden)]` and the re-export doesn't have
309328 // `#[doc(inline)]`, then we don't inline it.
310- Node :: Item ( _) if is_bang_macro && !please_inline && renamed . is_some ( ) && is_hidden => {
329+ Node :: Item ( _) if is_bang_macro && !please_inline && !is_glob && is_hidden => {
311330 return false ;
312331 }
313- Node :: Item ( & hir:: Item { kind : hir:: ItemKind :: Mod ( _, m) , .. } ) if glob => {
332+ Node :: Item ( & hir:: Item { kind : hir:: ItemKind :: Mod ( _, m) , .. } ) if is_glob => {
314333 let prev = mem:: replace ( & mut self . inlining , true ) ;
315334 for & i in m. item_ids {
316335 let i = tcx. hir_item ( i) ;
@@ -319,13 +338,13 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
319338 self . inlining = prev;
320339 true
321340 }
322- Node :: Item ( it) if !glob => {
341+ Node :: Item ( it) if !is_glob => {
323342 let prev = mem:: replace ( & mut self . inlining , true ) ;
324343 self . visit_item_inner ( it, renamed, Some ( def_id) ) ;
325344 self . inlining = prev;
326345 true
327346 }
328- Node :: ForeignItem ( it) if !glob => {
347+ Node :: ForeignItem ( it) if !is_glob => {
329348 let prev = mem:: replace ( & mut self . inlining , true ) ;
330349 self . visit_foreign_item_inner ( it, renamed) ;
331350 self . inlining = prev;
@@ -369,8 +388,8 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
369388 fn add_to_current_mod (
370389 & mut self ,
371390 item : & ' tcx hir:: Item < ' _ > ,
372- renamed : Option < Symbol > ,
373- parent_id : Option < LocalDefId > ,
391+ mut renamed : Option < Symbol > ,
392+ import_id : Option < LocalDefId > ,
374393 ) {
375394 if self . is_importable_from_parent
376395 // If we're inside an item, only impl blocks and `macro_rules!` with the `macro_export`
@@ -383,11 +402,21 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
383402 _ => false ,
384403 }
385404 {
386- self . modules
387- . last_mut ( )
388- . unwrap ( )
389- . items
390- . insert ( ( item. owner_id . def_id , renamed) , ( item, renamed, parent_id) ) ;
405+ if renamed == item. kind . ident ( ) . map ( |ident| ident. name ) {
406+ renamed = None ;
407+ }
408+ let key = ( item. owner_id . def_id , renamed) ;
409+ if let Some ( import_id) = import_id {
410+ self . modules
411+ . last_mut ( )
412+ . unwrap ( )
413+ . items
414+ . entry ( key)
415+ . and_modify ( |v| v. 2 . push ( import_id) )
416+ . or_insert_with ( || ( item, renamed, vec ! [ import_id] ) ) ;
417+ } else {
418+ self . modules . last_mut ( ) . unwrap ( ) . items . insert ( key, ( item, renamed, Vec :: new ( ) ) ) ;
419+ }
391420 }
392421 }
393422
@@ -459,7 +488,7 @@ impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
459488 _ => false ,
460489 } ) ;
461490 let ident = match kind {
462- hir:: UseKind :: Single ( ident) => Some ( renamed . unwrap_or ( ident. name ) ) ,
491+ hir:: UseKind :: Single ( ident) => Some ( ident. name ) ,
463492 hir:: UseKind :: Glob => None ,
464493 hir:: UseKind :: ListStem => unreachable ! ( ) ,
465494 } ;
0 commit comments