Change:
rustdoc renders renamed imports () using the new, private name (e.g., in ). This is confusing behavior since the new name is an implementation detail. I think it should be using the original item's name instead. As an example, returns a , but the module it's defined in renames to . I would expect rustdoc to still show the item's definition name, but instead it shows the private name.
I don't think this should be changed until is fixed. Otherwise you could have two items shown with the same name and namespace in the same scope.
Code to reproduce: I don't think we need to wait for to be fixed as the fix for this doesn't impact it: if the re-export is not publicly reexported, we simply show the item underneath. When we will it will handle it the same.

Discussion:
Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx) } <ins> /// The goal of this function is to return the first `Path` which is not private (ie not private /// or `doc(hidden)`). If it's not possible, it'll return the "end type". /// /// If the path is not a re-export or is public, it'll return `None`. fn first_non_private( cx: &mut DocContext<'_>, hir_id: hir::HirId, path: &hir::Path<'_>, ) -> Option<Path> { use std::mem::transmute; let (parent_def_id, mut ident) = match &path.segments[..] { [] => return None, // Relative paths are available in the same scope as the owner. [leaf] => (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident), // So are self paths. [parent, leaf] if parent.ident.name == kw::SelfLower => { (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident) } // Crate paths are not. We start from the crate root. [parent, leaf] if matches!(parent.ident.name, kw::Crate | kw::PathRoot) => { (LOCAL_CRATE.as_def_id().as_local()?, leaf.ident) } [parent, leaf] if parent.ident.name == kw::Super => { let parent_mod = cx.tcx.parent_module(hir_id); if let Some(super_parent) = cx.tcx.opt_local_parent(parent_mod) { (super_parent, leaf.ident) } else { // If we can't find the parent of the parent, then the parent is already the crate. (LOCAL_CRATE.as_def_id().as_local()?, leaf.ident) } } // Absolute paths are not. We start from the parent of the item. [.., parent, leaf] => (parent.res.opt_def_id()?.as_local()?, leaf.ident), }; let target_def_id = path.res.opt_def_id()?; // First we try to get the `DefId` of the item. for child in cx.tcx.module_children_local(parent_def_id).iter().filter(move |c| c.ident == ident) { if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = child.res { continue; } if let Some(def_id) = child.res.opt_def_id() && target_def_id == def_id { let mut last_path_res = None; 'reexps: for reexp in child.reexport_chain.iter() { if let Some(use_def_id) = reexp.id() && let Some(local_use_def_id) = use_def_id.as_local() { let hir = cx.tcx.hir(); for item_id in hir.module_items(cx.tcx.local_parent(local_use_def_id)) { let item = hir.item(item_id); if item.ident == ident && let hir::ItemKind::Use(path, _) = item.kind { for res in &path.res { if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { continue; } if (cx.render_options.document_hidden || !cx.tcx.is_doc_hidden(use_def_id)) && // We never check for "cx.render_options.document_private" // because if a re-export is not fully public, it's never // documented. cx.tcx.local_visibility(local_use_def_id).is_public() { break 'reexps; } ident = path.segments.last().unwrap().ident; last_path_res = Some((path, res)); continue 'reexps; } } } } } if !child.reexport_chain.is_empty() { // So in here, we use the data we gathered from iterating the reexports. If // `last_path_res` is set, it can mean two things: // // 1. We found a public reexport. // 2. We didn't find a public reexport so it's the "end type" path. if let Some((new_path, _)) = last_path_res { // In here we need to play with the path data one last time to provide it the // missing `args` and `res` of the final `Path` we get, which, since it comes // from a re-export, doesn't have the generics that were originally there, so // we add them by hand. let mut segments = new_path.segments.to_vec(); if let Some(last) = segments.last_mut() { // `transmute` is needed because we are using a wrong lifetime. Since // `segments` will be dropped at the end of this block, it's fine. last.args = unsafe { transmute( path.segments.last().as_ref().unwrap().args.clone(), ) }; last.res = path.res; } // `transmute` is needed because we are using a wrong lifetime. Since // `segments` will be dropped at the end of this block, it's fine. let segments = unsafe { transmute(segments.as_slice()) }; let new_path = hir::Path { segments, res: path.res, span: new_path.span }; return Some(clean_path(&new_path, cx)); } // If `last_path_res` is `None`, it can mean two things: // // 1. The re-export is public, no need to change anything, just use the path as is. // 2. Nothing was found, so let's just return the original path. return None; } } } None } </ins> fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { <del> let hir::Ty { hir_id: _, span, ref kind } = *hir_ty; </del> <ins> let hir::Ty { hir_id, span, ref kind } = *hir_ty; </ins> let hir::TyKind::Path(qpath) = kind else { unreachable!() }; match qpath {

if let Some(expanded) = maybe_expand_private_type_alias(cx, path) { expanded } else { <del> let path = clean_path(path, cx); </del> <ins> // First we check if it's a private re-export. let path = if let Some(path) = first_non_private(cx, hir_id, &path) { path } else { clean_path(path, cx) }; </ins> resolve_type(cx, path) } }

} } <del> Some(cx.enter_alias(args, def_id.to_def_id(), |cx| clean_ty(ty, cx))) </del> <ins> Some(cx.enter_alias(args, def_id.to_def_id(), |cx| clean_ty(&ty, cx))) </ins> } pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type {

<ins> // edition:2015 #![crate_name = "foo"] use external::Public as Private; pub mod external { pub struct Public; // @has 'foo/external/fn.make.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn make() -> Public' pub fn make() -> ::Private { super::Private } } </ins>

<ins> // compile-flags: -Z unstable-options --document-hidden-items #![crate_name = "foo"] #[doc(hidden)] pub use crate::bar::Bar as Alias; mod bar { pub struct Bar; } // @has 'foo/fn.bar.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar() -> Alias' pub fn bar() -> Alias { Alias } </ins>

<ins> // compile-flags: --document-private-items #![crate_name = "foo"] use crate::bar::Bar as Alias; pub(crate) use crate::bar::Bar as CrateAlias; mod bar { pub struct Bar; pub use self::Bar as Inner; } // It's a fully private re-export so it should not be displayed. // @has 'foo/fn.bar.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar() -> Bar' pub fn bar() -> Alias { Alias } // It's public re-export inside a private module so it should be visible. // @has 'foo/fn.bar2.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar2() -> Inner' pub fn bar2() -> crate::bar::Inner { Alias } // It's a non-public, so it doesn't appear in documentation so it should not be visible. // @has 'foo/fn.bar3.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar3() -> Bar' pub fn bar3() -> CrateAlias { Alias } </ins>

<ins> // This test ensures that if a private re-export is present in a public API, it'll be // replaced by the first public item in the re-export chain or by the private item. #![crate_name = "foo"] use crate::bar::Bar as Alias; pub use crate::bar::Bar as Whatever; use crate::Whatever as Whatever2; use crate::Whatever2 as Whatever3; pub use crate::bar::Inner as Whatever4; mod bar { pub struct Bar; pub use self::Bar as Inner; } // @has 'foo/fn.bar.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar() -> Bar' pub fn bar() -> Alias { Alias } // @has 'foo/fn.bar2.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar2() -> Whatever' pub fn bar2() -> Whatever3 { Whatever } // @has 'foo/fn.bar3.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar3() -> Whatever4' pub fn bar3() -> Whatever4 { Whatever } // @has 'foo/fn.bar4.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar4() -> Bar' pub fn bar4() -> crate::Alias { Alias } // @has 'foo/fn.bar5.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar5() -> Whatever' pub fn bar5() -> crate::Whatever3 { Whatever } // @has 'foo/fn.bar6.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar6() -> Whatever4' pub fn bar6() -> crate::Whatever4 { Whatever } // @has 'foo/fn.bar7.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar7() -> Bar' pub fn bar7() -> self::Alias { Alias } // @has 'foo/fn.bar8.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar8() -> Whatever' pub fn bar8() -> self::Whatever3 { Whatever } // @has 'foo/fn.bar9.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar9() -> Whatever4' pub fn bar9() -> self::Whatever4 { Whatever } mod nested { pub(crate) use crate::Alias; pub(crate) use crate::Whatever3; pub(crate) use crate::Whatever4; pub(crate) use crate::nested as nested2; } // @has 'foo/fn.bar10.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar10() -> Bar' pub fn bar10() -> nested::Alias { Alias } // @has 'foo/fn.bar11.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar11() -> Whatever' pub fn bar11() -> nested::Whatever3 { Whatever } // @has 'foo/fn.bar12.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar12() -> Whatever4' pub fn bar12() -> nested::Whatever4 { Whatever } // @has 'foo/fn.bar13.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar13() -> Bar' pub fn bar13() -> nested::nested2::Alias { Alias } // @has 'foo/fn.bar14.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar14() -> Whatever' pub fn bar14() -> nested::nested2::Whatever3 { Whatever } // @has 'foo/fn.bar15.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn bar15() -> Whatever4' pub fn bar15() -> nested::nested2::Whatever4 { Whatever } use external::Public as Private; pub mod external { pub struct Public; // @has 'foo/external/fn.make.html' // @has - '//*[@class="rust item-decl"]/code' 'pub fn make() -> Public' pub fn make() -> super::Private { super::Private } } </ins>