Skip to content

Commit 0d14e26

Browse files
Rollup merge of rust-lang#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 rust-lang#81141. If we have: ```rust use Private as Something; pub fn foo() -> Something {} ``` Then `Something` will be replaced by `Private`. r? `````@notriddle`````
2 parents 1f98612 + 4a1e06b commit 0d14e26

File tree

3 files changed

+234
-3
lines changed

3 files changed

+234
-3
lines changed

src/librustdoc/clean/mod.rs

+97-3
Original file line numberDiff line numberDiff line change
@@ -1479,8 +1479,97 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
14791479
Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx)
14801480
}
14811481

1482+
/// The goal of this function is to return the first `Path` which is not private (ie not private
1483+
/// or `doc(hidden)`). If it's not possible, it'll return the "end type".
1484+
///
1485+
/// If the path is not a re-export or is public, it'll return `None`.
1486+
fn first_non_private(
1487+
cx: &mut DocContext<'_>,
1488+
hir_id: hir::HirId,
1489+
path: &hir::Path<'_>,
1490+
) -> Option<Path> {
1491+
let (parent_def_id, mut ident) = match &path.segments[..] {
1492+
[] => return None,
1493+
// Relative paths are available in the same scope as the owner.
1494+
[leaf] => (cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident),
1495+
// So are self paths.
1496+
[parent, leaf] if parent.ident.name == kw::SelfLower => {
1497+
(cx.tcx.local_parent(hir_id.owner.def_id), leaf.ident)
1498+
}
1499+
// Crate paths are not. We start from the crate root.
1500+
[parent, leaf] if matches!(parent.ident.name, kw::Crate | kw::PathRoot) => {
1501+
(LOCAL_CRATE.as_def_id().as_local()?, leaf.ident)
1502+
}
1503+
[parent, leaf] if parent.ident.name == kw::Super => {
1504+
let parent_mod = cx.tcx.parent_module(hir_id);
1505+
if let Some(super_parent) = cx.tcx.opt_local_parent(parent_mod) {
1506+
(super_parent, leaf.ident)
1507+
} else {
1508+
// If we can't find the parent of the parent, then the parent is already the crate.
1509+
(LOCAL_CRATE.as_def_id().as_local()?, leaf.ident)
1510+
}
1511+
}
1512+
// Absolute paths are not. We start from the parent of the item.
1513+
[.., parent, leaf] => (parent.res.opt_def_id()?.as_local()?, leaf.ident),
1514+
};
1515+
let target_def_id = path.res.opt_def_id()?;
1516+
// First we try to get the `DefId` of the item.
1517+
for child in
1518+
cx.tcx.module_children_local(parent_def_id).iter().filter(move |c| c.ident == ident)
1519+
{
1520+
if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = child.res {
1521+
continue;
1522+
}
1523+
1524+
if let Some(def_id) = child.res.opt_def_id() && target_def_id == def_id {
1525+
let mut last_path_res = None;
1526+
'reexps: for reexp in child.reexport_chain.iter() {
1527+
if let Some(use_def_id) = reexp.id() &&
1528+
let Some(local_use_def_id) = use_def_id.as_local()
1529+
{
1530+
let hir = cx.tcx.hir();
1531+
for item_id in hir.module_items(cx.tcx.local_parent(local_use_def_id)) {
1532+
let item = hir.item(item_id);
1533+
if item.ident == ident && let hir::ItemKind::Use(path, _) = item.kind {
1534+
for res in &path.res {
1535+
if let Res::Def(DefKind::Ctor(..), _) | Res::SelfCtor(..) = res {
1536+
continue;
1537+
}
1538+
if !cx.tcx.is_doc_hidden(use_def_id) &&
1539+
cx.tcx.local_visibility(local_use_def_id).is_public() {
1540+
break 'reexps;
1541+
}
1542+
ident = path.segments.last().unwrap().ident;
1543+
last_path_res = Some((path, res));
1544+
continue 'reexps;
1545+
}
1546+
}
1547+
}
1548+
}
1549+
}
1550+
if !child.reexport_chain.is_empty() {
1551+
// So in here, we use the data we gathered from iterating the reexports. If
1552+
// `last_path_res` is set, it can mean two things:
1553+
//
1554+
// 1. We found a public reexport.
1555+
// 2. We didn't find a public reexport so it's the "end type" path.
1556+
if let Some((path, res)) = last_path_res {
1557+
let path = hir::Path { segments: path.segments, res: *res, span: path.span };
1558+
return Some(clean_path(&path, cx));
1559+
}
1560+
// If `last_path_res` is `None`, it can mean two things:
1561+
//
1562+
// 1. The re-export is public, no need to change anything, just use the path as is.
1563+
// 2. Nothing was found, so let's just return the original path.
1564+
return None;
1565+
}
1566+
}
1567+
}
1568+
None
1569+
}
1570+
14821571
fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type {
1483-
let hir::Ty { hir_id: _, span, ref kind } = *hir_ty;
1572+
let hir::Ty { hir_id, span, ref kind } = *hir_ty;
14841573
let hir::TyKind::Path(qpath) = kind else { unreachable!() };
14851574

14861575
match qpath {
@@ -1497,7 +1586,12 @@ fn clean_qpath<'tcx>(hir_ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type
14971586
if let Some(expanded) = maybe_expand_private_type_alias(cx, path) {
14981587
expanded
14991588
} else {
1500-
let path = clean_path(path, cx);
1589+
// First we check if it's a private re-export.
1590+
let path = if let Some(path) = first_non_private(cx, hir_id, &path) {
1591+
path
1592+
} else {
1593+
clean_path(path, cx)
1594+
};
15011595
resolve_type(cx, path)
15021596
}
15031597
}
@@ -1649,7 +1743,7 @@ fn maybe_expand_private_type_alias<'tcx>(
16491743
}
16501744
}
16511745

1652-
Some(cx.enter_alias(substs, def_id.to_def_id(), |cx| clean_ty(ty, cx)))
1746+
Some(cx.enter_alias(substs, def_id.to_def_id(), |cx| clean_ty(&ty, cx)))
16531747
}
16541748

16551749
pub(crate) fn clean_ty<'tcx>(ty: &hir::Ty<'tcx>, cx: &mut DocContext<'tcx>) -> Type {
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,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+
}

0 commit comments

Comments
 (0)