Skip to content

Commit 9339f44

Browse files
committed
Auto merge of #113374 - GuillaumeGomez:private-to-public-path, r=notriddle,fmease
[rustdoc] If re-export is private, get the next item until a public one is found or expose the private item directly Fixes #81141. If we have: ```rust use Private as Something; pub fn foo() -> Something {} ``` Then `Something` will be replaced by `Private`. r? `@notriddle`
2 parents b73e9a4 + 2461d0c commit 9339f44

9 files changed

+344
-3
lines changed

src/librustdoc/clean/mod.rs

+121-3
Original file line numberDiff line numberDiff line change
@@ -1514,8 +1514,121 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
15141514
Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx)
15151515
}
15161516

1517+
fn first_non_private_clean_path<'tcx>(
1518+
cx: &mut DocContext<'tcx>,
1519+
path: &hir::Path<'tcx>,
1520+
new_path_segments: &'tcx [hir::PathSegment<'tcx>],
1521+
new_path_span: rustc_span::Span,
1522+
) -> Path {
1523+
let new_hir_path =
1524+
hir::Path { segments: new_path_segments, res: path.res, span: new_path_span };
1525+
let mut new_clean_path = clean_path(&new_hir_path, cx);
1526+
// In here we need to play with the path data one last time to provide it the
1527+
// missing `args` and `res` of the final `Path` we get, which, since it comes
1528+
// from a re-export, doesn't have the generics that were originally there, so
1529+
// we add them by hand.
1530+
if let Some(path_last) = path.segments.last().as_ref()
1531+
&& let Some(new_path_last) = new_clean_path.segments[..].last_mut()
1532+
&& let Some(path_last_args) = path_last.args.as_ref()
1533+
&& path_last.args.is_some()
1534+
{
1535+
assert!(new_path_last.args.is_empty());
1536+
new_path_last.args = clean_generic_args(path_last_args, cx);
1537+
}
1538+
new_clean_path
1539+
}
1540+
1541+
/// The goal of this function is to return the first `Path` which is not private (ie not private
1542+
/// or `doc(hidden)`). If it's not possible, it'll return the "end type".
1543+
///
1544+
/// If the path is not a re-export or is public, it'll return `None`.
1545+
fn first_non_private<'tcx>(
1546+
cx: &mut DocContext<'tcx>,
1547+
hir_id: hir::HirId,
1548+
path: &hir::Path<'tcx>,
1549+
) -> Option<Path> {
1550+
let target_def_id = path.res.opt_def_id()?;
1551+
let (parent_def_id, ident) = match &path.segments[..] {
1552+
[] => return None,
1553+
// Relative paths are available in the same scope as the owner.
1554+
[leaf] => (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident),
1555+
// So are self paths.
1556+
[parent, leaf] if parent.ident.name == kw::SelfLower => {
1557+
(cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident)
1558+
}
1559+
// Crate paths are not. We start from the crate root.
1560+
[parent, leaf] if matches!(parent.ident.name, kw::Crate | kw::PathRoot) => {
1561+
(LOCAL_CRATE.as_def_id().as_local()?, leaf.ident)
1562+
}
1563+
[parent, leaf] if parent.ident.name == kw::Super => {
1564+
let parent_mod = cx.tcx.parent_module(hir_id);
1565+
if let Some(super_parent) = cx.tcx.opt_local_parent(parent_mod) {
1566+
(super_parent, leaf.ident)
1567+
} else {
1568+
// If we can't find the parent of the parent, then the parent is already the crate.
1569+
(LOCAL_CRATE.as_def_id().as_local()?, leaf.ident)
1570+
}
1571+
}
1572+
// Absolute paths are not. We start from the parent of the item.
1573+
[.., parent, leaf] => (parent.res.opt_def_id()?.as_local()?, leaf.ident),
1574+
};
1575+
let hir = cx.tcx.hir();
1576+
// First we try to get the `DefId` of the item.
1577+
for child in
1578+
cx.tcx.module_children_local(parent_def_id).iter().filter(move |c| c.ident == ident)
1579+
{
1580+
if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = child.res {
1581+
continue;
1582+
}
1583+
1584+
if let Some(def_id) = child.res.opt_def_id() && target_def_id == def_id {
1585+
let mut last_path_res = None;
1586+
'reexps: for reexp in child.reexport_chain.iter() {
1587+
if let Some(use_def_id) = reexp.id() &&
1588+
let Some(local_use_def_id) = use_def_id.as_local() &&
1589+
let Some(hir::Node::Item(item)) = hir.find_by_def_id(local_use_def_id) &&
1590+
!item.ident.name.is_empty() &&
1591+
let hir::ItemKind::Use(path, _) = item.kind
1592+
{
1593+
for res in &path.res {
1594+
if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
1595+
continue;
1596+
}
1597+
if (cx.render_options.document_hidden ||
1598+
!cx.tcx.is_doc_hidden(use_def_id)) &&
1599+
// We never check for "cx.render_options.document_private"
1600+
// because if a re-export is not fully public, it's never
1601+
// documented.
1602+
cx.tcx.local_visibility(local_use_def_id).is_public() {
1603+
break 'reexps;
1604+
}
1605+
last_path_res = Some((path, res));
1606+
continue 'reexps;
1607+
}
1608+
}
1609+
}
1610+
if !child.reexport_chain.is_empty() {
1611+
// So in here, we use the data we gathered from iterating the reexports. If
1612+
// `last_path_res` is set, it can mean two things:
1613+
//
1614+
// 1. We found a public reexport.
1615+
// 2. We didn't find a public reexport so it's the "end type" path.
1616+
if let Some((new_path, _)) = last_path_res {
1617+
return Some(first_non_private_clean_path(cx, path, new_path.segments, new_path.span));
1618+
}
1619+
// If `last_path_res` is `None`, it can mean two things:
1620+
//
1621+
// 1. The re-export is public, no need to change anything, just use the path as is.
1622+
// 2. Nothing was found, so let's just return the original path.
1623+
return None;
1624+
}
1625+
}
1626+
}
1627+
None
1628+
}
1629+
15171630
fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type {
1518-
let hir::Ty { hir_id: _, span, ref kind } = *hir_ty;
1631+
let hir::Ty { hir_id, span, ref kind } = *hir_ty;
15191632
let hir::TyKind::Path(qpath) = kind else { unreachable!() };
15201633

15211634
match qpath {
@@ -1532,7 +1645,12 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type
15321645
if let Some(expanded) = maybe_expand_private_type_alias(cx, path) {
15331646
expanded
15341647
} else {
1535-
let path = clean_path(path, cx);
1648+
// First we check if it's a private re-export.
1649+
let path = if let Some(path) = first_non_private(cx, hir_id, &path) {
1650+
path
1651+
} else {
1652+
clean_path(path, cx)
1653+
};
15361654
resolve_type(cx, path)
15371655
}
15381656
}
@@ -1683,7 +1801,7 @@ fn maybe_expand_private_type_alias<'tcx>(
16831801
}
16841802
}
16851803

1686-
Some(cx.enter_alias(args, def_id.to_def_id(), |cx| clean_ty(ty, cx)))
1804+
Some(cx.enter_alias(args, def_id.to_def_id(), |cx| clean_ty(&ty, cx)))
16871805
}
16881806

16891807
pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type {

src/librustdoc/clean/types.rs

+11
Original file line numberDiff line numberDiff line change
@@ -2212,6 +2212,17 @@ pub(crate) enum GenericArgs {
22122212
Parenthesized { inputs: Box<[Type]>, output: Option<Box<Type>> },
22132213
}
22142214

2215+
impl GenericArgs {
2216+
pub(crate) fn is_empty(&self) -> bool {
2217+
match self {
2218+
GenericArgs::AngleBracketed { args, bindings } => {
2219+
args.is_empty() && bindings.is_empty()
2220+
}
2221+
GenericArgs::Parenthesized { inputs, output } => inputs.is_empty() && output.is_none(),
2222+
}
2223+
}
2224+
}
2225+
22152226
#[derive(Clone, PartialEq, Eq, Debug, Hash)]
22162227
pub(crate) struct PathSegment {
22172228
pub(crate) name: Symbol,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
pub struct Ident;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// edition:2015
2+
3+
#![crate_name = "foo"]
4+
5+
use external::Public as Private;
6+
7+
pub mod external {
8+
pub struct Public;
9+
10+
// @has 'foo/external/fn.make.html'
11+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn make() -> Public'
12+
pub fn make() -> ::Private { super::Private }
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
#![crate_name = "foo"]
2+
3+
use crate::bar::Foo as Alias;
4+
5+
pub mod bar {
6+
pub struct Foo<'a, T>(&'a T);
7+
}
8+
9+
// @has "foo/fn.foo.html"
10+
// @has - '//*[@class="rust item-decl"]/code' "pub fn foo<'a, T>(f: Foo<'a, T>) -> Foo<'a, usize>"
11+
pub fn foo<'a, T>(f: Alias<'a, T>) -> Alias<'a, usize> {
12+
Alias(&0)
13+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// compile-flags: -Z unstable-options --document-hidden-items
2+
3+
#![crate_name = "foo"]
4+
5+
#[doc(hidden)]
6+
pub use crate::bar::Bar as Alias;
7+
8+
mod bar {
9+
pub struct Bar;
10+
}
11+
12+
// @has 'foo/fn.bar.html'
13+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar() -> Alias'
14+
pub fn bar() -> Alias {
15+
Alias
16+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// compile-flags: --document-private-items
2+
3+
#![crate_name = "foo"]
4+
5+
use crate::bar::Bar as Alias;
6+
pub(crate) use crate::bar::Bar as CrateAlias;
7+
8+
mod bar {
9+
pub struct Bar;
10+
pub use self::Bar as Inner;
11+
}
12+
13+
// It's a fully private re-export so it should not be displayed.
14+
// @has 'foo/fn.bar.html'
15+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar() -> Bar'
16+
pub fn bar() -> Alias {
17+
Alias
18+
}
19+
20+
// It's public re-export inside a private module so it should be visible.
21+
// @has 'foo/fn.bar2.html'
22+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar2() -> Inner'
23+
pub fn bar2() -> crate::bar::Inner {
24+
Alias
25+
}
26+
27+
// It's a non-public, so it doesn't appear in documentation so it should not be visible.
28+
// @has 'foo/fn.bar3.html'
29+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar3() -> Bar'
30+
pub fn bar3() -> CrateAlias {
31+
Alias
32+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,124 @@
1+
// This test ensures that if a private re-export is present in a public API, it'll be
2+
// replaced by the first public item in the re-export chain or by the private item.
3+
4+
#![crate_name = "foo"]
5+
6+
use crate::bar::Bar as Alias;
7+
8+
pub use crate::bar::Bar as Whatever;
9+
use crate::Whatever as Whatever2;
10+
use crate::Whatever2 as Whatever3;
11+
pub use crate::bar::Inner as Whatever4;
12+
13+
mod bar {
14+
pub struct Bar;
15+
pub use self::Bar as Inner;
16+
}
17+
18+
// @has 'foo/fn.bar.html'
19+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar() -> Bar'
20+
pub fn bar() -> Alias {
21+
Alias
22+
}
23+
24+
// @has 'foo/fn.bar2.html'
25+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar2() -> Whatever'
26+
pub fn bar2() -> Whatever3 {
27+
Whatever
28+
}
29+
30+
// @has 'foo/fn.bar3.html'
31+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar3() -> Whatever4'
32+
pub fn bar3() -> Whatever4 {
33+
Whatever
34+
}
35+
36+
// @has 'foo/fn.bar4.html'
37+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar4() -> Bar'
38+
pub fn bar4() -> crate::Alias {
39+
Alias
40+
}
41+
42+
// @has 'foo/fn.bar5.html'
43+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar5() -> Whatever'
44+
pub fn bar5() -> crate::Whatever3 {
45+
Whatever
46+
}
47+
48+
// @has 'foo/fn.bar6.html'
49+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar6() -> Whatever4'
50+
pub fn bar6() -> crate::Whatever4 {
51+
Whatever
52+
}
53+
54+
55+
// @has 'foo/fn.bar7.html'
56+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar7() -> Bar'
57+
pub fn bar7() -> self::Alias {
58+
Alias
59+
}
60+
61+
// @has 'foo/fn.bar8.html'
62+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar8() -> Whatever'
63+
pub fn bar8() -> self::Whatever3 {
64+
Whatever
65+
}
66+
67+
// @has 'foo/fn.bar9.html'
68+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar9() -> Whatever4'
69+
pub fn bar9() -> self::Whatever4 {
70+
Whatever
71+
}
72+
73+
mod nested {
74+
pub(crate) use crate::Alias;
75+
pub(crate) use crate::Whatever3;
76+
pub(crate) use crate::Whatever4;
77+
pub(crate) use crate::nested as nested2;
78+
}
79+
80+
// @has 'foo/fn.bar10.html'
81+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar10() -> Bar'
82+
pub fn bar10() -> nested::Alias {
83+
Alias
84+
}
85+
86+
// @has 'foo/fn.bar11.html'
87+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar11() -> Whatever'
88+
pub fn bar11() -> nested::Whatever3 {
89+
Whatever
90+
}
91+
92+
// @has 'foo/fn.bar12.html'
93+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar12() -> Whatever4'
94+
pub fn bar12() -> nested::Whatever4 {
95+
Whatever
96+
}
97+
98+
// @has 'foo/fn.bar13.html'
99+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar13() -> Bar'
100+
pub fn bar13() -> nested::nested2::Alias {
101+
Alias
102+
}
103+
104+
// @has 'foo/fn.bar14.html'
105+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar14() -> Whatever'
106+
pub fn bar14() -> nested::nested2::Whatever3 {
107+
Whatever
108+
}
109+
110+
// @has 'foo/fn.bar15.html'
111+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn bar15() -> Whatever4'
112+
pub fn bar15() -> nested::nested2::Whatever4 {
113+
Whatever
114+
}
115+
116+
use external::Public as Private;
117+
118+
pub mod external {
119+
pub struct Public;
120+
121+
// @has 'foo/external/fn.make.html'
122+
// @has - '//*[@class="rust item-decl"]/code' 'pub fn make() -> Public'
123+
pub fn make() -> super::Private { super::Private }
124+
}

tests/rustdoc/private-use.rs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// Regression test for <https://github.com/rust-lang/rust/pull/113374> to
2+
// ensure it doesn't panic.
3+
4+
mod generics {
5+
pub enum WherePredicate {
6+
EqPredicate,
7+
}
8+
}
9+
pub mod visit {
10+
use *;
11+
pub fn visit_where_predicate<V>(_visitor: &mut V, _i: &WherePredicate) {}
12+
}
13+
pub use generics::*;

0 commit comments

Comments
 (0)