From ee7dba4db8964b3db2aca0eb8b8fec81178792b8 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 5 Jul 2023 18:36:10 +0200 Subject: [PATCH 01/17] If re-export is private, get the next item until a public one is found or expose the private item directly --- src/librustdoc/clean/mod.rs | 96 +++++++++++++++++++++++++++++++++++-- 1 file changed, 93 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index d14953f1bb7..fb232c4aac8 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1496,8 +1496,93 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx) } +/// 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_not_private( + cx: &mut DocContext<'_>, + hir_id: hir::HirId, + path: &hir::Path<'_>, +) -> Option { + if path.segments.is_empty() { + return None; + } + let parent_def_id = if path.segments.len() == 1 { + // Then it's available in the same scope as the owner. + hir_id.owner.def_id + } else { + // It's not available in the same scope, so we start from the parent of the item. + path.segments[path.segments.len() - 2].res.opt_def_id()?.as_local()? + }; + let target_def_id = path.res.opt_def_id()?; + let mut ident = path.segments.last().unwrap().ident; + // First we try to get the `DefId` of the item. + for child in cx + .tcx + .module_children_local(cx.tcx.local_parent(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(); + // let parent_mod = hir.local_def_id_to_hir_id(); + 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 { + match item.kind { + hir::ItemKind::Use(path, _) => { + for res in &path.res { + if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { + continue; + } + if !cx.tcx.is_doc_hidden(use_def_id) && + 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((path, res)) = last_path_res { + let path = hir::Path { segments: path.segments, res: *res, span: path.span }; + return Some(clean_path(&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 +} + fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { - let hir::Ty { hir_id: _, span, ref kind } = *hir_ty; + let hir::Ty { hir_id, span, ref kind } = *hir_ty; let hir::TyKind::Path(qpath) = kind else { unreachable!() }; match qpath { @@ -1514,7 +1599,12 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type if let Some(expanded) = maybe_expand_private_type_alias(cx, path) { expanded } else { - let path = clean_path(path, cx); + // First we check if it's a private re-export. + let path = if let Some(path) = first_not_private(cx, hir_id, &path) { + path + } else { + clean_path(path, cx) + }; resolve_type(cx, path) } } @@ -1665,7 +1755,7 @@ fn maybe_expand_private_type_alias<'tcx>( } } - Some(cx.enter_alias(args, def_id.to_def_id(), |cx| clean_ty(ty, cx))) + Some(cx.enter_alias(args, def_id.to_def_id(), |cx| clean_ty(&ty, cx))) } pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type { From d67a31f0581f95752a16061668b2700bd78e0eca Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Wed, 5 Jul 2023 18:45:42 +0200 Subject: [PATCH 02/17] Add regression test for #81141 --- ...ue-81141-private-reexport-in-public-api.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 tests/rustdoc/issue-81141-private-reexport-in-public-api.rs diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs new file mode 100644 index 00000000000..d6ef8435429 --- /dev/null +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs @@ -0,0 +1,34 @@ +// 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 +} From 078e9029526f1ea97a86c013142c0ce6a8b9ec0d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 7 Jul 2023 13:55:31 +0200 Subject: [PATCH 03/17] Improve code readability --- src/librustdoc/clean/mod.rs | 59 +++++++++++++++++-------------------- 1 file changed, 27 insertions(+), 32 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index fb232c4aac8..17da8bd2d44 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1505,24 +1505,25 @@ fn first_not_private( hir_id: hir::HirId, path: &hir::Path<'_>, ) -> Option { - if path.segments.is_empty() { - return None; - } - let parent_def_id = if path.segments.len() == 1 { - // Then it's available in the same scope as the owner. - hir_id.owner.def_id - } else { - // It's not available in the same scope, so we start from the parent of the item. - path.segments[path.segments.len() - 2].res.opt_def_id()?.as_local()? + 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 parent.ident.name == kw::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()?; - let mut ident = path.segments.last().unwrap().ident; // First we try to get the `DefId` of the item. - for child in cx - .tcx - .module_children_local(cx.tcx.local_parent(parent_def_id)) - .iter() - .filter(move |c| c.ident == ident) + 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; @@ -1535,26 +1536,20 @@ fn first_not_private( let Some(local_use_def_id) = use_def_id.as_local() { let hir = cx.tcx.hir(); - // let parent_mod = hir.local_def_id_to_hir_id(); 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 { - match item.kind { - hir::ItemKind::Use(path, _) => { - for res in &path.res { - if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { - continue; - } - if !cx.tcx.is_doc_hidden(use_def_id) && - 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 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.tcx.is_doc_hidden(use_def_id) && + 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; } } } From 278d15c254b14199e531b34b209dac9030000d5b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 7 Jul 2023 13:55:47 +0200 Subject: [PATCH 04/17] Extend issue-81141-private-reexport-in-public-api test to cover more cases --- ...ue-81141-private-reexport-in-public-api.rs | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs index d6ef8435429..312146e7957 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs @@ -32,3 +32,83 @@ pub fn bar2() -> Whatever3 { 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 +} From 6b56e8e1cfedadd25c8c0cb6ecd8a4a8640158ff Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 7 Jul 2023 16:56:21 +0200 Subject: [PATCH 05/17] Rename `first_not_private` into `first_non_private` --- src/librustdoc/clean/mod.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 17da8bd2d44..ac569130cb2 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1500,7 +1500,7 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( /// 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_not_private( +fn first_non_private( cx: &mut DocContext<'_>, hir_id: hir::HirId, path: &hir::Path<'_>, @@ -1595,7 +1595,7 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type expanded } else { // First we check if it's a private re-export. - let path = if let Some(path) = first_not_private(cx, hir_id, &path) { + let path = if let Some(path) = first_non_private(cx, hir_id, &path) { path } else { clean_path(path, cx) From 9fb65489057ca6f4e240c6f0c373127d06da316d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 7 Jul 2023 17:55:33 +0200 Subject: [PATCH 06/17] Correctly handle `super` and `::` --- src/librustdoc/clean/mod.rs | 11 ++++++++++- .../issue-81141-private-reexport-in-public-api-2.rs | 13 +++++++++++++ .../issue-81141-private-reexport-in-public-api.rs | 10 ++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index ac569130cb2..0be93d9b057 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1514,9 +1514,18 @@ fn first_non_private( (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident) } // Crate paths are not. We start from the crate root. - [parent, leaf] if parent.ident.name == kw::Crate => { + [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), }; diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs new file mode 100644 index 00000000000..4e9d188bbf8 --- /dev/null +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api-2.rs @@ -0,0 +1,13 @@ +// 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 } +} diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs index 312146e7957..bd54d02c6ec 100644 --- a/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api.rs @@ -112,3 +112,13 @@ pub fn bar14() -> nested::nested2::Whatever3 { 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 } +} From 298cd366d510bbcdf3fd061debd6bb011e681ad1 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jul 2023 14:10:26 +0200 Subject: [PATCH 07/17] Add test for private items --- src/librustdoc/clean/mod.rs | 3 ++ ...-private-reexport-in-public-api-private.rs | 32 +++++++++++++++++++ 2 files changed, 35 insertions(+) create mode 100644 tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 0be93d9b057..b1fdb4125a3 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1553,6 +1553,9 @@ fn first_non_private( continue; } if !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; } diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs new file mode 100644 index 00000000000..15749674a3d --- /dev/null +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api-private.rs @@ -0,0 +1,32 @@ +// 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 +} From d29afe2e145ae2d337a559484efd9098805d0dd6 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jul 2023 14:21:02 +0200 Subject: [PATCH 08/17] Add support for `--document-hidden-items` in `first_non_private` --- src/librustdoc/clean/mod.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b1fdb4125a3..702b58b1362 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1552,7 +1552,8 @@ fn first_non_private( if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res { continue; } - if !cx.tcx.is_doc_hidden(use_def_id) && + 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. From 5859b44e97d10b9e30d37b0f87d0fac68dfbc1fd Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jul 2023 14:21:25 +0200 Subject: [PATCH 09/17] Add test for `--document-hidden-items` --- ...1141-private-reexport-in-public-api-hidden.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs new file mode 100644 index 00000000000..5053a328bad --- /dev/null +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api-hidden.rs @@ -0,0 +1,16 @@ +// 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 +} From 49ccde0054f4b6c9be6987a28211b0101613529e Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jul 2023 17:00:55 +0200 Subject: [PATCH 10/17] Re-add missing generics in `first_non_private` --- src/librustdoc/clean/mod.rs | 26 +++++++++++++++++++++++--- 1 file changed, 23 insertions(+), 3 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 702b58b1362..b5c257d8d3d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1505,6 +1505,8 @@ fn first_non_private( hir_id: hir::HirId, path: &hir::Path<'_>, ) -> Option { + 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. @@ -1574,9 +1576,27 @@ fn first_non_private( // // 1. We found a public reexport. // 2. We didn't find a public reexport so it's the "end type" path. - if let Some((path, res)) = last_path_res { - let path = hir::Path { segments: path.segments, res: *res, span: path.span }; - return Some(clean_path(&path, cx)); + 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: // From 95cea621cdf701a4dfaa668af78a4e80f4e94f98 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Mon, 10 Jul 2023 17:01:20 +0200 Subject: [PATCH 11/17] Add regression test for generics reexport of private import --- ...81141-private-reexport-in-public-api-generics.rs | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs diff --git a/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs b/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs new file mode 100644 index 00000000000..7e289508628 --- /dev/null +++ b/tests/rustdoc/issue-81141-private-reexport-in-public-api-generics.rs @@ -0,0 +1,13 @@ +#![crate_name = "foo"] + +use crate::bar::Foo as Alias; + +pub mod bar { + pub struct Foo<'a, T>(&'a T); +} + +// @has "foo/fn.foo.html" +// @has - '//*[@class="rust item-decl"]/code' "pub fn foo<'a, T>(f: Foo<'a, T>) -> Foo<'a, usize>" +pub fn foo<'a, T>(f: Alias<'a, T>) -> Alias<'a, usize> { + Alias(&0) +} From bc7d958ab2768d022a3ed6af96a222543b13e1a3 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 11 Jul 2023 11:38:31 +0200 Subject: [PATCH 12/17] Remove needs for transmute --- src/librustdoc/clean/mod.rs | 37 ++++++++++++++++------------------- src/librustdoc/clean/types.rs | 11 +++++++++++ 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b5c257d8d3d..9aaef40aa6b 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1500,13 +1500,11 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( /// 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<'_>, +fn first_non_private<'tcx>( + cx: &mut DocContext<'tcx>, hir_id: hir::HirId, - path: &hir::Path<'_>, + path: &hir::Path<'tcx>, ) -> Option { - 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. @@ -1577,26 +1575,25 @@ fn first_non_private( // 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 { + let new_hir_path = hir::Path { + segments: new_path.segments, + res: path.res, + span: new_path.span, + }; + let mut new_clean_path = clean_path(&new_hir_path, cx); // 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; + if let Some(path_last) = path.segments.last().as_ref() + && let Some(new_path_last) = new_clean_path.segments[..].last_mut() + && let Some(path_last_args) = path_last.args.as_ref() + && path_last.args.is_some() + { + assert!(new_path_last.args.is_empty()); + new_path_last.args = clean_generic_args(path_last_args, cx); } - // `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)); + return Some(new_clean_path); } // If `last_path_res` is `None`, it can mean two things: // diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ddef165a054..60e316decf9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2203,6 +2203,17 @@ pub(crate) enum GenericArgs { Parenthesized { inputs: Box<[Type]>, output: Option> }, } +impl GenericArgs { + pub(crate) fn is_empty(&self) -> bool { + match self { + GenericArgs::AngleBracketed { args, bindings } => { + args.is_empty() && bindings.is_empty() + } + GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), + } + } +} + #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct PathSegment { pub(crate) name: Symbol, From 662c16771184a26ca0a69f6bf66b13467627d59d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 13 Jul 2023 13:54:23 +0200 Subject: [PATCH 13/17] Revert "Remove needs for transmute" This reverts commit ea9a17b9995b7a076283777b7d462a360fece2d6. --- src/librustdoc/clean/mod.rs | 37 +++++++++++++++++++---------------- src/librustdoc/clean/types.rs | 11 ----------- 2 files changed, 20 insertions(+), 28 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 9aaef40aa6b..b5c257d8d3d 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1500,11 +1500,13 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( /// 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<'tcx>( - cx: &mut DocContext<'tcx>, +fn first_non_private( + cx: &mut DocContext<'_>, hir_id: hir::HirId, - path: &hir::Path<'tcx>, + path: &hir::Path<'_>, ) -> Option { + 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. @@ -1575,25 +1577,26 @@ fn first_non_private<'tcx>( // 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 { - let new_hir_path = hir::Path { - segments: new_path.segments, - res: path.res, - span: new_path.span, - }; - let mut new_clean_path = clean_path(&new_hir_path, cx); // 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. - if let Some(path_last) = path.segments.last().as_ref() - && let Some(new_path_last) = new_clean_path.segments[..].last_mut() - && let Some(path_last_args) = path_last.args.as_ref() - && path_last.args.is_some() - { - assert!(new_path_last.args.is_empty()); - new_path_last.args = clean_generic_args(path_last_args, cx); + 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; } - return Some(new_clean_path); + // `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: // diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index 60e316decf9..ddef165a054 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2203,17 +2203,6 @@ pub(crate) enum GenericArgs { Parenthesized { inputs: Box<[Type]>, output: Option> }, } -impl GenericArgs { - pub(crate) fn is_empty(&self) -> bool { - match self { - GenericArgs::AngleBracketed { args, bindings } => { - args.is_empty() && bindings.is_empty() - } - GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), - } - } -} - #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct PathSegment { pub(crate) name: Symbol, From 988729d842be9fddeecdf8bd9c8cf018b511ed8b Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 20 Jul 2023 15:09:22 +0200 Subject: [PATCH 14/17] Cache qpath first public result --- src/librustdoc/clean/mod.rs | 72 +++++++++++++++++++++++-------------- src/librustdoc/core.rs | 5 ++- 2 files changed, 50 insertions(+), 27 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index b5c257d8d3d..2ffe38c2403 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1496,17 +1496,55 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx) } +fn first_non_private_clean_path<'tcx>( + cx: &mut DocContext<'tcx>, + path: &hir::Path<'tcx>, + mut new_path_segments: Vec>, + new_path_span: rustc_span::Span, +) -> Path { + use std::mem::transmute; + + // 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. + if let Some(last) = new_path_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 path = unsafe { + hir::Path { + segments: transmute(new_path_segments.as_slice()), + res: path.res, + span: new_path_span, + } + }; + clean_path(&path, cx) +} + /// 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<'_>, +fn first_non_private<'tcx>( + cx: &mut DocContext<'tcx>, hir_id: hir::HirId, - path: &hir::Path<'_>, + path: &hir::Path<'tcx>, ) -> Option { - use std::mem::transmute; - + let use_id = path.segments.last().map(|seg| seg.hir_id)?; + let target_def_id = path.res.opt_def_id()?; + let saved_path = cx + .updated_qpath + .borrow() + .get(&use_id) + .map(|saved_path| (saved_path.segments.to_vec(), saved_path.span)); + if let Some((segments, span)) = saved_path { + return Some(first_non_private_clean_path(cx, path, segments, span)); + } let (parent_def_id, mut ident) = match &path.segments[..] { [] => return None, // Relative paths are available in the same scope as the owner. @@ -1531,7 +1569,6 @@ fn first_non_private( // 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) @@ -1577,26 +1614,9 @@ fn first_non_private( // 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)); + cx.updated_qpath.borrow_mut().insert(use_id, new_path.clone()); + let new_path_segments = new_path.segments.to_vec(); + return Some(first_non_private_clean_path(cx, path, new_path_segments, new_path.span)); } // If `last_path_res` is `None`, it can mean two things: // diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 7fb069d6e70..6cccb403491 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -8,7 +8,7 @@ use rustc_feature::UnstableFeatures; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{HirId, Path}; +use rustc_hir::{HirId, Path, UsePath}; use rustc_interface::interface; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; @@ -63,6 +63,8 @@ pub(crate) struct DocContext<'tcx> { pub(crate) output_format: OutputFormat, /// Used by `strip_private`. pub(crate) show_coverage: bool, + /// Used by `first_non_private` to prevent computing the same path more than once. + pub(crate) updated_qpath: RefCell>>, } impl<'tcx> DocContext<'tcx> { @@ -352,6 +354,7 @@ pub(crate) fn run_global_ctxt( output_format, render_options, show_coverage, + updated_qpath: Default::default(), }; for cnum in tcx.crates(()) { From 1f828f01552b54abb8e33610484d9269067fcc28 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Jul 2023 15:39:45 +0200 Subject: [PATCH 15/17] Improve performance of `first_non_private` --- src/librustdoc/clean/mod.rs | 40 +++++++++++++++++-------------------- 1 file changed, 18 insertions(+), 22 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 2ffe38c2403..743ba6835db 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1545,7 +1545,7 @@ fn first_non_private<'tcx>( if let Some((segments, span)) = saved_path { return Some(first_non_private_clean_path(cx, path, segments, span)); } - let (parent_def_id, mut ident) = match &path.segments[..] { + let (parent_def_id, 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), @@ -1569,6 +1569,7 @@ fn first_non_private<'tcx>( // Absolute paths are not. We start from the parent of the item. [.., parent, leaf] => (parent.res.opt_def_id()?.as_local()?, leaf.ident), }; + let hir = cx.tcx.hir(); // 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) @@ -1581,29 +1582,24 @@ fn first_non_private<'tcx>( 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 Some(local_use_def_id) = use_def_id.as_local() && + let Some(hir::Node::Item(item)) = hir.find_by_def_id(local_use_def_id) && + let hir::ItemKind::Use(path, _) = item.kind { - 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; - } + 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; + } + last_path_res = Some((path, res)); + continue 'reexps; } } } From 51eb0c3b361093b880009b997dc69cddcd73e9dc Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 25 Jul 2023 23:14:09 +0200 Subject: [PATCH 16/17] Fix regression for private in public --- src/librustdoc/clean/mod.rs | 1 + .../auxiliary/jump-to-def-res-err-handling-aux.rs | 1 + tests/rustdoc/private-use.rs | 13 +++++++++++++ 3 files changed, 15 insertions(+) create mode 100644 tests/rustdoc/auxiliary/jump-to-def-res-err-handling-aux.rs create mode 100644 tests/rustdoc/private-use.rs diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 743ba6835db..998380abfac 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1584,6 +1584,7 @@ fn first_non_private<'tcx>( if let Some(use_def_id) = reexp.id() && let Some(local_use_def_id) = use_def_id.as_local() && let Some(hir::Node::Item(item)) = hir.find_by_def_id(local_use_def_id) && + !item.ident.name.is_empty() && let hir::ItemKind::Use(path, _) = item.kind { for res in &path.res { diff --git a/tests/rustdoc/auxiliary/jump-to-def-res-err-handling-aux.rs b/tests/rustdoc/auxiliary/jump-to-def-res-err-handling-aux.rs new file mode 100644 index 00000000000..eacec957a2a --- /dev/null +++ b/tests/rustdoc/auxiliary/jump-to-def-res-err-handling-aux.rs @@ -0,0 +1 @@ +pub struct Ident; diff --git a/tests/rustdoc/private-use.rs b/tests/rustdoc/private-use.rs new file mode 100644 index 00000000000..689ed73140d --- /dev/null +++ b/tests/rustdoc/private-use.rs @@ -0,0 +1,13 @@ +// Regression test for to +// ensure it doesn't panic. + +mod generics { + pub enum WherePredicate { + EqPredicate, + } +} +pub mod visit { + use *; + pub fn visit_where_predicate(_visitor: &mut V, _i: &WherePredicate) {} +} +pub use generics::*; From 2461d0cf9cca7e437e17cc251496963caeac051d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Thu, 27 Jul 2023 10:54:10 +0200 Subject: [PATCH 17/17] Remove transmute calls and caching for use paths --- src/librustdoc/clean/mod.rs | 43 +++++++++++------------------------ src/librustdoc/clean/types.rs | 11 +++++++++ src/librustdoc/core.rs | 5 +--- 3 files changed, 25 insertions(+), 34 deletions(-) diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 998380abfac..fd38d1c045e 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -1499,31 +1499,25 @@ pub(crate) fn clean_middle_assoc_item<'tcx>( fn first_non_private_clean_path<'tcx>( cx: &mut DocContext<'tcx>, path: &hir::Path<'tcx>, - mut new_path_segments: Vec>, + new_path_segments: &'tcx [hir::PathSegment<'tcx>], new_path_span: rustc_span::Span, ) -> Path { - use std::mem::transmute; - + let new_hir_path = + hir::Path { segments: new_path_segments, res: path.res, span: new_path_span }; + let mut new_clean_path = clean_path(&new_hir_path, cx); // 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. - if let Some(last) = new_path_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; + if let Some(path_last) = path.segments.last().as_ref() + && let Some(new_path_last) = new_clean_path.segments[..].last_mut() + && let Some(path_last_args) = path_last.args.as_ref() + && path_last.args.is_some() + { + assert!(new_path_last.args.is_empty()); + new_path_last.args = clean_generic_args(path_last_args, cx); } - // `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 path = unsafe { - hir::Path { - segments: transmute(new_path_segments.as_slice()), - res: path.res, - span: new_path_span, - } - }; - clean_path(&path, cx) + new_clean_path } /// The goal of this function is to return the first `Path` which is not private (ie not private @@ -1535,16 +1529,7 @@ fn first_non_private<'tcx>( hir_id: hir::HirId, path: &hir::Path<'tcx>, ) -> Option { - let use_id = path.segments.last().map(|seg| seg.hir_id)?; let target_def_id = path.res.opt_def_id()?; - let saved_path = cx - .updated_qpath - .borrow() - .get(&use_id) - .map(|saved_path| (saved_path.segments.to_vec(), saved_path.span)); - if let Some((segments, span)) = saved_path { - return Some(first_non_private_clean_path(cx, path, segments, span)); - } let (parent_def_id, ident) = match &path.segments[..] { [] => return None, // Relative paths are available in the same scope as the owner. @@ -1611,9 +1596,7 @@ fn first_non_private<'tcx>( // 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 { - cx.updated_qpath.borrow_mut().insert(use_id, new_path.clone()); - let new_path_segments = new_path.segments.to_vec(); - return Some(first_non_private_clean_path(cx, path, new_path_segments, new_path.span)); + return Some(first_non_private_clean_path(cx, path, new_path.segments, new_path.span)); } // If `last_path_res` is `None`, it can mean two things: // diff --git a/src/librustdoc/clean/types.rs b/src/librustdoc/clean/types.rs index ddef165a054..60e316decf9 100644 --- a/src/librustdoc/clean/types.rs +++ b/src/librustdoc/clean/types.rs @@ -2203,6 +2203,17 @@ pub(crate) enum GenericArgs { Parenthesized { inputs: Box<[Type]>, output: Option> }, } +impl GenericArgs { + pub(crate) fn is_empty(&self) -> bool { + match self { + GenericArgs::AngleBracketed { args, bindings } => { + args.is_empty() && bindings.is_empty() + } + GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(), + } + } +} + #[derive(Clone, PartialEq, Eq, Debug, Hash)] pub(crate) struct PathSegment { pub(crate) name: Symbol, diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 6cccb403491..7fb069d6e70 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -8,7 +8,7 @@ use rustc_feature::UnstableFeatures; use rustc_hir::def::Res; use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId}; use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{HirId, Path, UsePath}; +use rustc_hir::{HirId, Path}; use rustc_interface::interface; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{ParamEnv, Ty, TyCtxt}; @@ -63,8 +63,6 @@ pub(crate) struct DocContext<'tcx> { pub(crate) output_format: OutputFormat, /// Used by `strip_private`. pub(crate) show_coverage: bool, - /// Used by `first_non_private` to prevent computing the same path more than once. - pub(crate) updated_qpath: RefCell>>, } impl<'tcx> DocContext<'tcx> { @@ -354,7 +352,6 @@ pub(crate) fn run_global_ctxt( output_format, render_options, show_coverage, - updated_qpath: Default::default(), }; for cnum in tcx.crates(()) {