Don't unnecessarily clone some fields in Context

There was no need to clone `id_map` because it was reset before each
item was rendered. `deref_id_map` was not reset, but it was keyed by
`DefId` and thus was unlikely to have collisions (at least for now).

Now we just clone the fields that need to be cloned, and instead create
fresh versions of the others.
This commit is contained in:
Camelid 2021-03-01 19:12:03 -08:00
parent ff39c46959
commit c09d9d34f0
5 changed files with 28 additions and 22 deletions

View file

@ -9,7 +9,7 @@ use crate::formats::cache::Cache;
/// Allows for different backends to rustdoc to be used with the `run_format()` function. Each
/// backend renderer has hooks for initialization, documenting an item, entering and exiting a
/// module, and cleanup/finalizing output.
crate trait FormatRenderer<'tcx>: Clone {
crate trait FormatRenderer<'tcx>: Sized {
/// Gives a description of the renderer. Used for performance profiling.
fn descr() -> &'static str;
@ -23,6 +23,9 @@ crate trait FormatRenderer<'tcx>: Clone {
tcx: TyCtxt<'tcx>,
) -> Result<(Self, clean::Crate), Error>;
/// Make a new renderer to render a child of the item currently being rendered.
fn make_child_renderer(&self) -> Self;
/// Renders a single non-module item. This means no recursive sub-item rendering is required.
fn item(&mut self, item: clean::Item) -> Result<(), Error>;
@ -67,7 +70,7 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>(
item.name = Some(krate.name);
// Render the crate documentation
let mut work = vec![(format_renderer.clone(), item)];
let mut work = vec![(format_renderer.make_child_renderer(), item)];
let unknown = rustc_span::Symbol::intern("<unknown item>");
while let Some((mut cx, item)) = work.pop() {
@ -87,7 +90,7 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>(
};
for it in module.items {
debug!("Adding {:?} to worklist", it.name);
work.push((cx.clone(), it));
work.push((cx.make_child_renderer(), it));
}
cx.mod_item_out(&name)?;

View file

@ -1373,10 +1373,6 @@ impl IdMap {
}
}
crate fn reset(&mut self) {
self.map = init_id_map();
}
crate fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
let id = match self.map.get_mut(candidate.as_ref()) {
None => candidate.to_string(),

View file

@ -38,15 +38,9 @@ fn test_unique_id() {
"assoc_type.Item-1",
];
let map = RefCell::new(IdMap::new());
let test = || {
let mut map = map.borrow_mut();
let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
assert_eq!(&actual[..], expected);
};
test();
map.borrow_mut().reset();
test();
let mut map = IdMap::new();
let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
assert_eq!(&actual[..], expected);
}
#[test]

View file

@ -40,7 +40,6 @@ use crate::html::{layout, sources};
/// It is intended that this context is a lightweight object which can be fairly
/// easily cloned because it is cloned per work-job (about once per item in the
/// rustdoc tree).
#[derive(Clone)]
crate struct Context<'tcx> {
/// Current hierarchy of components leading down to what's currently being
/// rendered
@ -157,11 +156,6 @@ impl<'tcx> Context<'tcx> {
static_extra_scripts: &[],
};
{
self.id_map.borrow_mut().reset();
self.id_map.borrow_mut().populate(&INITIAL_IDS);
}
if !self.render_redirect_pages {
layout::render(
&self.shared.layout,
@ -436,6 +430,21 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
Ok((cx, krate))
}
fn make_child_renderer(&self) -> Self {
let mut id_map = IdMap::new();
id_map.populate(&INITIAL_IDS);
Self {
current: self.current.clone(),
dst: self.dst.clone(),
render_redirect_pages: self.render_redirect_pages,
id_map: Box::new(RefCell::new(id_map)),
deref_id_map: Box::new(RefCell::new(FxHashMap::default())),
shared: Rc::clone(&self.shared),
cache: Rc::clone(&self.cache),
}
}
fn after_krate(
&mut self,
krate: &clean::Crate,

View file

@ -149,6 +149,10 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
))
}
fn make_child_renderer(&self) -> Self {
self.clone()
}
/// Inserts an item into the index. This should be used rather than directly calling insert on
/// the hashmap because certain items (traits and types) need to have their mappings for trait
/// implementations filled out before they're inserted.