about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume.gomez@huawei.com>2023-07-05 18:36:10 +0200
committerGuillaume Gomez <guillaume.gomez@huawei.com>2023-07-24 17:07:56 +0200
commitee7dba4db8964b3db2aca0eb8b8fec81178792b8 (patch)
treea47a63c3238f245574cfedd99d773448f0810747 /src
parent155a5c2862c2939aa8464456ca9f358fbb213005 (diff)
downloadrust-ee7dba4db8964b3db2aca0eb8b8fec81178792b8.tar.gz
rust-ee7dba4db8964b3db2aca0eb8b8fec81178792b8.zip
If re-export is private, get the next item until a public one is found or expose the private item directly
Diffstat (limited to 'src')
-rw-r--r--src/librustdoc/clean/mod.rs96
1 files 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<Path> {
+    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 {