about summary refs log tree commit diff
diff options
context:
space:
mode:
authorGuillaume Gomez <guillaume.gomez@huawei.com>2023-07-20 15:09:22 +0200
committerGuillaume Gomez <guillaume.gomez@huawei.com>2023-07-24 17:07:57 +0200
commit988729d842be9fddeecdf8bd9c8cf018b511ed8b (patch)
tree3aff7dee99f95691d324c56882324b5f1d493518
parent662c16771184a26ca0a69f6bf66b13467627d59d (diff)
downloadrust-988729d842be9fddeecdf8bd9c8cf018b511ed8b.tar.gz
rust-988729d842be9fddeecdf8bd9c8cf018b511ed8b.zip
Cache qpath first public result
-rw-r--r--src/librustdoc/clean/mod.rs72
-rw-r--r--src/librustdoc/core.rs5
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<hir::PathSegment<'tcx>>,
+    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<Path> {
-    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<FxHashMap<HirId, UsePath<'tcx>>>,
 }
 
 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(()) {