Skip to content

Commit 988729d

Browse files
Cache qpath first public result
1 parent 662c167 commit 988729d

File tree

2 files changed

+50
-27
lines changed

2 files changed

+50
-27
lines changed

src/librustdoc/clean/mod.rs

Lines changed: 46 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1496,17 +1496,55 @@ pub(crate) fn clean_middle_assoc_item<'tcx>(
14961496
Item::from_def_id_and_parts(assoc_item.def_id, Some(assoc_item.name), kind, cx)
14971497
}
14981498

1499+
fn first_non_private_clean_path<'tcx>(
1500+
cx: &mut DocContext<'tcx>,
1501+
path: &hir::Path<'tcx>,
1502+
mut new_path_segments: Vec<hir::PathSegment<'tcx>>,
1503+
new_path_span: rustc_span::Span,
1504+
) -> Path {
1505+
use std::mem::transmute;
1506+
1507+
// In here we need to play with the path data one last time to provide it the
1508+
// missing `args` and `res` of the final `Path` we get, which, since it comes
1509+
// from a re-export, doesn't have the generics that were originally there, so
1510+
// we add them by hand.
1511+
if let Some(last) = new_path_segments.last_mut() {
1512+
// `transmute` is needed because we are using a wrong lifetime. Since
1513+
// `segments` will be dropped at the end of this block, it's fine.
1514+
last.args = unsafe { transmute(path.segments.last().as_ref().unwrap().args.clone()) };
1515+
last.res = path.res;
1516+
}
1517+
// `transmute` is needed because we are using a wrong lifetime. Since
1518+
// `segments` will be dropped at the end of this block, it's fine.
1519+
let path = unsafe {
1520+
hir::Path {
1521+
segments: transmute(new_path_segments.as_slice()),
1522+
res: path.res,
1523+
span: new_path_span,
1524+
}
1525+
};
1526+
clean_path(&path, cx)
1527+
}
1528+
14991529
/// The goal of this function is to return the first `Path` which is not private (ie not private
15001530
/// or `doc(hidden)`). If it's not possible, it'll return the "end type".
15011531
///
15021532
/// If the path is not a re-export or is public, it'll return `None`.
1503-
fn first_non_private(
1504-
cx: &mut DocContext<'_>,
1533+
fn first_non_private<'tcx>(
1534+
cx: &mut DocContext<'tcx>,
15051535
hir_id: hir::HirId,
1506-
path: &hir::Path<'_>,
1536+
path: &hir::Path<'tcx>,
15071537
) -> Option<Path> {
1508-
use std::mem::transmute;
1509-
1538+
let use_id = path.segments.last().map(|seg| seg.hir_id)?;
1539+
let target_def_id = path.res.opt_def_id()?;
1540+
let saved_path = cx
1541+
.updated_qpath
1542+
.borrow()
1543+
.get(&use_id)
1544+
.map(|saved_path| (saved_path.segments.to_vec(), saved_path.span));
1545+
if let Some((segments, span)) = saved_path {
1546+
return Some(first_non_private_clean_path(cx, path, segments, span));
1547+
}
15101548
let (parent_def_id, mut ident) = match &path.segments[..] {
15111549
[] => return None,
15121550
// Relative paths are available in the same scope as the owner.
@@ -1531,7 +1569,6 @@ fn first_non_private(
15311569
// Absolute paths are not. We start from the parent of the item.
15321570
[.., parent, leaf] => (parent.res.opt_def_id()?.as_local()?, leaf.ident),
15331571
};
1534-
let target_def_id = path.res.opt_def_id()?;
15351572
// First we try to get the `DefId` of the item.
15361573
for child in
15371574
cx.tcx.module_children_local(parent_def_id).iter().filter(move |c| c.ident == ident)
@@ -1577,26 +1614,9 @@ fn first_non_private(
15771614
// 1. We found a public reexport.
15781615
// 2. We didn't find a public reexport so it's the "end type" path.
15791616
if let Some((new_path, _)) = last_path_res {
1580-
// In here we need to play with the path data one last time to provide it the
1581-
// missing `args` and `res` of the final `Path` we get, which, since it comes
1582-
// from a re-export, doesn't have the generics that were originally there, so
1583-
// we add them by hand.
1584-
let mut segments = new_path.segments.to_vec();
1585-
if let Some(last) = segments.last_mut() {
1586-
// `transmute` is needed because we are using a wrong lifetime. Since
1587-
// `segments` will be dropped at the end of this block, it's fine.
1588-
last.args = unsafe {
1589-
transmute(
1590-
path.segments.last().as_ref().unwrap().args.clone(),
1591-
)
1592-
};
1593-
last.res = path.res;
1594-
}
1595-
// `transmute` is needed because we are using a wrong lifetime. Since
1596-
// `segments` will be dropped at the end of this block, it's fine.
1597-
let segments = unsafe { transmute(segments.as_slice()) };
1598-
let new_path = hir::Path { segments, res: path.res, span: new_path.span };
1599-
return Some(clean_path(&new_path, cx));
1617+
cx.updated_qpath.borrow_mut().insert(use_id, new_path.clone());
1618+
let new_path_segments = new_path.segments.to_vec();
1619+
return Some(first_non_private_clean_path(cx, path, new_path_segments, new_path.span));
16001620
}
16011621
// If `last_path_res` is `None`, it can mean two things:
16021622
//

src/librustdoc/core.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_feature::UnstableFeatures;
88
use rustc_hir::def::Res;
99
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
1010
use rustc_hir::intravisit::{self, Visitor};
11-
use rustc_hir::{HirId, Path};
11+
use rustc_hir::{HirId, Path, UsePath};
1212
use rustc_interface::interface;
1313
use rustc_middle::hir::nested_filter;
1414
use rustc_middle::ty::{ParamEnv, Ty, TyCtxt};
@@ -63,6 +63,8 @@ pub(crate) struct DocContext<'tcx> {
6363
pub(crate) output_format: OutputFormat,
6464
/// Used by `strip_private`.
6565
pub(crate) show_coverage: bool,
66+
/// Used by `first_non_private` to prevent computing the same path more than once.
67+
pub(crate) updated_qpath: RefCell<FxHashMap<HirId, UsePath<'tcx>>>,
6668
}
6769

6870
impl<'tcx> DocContext<'tcx> {
@@ -352,6 +354,7 @@ pub(crate) fn run_global_ctxt(
352354
output_format,
353355
render_options,
354356
show_coverage,
357+
updated_qpath: Default::default(),
355358
};
356359

357360
for cnum in tcx.crates(()) {

0 commit comments

Comments
 (0)