From a84be78cf3616fb5af522b0b8d2e4c25ae13f978 Mon Sep 17 00:00:00 2001 From: Wardenfar Date: Mon, 8 Aug 2022 13:51:52 +0200 Subject: [PATCH] stop cloning html render context --- src/librustdoc/formats/renderer.rs | 92 +++++++---- src/librustdoc/html/render/context.rs | 173 +++++++++++++-------- src/librustdoc/html/render/mod.rs | 73 +++++---- src/librustdoc/html/render/print_item.rs | 53 +++---- src/librustdoc/html/render/write_shared.rs | 13 +- src/librustdoc/html/sources.rs | 10 +- src/librustdoc/json/mod.rs | 4 - 7 files changed, 249 insertions(+), 169 deletions(-) diff --git a/src/librustdoc/formats/renderer.rs b/src/librustdoc/formats/renderer.rs index 62ba984acc961..a8164d4e4df23 100644 --- a/src/librustdoc/formats/renderer.rs +++ b/src/librustdoc/formats/renderer.rs @@ -1,7 +1,7 @@ use rustc_middle::ty::TyCtxt; use rustc_span::Symbol; -use crate::clean; +use crate::clean::{self, Item}; use crate::config::RenderOptions; use crate::error::Error; use crate::formats::cache::Cache; @@ -27,12 +27,19 @@ pub(crate) trait FormatRenderer<'tcx>: Sized { 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>; + /// Runs before rendering an item (not a module) + fn before_item(&mut self, _item: &clean::Item) -> Result<(), Error> { + Ok(()) + } + + /// Runs after rendering an item (not a module) + fn after_item(&mut self) -> Result<(), Error> { + Ok(()) + } + /// Renders a module (should not handle recursing into children). fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error>; @@ -65,33 +72,66 @@ pub(crate) fn run_format<'tcx, T: FormatRenderer<'tcx>>( return Ok(()); } - // Render the crate documentation - let mut work = vec![(format_renderer.make_child_renderer(), krate.module)]; + enum WorkUnit { + Module { item: Item, current_index: usize }, + Single(Item), + } + + let mut work_units: Vec = + vec![WorkUnit::Module { item: krate.module, current_index: 0 }]; let unknown = Symbol::intern(""); - while let Some((mut cx, item)) = work.pop() { - if item.is_mod() && T::RUN_ON_MODULE { - // modules are special because they add a namespace. We also need to - // recurse into the items of the module as well. - let _timer = - prof.generic_activity_with_arg("render_mod_item", item.name.unwrap().to_string()); - - cx.mod_item_in(&item)?; - let (clean::StrippedItem(box clean::ModuleItem(module)) | clean::ModuleItem(module)) = *item.kind - else { unreachable!() }; - for it in module.items { - debug!("Adding {:?} to worklist", it.name); - work.push((cx.make_child_renderer(), it)); + while let Some(work_unit) = work_units.pop() { + match work_unit { + WorkUnit::Module { item, current_index } if T::RUN_ON_MODULE => { + let (clean::StrippedItem(box clean::ModuleItem(module)) | clean::ModuleItem(module)) = item.kind.as_ref() + else { unreachable!() }; + + if current_index == 0 { + // just enter the module + format_renderer.mod_item_in(&item)?; + } + + if current_index < module.items.len() { + // get the next item + let next_item = module.items[current_index].clone(); + + // stay in the module + work_units.push(WorkUnit::Module { item, current_index: current_index + 1 }); + + // push the next item + if next_item.is_mod() { + work_units.push(WorkUnit::Module { item: next_item, current_index: 0 }); + } else { + work_units.push(WorkUnit::Single(next_item)); + } + } else { + // the last item of the module has been rendered + // -> exit the module + format_renderer.mod_item_out()?; + } } - - cx.mod_item_out()?; - // FIXME: checking `item.name.is_some()` is very implicit and leads to lots of special - // cases. Use an explicit match instead. - } else if item.name.is_some() && !item.is_extern_crate() { - prof.generic_activity_with_arg("render_item", item.name.unwrap_or(unknown).as_str()) - .run(|| cx.item(item))?; + // FIXME: checking `item.name.is_some()` is very implicit and leads to lots of special + // cases. Use an explicit match instead. + WorkUnit::Module { item, .. } | WorkUnit::Single(item) + if item.name.is_some() && !item.is_extern_crate() => + { + // render the item + prof.generic_activity_with_arg( + "render_item", + item.name.unwrap_or(unknown).as_str(), + ) + .run(|| { + format_renderer.before_item(&item)?; + let result = format_renderer.item(item)?; + format_renderer.after_item()?; + Ok(result) + })?; + } + _ => {} } } + prof.extra_verbose_generic_activity("renderer_after_krate", T::descr()) .run(|| format_renderer.after_krate()) } diff --git a/src/librustdoc/html/render/context.rs b/src/librustdoc/html/render/context.rs index 01b96dc721558..c4922049e6b32 100644 --- a/src/librustdoc/html/render/context.rs +++ b/src/librustdoc/html/render/context.rs @@ -1,8 +1,7 @@ -use std::cell::RefCell; +use std::cell::{Cell, RefCell}; use std::collections::BTreeMap; use std::io; use std::path::{Path, PathBuf}; -use std::rc::Rc; use std::sync::mpsc::{channel, Receiver}; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; @@ -38,10 +37,6 @@ use crate::try_err; /// Major driving force in all rustdoc rendering. This contains information /// about where in the tree-like hierarchy rendering is occurring and controls /// how the current page is being rendered. -/// -/// 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). pub(crate) struct Context<'tcx> { /// Current hierarchy of components leading down to what's currently being /// rendered @@ -49,31 +44,37 @@ pub(crate) struct Context<'tcx> { /// The current destination folder of where HTML artifacts should be placed. /// This changes as the context descends into the module hierarchy. pub(crate) dst: PathBuf, + /// Local context (per item) + /// + /// Before rendering an element a new Frame is added, + /// when rendering is finished: the Frame is removed. + /// An element's Frame is derived from its module. + /// This Vec contains the Frame hierarchy that leads to the current element. + /// + /// INVARIANT: must have at least 1 item + /// `before_item` must be called before rendering + pub(crate) local: Vec, + /// Shared context between items + pub(crate) shared: SharedContext<'tcx>, +} + +/// Local state for each item +pub(crate) struct ContextItemFrame { /// A flag, which when `true`, will render pages which redirect to the /// real location of an item. This is used to allow external links to /// publicly reused items to redirect to the right location. - pub(super) render_redirect_pages: bool, + pub(super) render_redirect_pages: Cell, /// Tracks section IDs for `Deref` targets so they match in both the main /// body and the sidebar. - pub(super) deref_id_map: FxHashMap, + pub(super) deref_id_map: RefCell>, /// The map used to ensure all generated 'id=' attributes are unique. - pub(super) id_map: IdMap, - /// Shared mutable state. - /// - /// Issue for improving the situation: [#82381][] - /// - /// [#82381]: https://github.com/rust-lang/rust/issues/82381 - pub(crate) shared: Rc>, + pub(super) id_map: RefCell, /// This flag indicates whether source links should be generated or not. If /// the source files are present in the html rendering, then this will be /// `true`. - pub(crate) include_sources: bool, + pub(super) include_sources: Cell, } -// `Context` is cloned a lot, so we don't want the size to grow unexpectedly. -#[cfg(all(not(windows), target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(Context<'_>, 128); - /// Shared mutable state used in [`Context`] and elsewhere. pub(crate) struct SharedContext<'tcx> { pub(crate) tcx: TyCtxt<'tcx>, @@ -155,8 +156,40 @@ impl<'tcx> Context<'tcx> { self.shared.tcx.sess } - pub(super) fn derive_id(&mut self, id: String) -> String { - self.id_map.derive(id) + /// Get id_map from local context + pub(crate) fn id_map(&self) -> &RefCell { + &self.local.last().unwrap().id_map + } + + /// Get deref_id_map from local context + pub(crate) fn deref_id_map(&self) -> &RefCell> { + &self.local.last().unwrap().deref_id_map + } + + /// Get render_redirect_pages from local context + pub(crate) fn render_redirect_pages(&self) -> bool { + self.local.last().unwrap().render_redirect_pages.get() + } + + /// Get include_sources from local context + pub(crate) fn include_sources(&self) -> bool { + self.local.last().unwrap().include_sources.get() + } + + /// Update render_redirect_pages from local context + pub(crate) fn set_render_redirect_pages(&self, render_redirect_pages: bool) { + let frame = self.local.last().unwrap(); + frame.render_redirect_pages.set(render_redirect_pages); + } + + /// Update include_sources from local context + pub(crate) fn set_include_sources(&self, include_sources: bool) { + let frame = self.local.last().unwrap(); + frame.include_sources.set(include_sources); + } + + pub(super) fn derive_id(&self, id: String) -> String { + self.id_map().borrow_mut().derive(id) } /// String representation of how to get back to the root path of the 'doc/' @@ -201,25 +234,25 @@ impl<'tcx> Context<'tcx> { tyname.as_str() }; - if !self.render_redirect_pages { - let clone_shared = Rc::clone(&self.shared); + if !self.render_redirect_pages() { + let shared = &self.shared; let page = layout::Page { css_class: tyname_s, root_path: &self.root_path(), - static_root_path: clone_shared.static_root_path.as_deref(), + static_root_path: shared.static_root_path.as_deref(), title: &title, description: &desc, keywords: &keywords, - resource_suffix: &clone_shared.resource_suffix, + resource_suffix: &shared.resource_suffix, }; let mut page_buffer = Buffer::html(); print_item(self, it, &mut page_buffer, &page); layout::render( - &clone_shared.layout, + &shared.layout, &page, |buf: &mut _| print_sidebar(self, it, buf), move |buf: &mut Buffer| buf.push_buffer(page_buffer), - &clone_shared.style_files, + &shared.style_files, ) } else { if let Some(&(ref names, ty)) = self.cache().paths.get(&it.item_id.expect_def_id()) { @@ -509,13 +542,15 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { scx.ensure_dir(&dst)?; let mut cx = Context { + local: vec![ContextItemFrame { + render_redirect_pages: Cell::new(false), + deref_id_map: RefCell::new(FxHashMap::default()), + id_map: RefCell::new(id_map), + include_sources: Cell::new(include_sources), + }], current: Vec::new(), dst, - render_redirect_pages: false, - id_map, - deref_id_map: FxHashMap::default(), - shared: Rc::new(scx), - include_sources, + shared: scx, }; if emit_crate { @@ -524,29 +559,17 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { if !no_emit_shared { // Build our search index - let index = build_index(&krate, &mut Rc::get_mut(&mut cx.shared).unwrap().cache, tcx); + let index = build_index(&krate, &mut cx.shared.cache, tcx); // Write shared runs within a flock; disable thread dispatching of IO temporarily. - Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true); + cx.shared.fs.set_sync_only(true); write_shared(&mut cx, &krate, index, &md_opts)?; - Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false); + cx.shared.fs.set_sync_only(false); } Ok((cx, krate)) } - fn make_child_renderer(&self) -> Self { - Self { - current: self.current.clone(), - dst: self.dst.clone(), - render_redirect_pages: self.render_redirect_pages, - deref_id_map: FxHashMap::default(), - id_map: IdMap::new(), - shared: Rc::clone(&self.shared), - include_sources: self.include_sources, - } - } - fn after_krate(&mut self) -> Result<(), Error> { let crate_name = self.tcx().crate_name(LOCAL_CRATE); let final_file = self.dst.join(crate_name.as_str()).join("all.html"); @@ -557,7 +580,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { if !root_path.ends_with('/') { root_path.push('/'); } - let shared = Rc::clone(&self.shared); + let shared = &self.shared; let mut page = layout::Page { title: "List of all items in this crate", css_class: "mod", @@ -628,7 +651,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { &shared.layout, &page, "", - scrape_examples_help(&*shared), + scrape_examples_help(shared), &shared.style_files, ); shared.fs.write(scrape_examples_help_file, v)?; @@ -644,11 +667,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { } } - // No need for it anymore. - drop(shared); - // Flush pending errors. - Rc::get_mut(&mut self.shared).unwrap().fs.close(); + self.shared.fs.close(); let nb_errors = self.shared.errors.iter().map(|err| self.tcx().sess.struct_err(&err).emit()).count(); if nb_errors > 0 { @@ -659,6 +679,13 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { } fn mod_item_in(&mut self, item: &clean::Item) -> Result<(), Error> { + let item_name = item.name.unwrap(); + self.dst.push(&*item_name.as_str()); + self.current.push(item_name); + + // push an item frame for the module + self.before_item(item)?; + // Stripped modules survive the rustdoc passes (i.e., `strip-private`) // if they contain impls for public types. These modules can also // contain items such as publicly re-exported structures. @@ -666,12 +693,10 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { // External crates will provide links to these structures, so // these modules are recursed into, but not rendered normally // (a flag on the context). - if !self.render_redirect_pages { - self.render_redirect_pages = item.is_stripped(); + + if !self.render_redirect_pages() { + self.set_render_redirect_pages(item.is_stripped()) } - let item_name = item.name.unwrap(); - self.dst.push(&*item_name.as_str()); - self.current.push(item_name); info!("Recursing into {}", self.dst.display()); @@ -684,7 +709,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { } // Render sidebar-items.js used throughout this module. - if !self.render_redirect_pages { + if !self.render_redirect_pages() { let (clean::StrippedItem(box clean::ModuleItem(ref module)) | clean::ModuleItem(ref module)) = *item.kind else { unreachable!() }; let items = self.build_sidebar_items(module); @@ -698,9 +723,12 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { fn mod_item_out(&mut self) -> Result<(), Error> { info!("Recursed; leaving {}", self.dst.display()); - // Go back to where we were at self.dst.pop(); self.current.pop(); + + // pop the last item frame + self.after_item()?; + Ok(()) } @@ -712,8 +740,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { // External crates will provide links to these structures, so // these modules are recursed into, but not rendered normally // (a flag on the context). - if !self.render_redirect_pages { - self.render_redirect_pages = item.is_stripped(); + if !self.render_redirect_pages() { + self.set_render_redirect_pages(item.is_stripped()); } let buf = self.render_item(&item, false); @@ -726,7 +754,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { let joint_dst = self.dst.join(file_name); self.shared.fs.write(joint_dst, buf)?; - if !self.render_redirect_pages { + if !self.render_redirect_pages() { self.shared.all.borrow_mut().append(full_path(self, &item), &item_type); } // If the item is a macro, redirect from the old macro URL (with !) @@ -752,6 +780,23 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> { fn cache(&self) -> &Cache { &self.shared.cache } + + fn before_item(&mut self, _item: &clean::Item) -> Result<(), Error> { + // push a new item frame + self.local.push(ContextItemFrame { + render_redirect_pages: Cell::new(self.render_redirect_pages()), + deref_id_map: RefCell::new(FxHashMap::default()), + id_map: RefCell::new(IdMap::new()), + include_sources: Cell::new(self.include_sources()), + }); + Ok(()) + } + + fn after_item(&mut self) -> Result<(), Error> { + // pop the last frame + self.local.pop(); + Ok(()) + } } fn make_item_keywords(it: &clean::Item) -> String { diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 1e6f20d2b491c..2058e98091663 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -36,13 +36,13 @@ mod write_shared; pub(crate) use self::context::*; pub(crate) use self::span_map::{collect_spans_and_sources, LinkFromSrc}; +use std::borrow::Cow; use std::collections::VecDeque; use std::default::Default; use std::fmt; use std::fs; use std::iter::Peekable; use std::path::PathBuf; -use std::rc::Rc; use std::str; use std::string::ToString; @@ -368,7 +368,7 @@ fn scrape_examples_help(shared: &SharedContext<'_>) -> String { fn document( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, item: &clean::Item, parent: Option<&clean::Item>, heading_offset: HeadingOffset, @@ -387,18 +387,19 @@ fn document( /// Render md_text as markdown. fn render_markdown( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, md_text: &str, links: Vec, heading_offset: HeadingOffset, ) { + let mut ids = cx.id_map().borrow_mut(); write!( w, "
{}
", Markdown { content: md_text, links: &links, - ids: &mut cx.id_map, + ids: &mut ids, error_codes: cx.shared.codes, edition: cx.shared.edition(), playground: &cx.shared.playground, @@ -413,7 +414,7 @@ fn render_markdown( fn document_short( w: &mut Buffer, item: &clean::Item, - cx: &mut Context<'_>, + cx: &Context<'_>, link: AssocItemLink<'_>, parent: &clean::Item, show_def_docs: bool, @@ -442,7 +443,7 @@ fn document_short( fn document_full_collapsible( w: &mut Buffer, item: &clean::Item, - cx: &mut Context<'_>, + cx: &Context<'_>, heading_offset: HeadingOffset, ) { document_full_inner(w, item, cx, true, heading_offset); @@ -451,7 +452,7 @@ fn document_full_collapsible( fn document_full( w: &mut Buffer, item: &clean::Item, - cx: &mut Context<'_>, + cx: &Context<'_>, heading_offset: HeadingOffset, ) { document_full_inner(w, item, cx, false, heading_offset); @@ -460,7 +461,7 @@ fn document_full( fn document_full_inner( w: &mut Buffer, item: &clean::Item, - cx: &mut Context<'_>, + cx: &Context<'_>, is_collapsible: bool, heading_offset: HeadingOffset, ) { @@ -496,7 +497,7 @@ fn document_full_inner( /// * Required features (through the `doc_cfg` feature) fn document_item_info( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, item: &clean::Item, parent: Option<&clean::Item>, ) { @@ -532,7 +533,7 @@ fn portability(item: &clean::Item, parent: Option<&clean::Item>) -> Option, + cx: &Context<'_>, parent: Option<&clean::Item>, ) -> Vec { let mut extra_info = vec![]; @@ -560,9 +561,10 @@ fn short_item_info( if let Some(note) = note { let note = note.as_str(); + let mut ids = cx.id_map().borrow_mut(); let html = MarkdownHtml( note, - &mut cx.id_map, + &mut ids, error_codes, cx.shared.edition(), &cx.shared.playground, @@ -614,7 +616,7 @@ fn short_item_info( // Render the list of items inside one of the sections "Trait Implementations", // "Auto Trait Implementations," "Blanket Trait Implementations" (on struct/enum pages). pub(crate) fn render_impls( - cx: &mut Context<'_>, + cx: &Context<'_>, w: &mut Buffer, impls: &[&Impl], containing_item: &clean::Item, @@ -1037,7 +1039,7 @@ fn write_impl_section_heading(w: &mut Buffer, title: &str, id: &str) { pub(crate) fn render_all_impls( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, containing_item: &clean::Item, concrete: &[&Impl], synthetic: &[&Impl], @@ -1068,7 +1070,7 @@ pub(crate) fn render_all_impls( fn render_assoc_items( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, containing_item: &clean::Item, it: DefId, what: AssocItemRender<'_>, @@ -1080,14 +1082,14 @@ fn render_assoc_items( fn render_assoc_items_inner( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, containing_item: &clean::Item, it: DefId, what: AssocItemRender<'_>, derefs: &mut FxHashSet, ) { info!("Documenting associated items of {:?}", containing_item.name); - let shared = Rc::clone(&cx.shared); + let shared = &cx.shared; let cache = &shared.cache; let Some(v) = cache.impls.get(&it) else { return }; let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); @@ -1102,7 +1104,7 @@ fn render_assoc_items_inner( let id = cx.derive_id(small_url_encode(format!("deref-methods-{:#}", type_.print(cx)))); if let Some(def_id) = type_.def_id(cx.cache()) { - cx.deref_id_map.insert(def_id, id.clone()); + cx.deref_id_map().borrow_mut().insert(def_id, id.clone()); } write_impl_section_heading( &mut tmp_buf, @@ -1169,7 +1171,7 @@ fn render_assoc_items_inner( fn render_deref_methods( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, impl_: &Impl, container_item: &clean::Item, deref_mut: bool, @@ -1315,7 +1317,7 @@ struct ImplRenderingParameters { fn render_impl( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, i: &Impl, parent: &clean::Item, link: AssocItemLink<'_>, @@ -1324,7 +1326,7 @@ fn render_impl( aliases: &[String], rendering_params: ImplRenderingParameters, ) { - let shared = Rc::clone(&cx.shared); + let shared = &cx.shared; let cache = &shared.cache; let traits = &cache.traits; let trait_ = i.trait_did().map(|did| &traits[&did]); @@ -1338,7 +1340,7 @@ fn render_impl( fn doc_impl_item( boring: &mut Buffer, interesting: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, item: &clean::Item, parent: &clean::Item, containing_item: &clean::Item, @@ -1563,7 +1565,7 @@ fn render_impl( fn render_default_items( boring: &mut Buffer, interesting: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, t: &clean::Trait, i: &clean::Impl, parent: &clean::Item, @@ -1657,13 +1659,14 @@ fn render_impl( ", ); } + let mut ids = cx.id_map().borrow_mut(); write!( w, "
{}
", Markdown { content: &*dox, links: &i.impl_item.links(cx), - ids: &mut cx.id_map, + ids: &mut ids, error_codes: cx.shared.codes, edition: cx.shared.edition(), playground: &cx.shared.playground, @@ -1727,7 +1730,7 @@ fn render_rightside( pub(crate) fn render_impl_summary( w: &mut Buffer, - cx: &mut Context<'_>, + cx: &Context<'_>, i: &Impl, parent: &clean::Item, containing_item: &clean::Item, @@ -2135,10 +2138,15 @@ fn sidebar_deref_methods( .flat_map(|i| get_methods(i.inner_impl(), true, used_links, deref_mut, cx.tcx())) .collect::>(); if !ret.is_empty() { - let id = if let Some(target_def_id) = real_target.def_id(c) { - cx.deref_id_map.get(&target_def_id).expect("Deref section without derived id") + let id: Cow<'_, str> = if let Some(target_def_id) = real_target.def_id(c) { + let deref_id_map = cx.deref_id_map().borrow(); + let id: String = deref_id_map + .get(&target_def_id) + .expect("Deref section without derived id") + .to_owned(); + Cow::Owned(id) } else { - "deref-methods" + Cow::Borrowed("deref-methods") }; let title = format!( "Methods from {}<Target={}>", @@ -2147,7 +2155,7 @@ fn sidebar_deref_methods( ); // We want links' order to be reproducible so we don't use unstable sort. ret.sort(); - print_sidebar_block(out, id, &title, ret.iter()); + print_sidebar_block(out, id.as_ref(), &title, ret.iter()); } } @@ -2359,7 +2367,7 @@ fn sidebar_trait(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item, t: &clean /// implementations that are on concrete or partially generic types, only keeping implementations /// of the form `impl Trait for &T`. pub(crate) fn get_filtered_impls_for_reference<'a>( - shared: &'a Rc>, + shared: &'a SharedContext<'_>, it: &clean::Item, ) -> (Vec<&'a Impl>, Vec<&'a Impl>, Vec<&'a Impl>) { let def_id = it.item_id.expect_def_id(); @@ -2391,8 +2399,7 @@ fn sidebar_primitive(cx: &Context<'_>, buf: &mut Buffer, it: &clean::Item) { if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { sidebar_assoc_items(cx, &mut sidebar, it); } else { - let shared = Rc::clone(&cx.shared); - let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it); + let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&cx.shared, it); sidebar_render_assoc_items( cx, @@ -2726,14 +2733,14 @@ const MAX_FULL_EXAMPLES: usize = 5; const NUM_VISIBLE_LINES: usize = 10; /// Generates the HTML for example call locations generated via the --scrape-examples flag. -fn render_call_locations(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item) { +fn render_call_locations(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item) { let tcx = cx.tcx(); let def_id = item.item_id.expect_def_id(); let key = tcx.def_path_hash(def_id); let Some(call_locations) = cx.shared.call_locations.get(&key) else { return }; // Generate a unique ID so users can link to this section for a given method - let id = cx.id_map.derive("scraped-examples"); + let id = cx.id_map().borrow_mut().derive("scraped-examples"); write!( w, "
\ diff --git a/src/librustdoc/html/render/print_item.rs b/src/librustdoc/html/render/print_item.rs index cfa4509428f10..5e59461ed2799 100644 --- a/src/librustdoc/html/render/print_item.rs +++ b/src/librustdoc/html/render/print_item.rs @@ -13,7 +13,6 @@ use rustc_span::symbol::{kw, sym, Symbol}; use rustc_target::abi::{Layout, Primitive, TagEncoding, Variants}; use std::cmp::Ordering; use std::fmt; -use std::rc::Rc; use super::{ collect_paths_for_type, document, ensure_trailing_slash, get_filtered_impls_for_reference, @@ -73,12 +72,7 @@ fn print_where_clause_and_check<'a, 'tcx: 'a>( len_before != buffer.len() } -pub(super) fn print_item( - cx: &mut Context<'_>, - item: &clean::Item, - buf: &mut Buffer, - page: &Page<'_>, -) { +pub(super) fn print_item(cx: &Context<'_>, item: &clean::Item, buf: &mut Buffer, page: &Page<'_>) { debug_assert!(!item.is_stripped()); let typ = match *item.kind { clean::ModuleItem(_) => { @@ -129,7 +123,7 @@ pub(super) fn print_item( // this page, and this link will be auto-clicked. The `id` attribute is // used to find the link to auto-click. let src_href = - if cx.include_sources && !item.is_primitive() { cx.src_href(item) } else { None }; + if cx.include_sources() && !item.is_primitive() { cx.src_href(item) } else { None }; let path_components = if item.is_primitive() || item.is_keyword() { vec![] @@ -205,7 +199,7 @@ fn toggle_close(w: &mut Buffer) { w.write_str(""); } -fn item_module(w: &mut Buffer, cx: &mut Context<'_>, item: &clean::Item, items: &[clean::Item]) { +fn item_module(w: &mut Buffer, cx: &Context<'_>, item: &clean::Item, items: &[clean::Item]) { document(w, cx, item, None, HeadingOffset::H2); let mut indices = (0..items.len()).filter(|i| !items[*i].is_stripped()).collect::>(); @@ -495,7 +489,7 @@ fn extra_info_tags(item: &clean::Item, parent: &clean::Item, tcx: TyCtxt<'_>) -> tags } -fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &clean::Function) { +fn item_function(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, f: &clean::Function) { let header = it.fn_header(cx.tcx()).expect("printing a function which isn't a function"); let constness = print_constness_with_space(&header.constness, it.const_stability(cx.tcx())); let unsafety = header.unsafety.print_with_space(); @@ -538,7 +532,7 @@ fn item_function(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, f: &cle document(w, cx, it, None, HeadingOffset::H2) } -fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Trait) { +fn item_trait(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Trait) { let bounds = bounds(&t.bounds, false, cx); let required_types = t.items.iter().filter(|m| m.is_ty_associated_type()).collect::>(); let provided_types = t.items.iter().filter(|m| m.is_associated_type()).collect::>(); @@ -707,7 +701,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: ) } - fn trait_item(w: &mut Buffer, cx: &mut Context<'_>, m: &clean::Item, t: &clean::Item) { + fn trait_item(w: &mut Buffer, cx: &Context<'_>, m: &clean::Item, t: &clean::Item) { let name = m.name.unwrap(); info!("Documenting {} on {:?}", name, t.name); let item_type = m.type_(); @@ -826,8 +820,8 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: // If there are methods directly on this trait object, render them here. render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All); - let cloned_shared = Rc::clone(&cx.shared); - let cache = &cloned_shared.cache; + let shared = &cx.shared; + let cache = &shared.cache; let mut extern_crates = FxHashSet::default(); if let Some(implementors) = cache.implementors.get(&it.item_id.expect_def_id()) { // The DefId is for the first Type found with that name. The bool is @@ -1032,7 +1026,7 @@ fn item_trait(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean: ); } -fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { +fn item_trait_alias(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::TraitAlias) { wrap_into_docblock(w, |w| { wrap_item(w, "trait-alias", |w| { render_attributes_in_pre(w, it, ""); @@ -1056,7 +1050,7 @@ fn item_trait_alias(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: & render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) } -fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { +fn item_opaque_ty(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::OpaqueTy) { wrap_into_docblock(w, |w| { wrap_item(w, "opaque", |w| { render_attributes_in_pre(w, it, ""); @@ -1080,7 +1074,7 @@ fn item_opaque_ty(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &cl render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) } -fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Typedef) { +fn item_typedef(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { fn write_content(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Typedef) { wrap_item(w, "typedef", |w| { render_attributes_in_pre(w, it, ""); @@ -1109,7 +1103,7 @@ fn item_typedef(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clea document_type_layout(w, cx, def_id); } -fn item_union(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Union) { +fn item_union(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Union) { wrap_into_docblock(w, |w| { wrap_item(w, "union", |w| { render_attributes_in_pre(w, it, ""); @@ -1172,7 +1166,7 @@ fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item] } } -fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean::Enum) { +fn item_enum(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, e: &clean::Enum) { let count_variants = e.variants().count(); wrap_into_docblock(w, |w| { wrap_item(w, "enum", |w| { @@ -1332,14 +1326,14 @@ fn item_enum(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, e: &clean:: document_type_layout(w, cx, def_id); } -fn item_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, t: &clean::Macro) { +fn item_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, t: &clean::Macro) { wrap_into_docblock(w, |w| { highlight::render_macro_with_highlighting(&t.source, w); }); document(w, cx, it, None, HeadingOffset::H2) } -fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { +fn item_proc_macro(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, m: &clean::ProcMacro) { wrap_into_docblock(w, |w| { let name = it.name.expect("proc-macros always have names"); match m.kind { @@ -1371,7 +1365,7 @@ fn item_proc_macro(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, m: &c document(w, cx, it, None, HeadingOffset::H2) } -fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { +fn item_primitive(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { let def_id = it.item_id.expect_def_id(); document(w, cx, it, None, HeadingOffset::H2); if it.name.map(|n| n.as_str() != "reference").unwrap_or(false) { @@ -1379,14 +1373,13 @@ fn item_primitive(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { } else { // We handle the "reference" primitive type on its own because we only want to list // implementations on generic types. - let shared = Rc::clone(&cx.shared); - let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&shared, it); + let (concrete, synthetic, blanket_impl) = get_filtered_impls_for_reference(&cx.shared, it); render_all_impls(w, cx, it, &concrete, &synthetic, &blanket_impl); } } -fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &clean::Constant) { +fn item_constant(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, c: &clean::Constant) { wrap_into_docblock(w, |w| { wrap_item(w, "const", |w| { render_attributes_in_code(w, it); @@ -1435,7 +1428,7 @@ fn item_constant(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, c: &cle document(w, cx, it, None, HeadingOffset::H2) } -fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Struct) { +fn item_struct(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Struct) { wrap_into_docblock(w, |w| { wrap_item(w, "struct", |w| { render_attributes_in_code(w, it); @@ -1488,7 +1481,7 @@ fn item_struct(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean document_type_layout(w, cx, def_id); } -fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean::Static) { +fn item_static(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item, s: &clean::Static) { wrap_into_docblock(w, |w| { wrap_item(w, "static", |w| { render_attributes_in_code(w, it); @@ -1505,7 +1498,7 @@ fn item_static(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item, s: &clean document(w, cx, it, None, HeadingOffset::H2) } -fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { +fn item_foreign_type(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { wrap_into_docblock(w, |w| { wrap_item(w, "foreigntype", |w| { w.write_str("extern {\n"); @@ -1524,7 +1517,7 @@ fn item_foreign_type(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { render_assoc_items(w, cx, it, it.item_id.expect_def_id(), AssocItemRender::All) } -fn item_keyword(w: &mut Buffer, cx: &mut Context<'_>, it: &clean::Item) { +fn item_keyword(w: &mut Buffer, cx: &Context<'_>, it: &clean::Item) { document(w, cx, it, None, HeadingOffset::H2) } @@ -1622,7 +1615,7 @@ fn compare_impl<'a, 'b>(lhs: &'a &&Impl, rhs: &'b &&Impl, cx: &Context<'_>) -> O } fn render_implementor( - cx: &mut Context<'_>, + cx: &Context<'_>, implementor: &Impl, trait_: &clean::Item, w: &mut Buffer, diff --git a/src/librustdoc/html/render/write_shared.rs b/src/librustdoc/html/render/write_shared.rs index fc4d46fe6b6f1..190c6b2a4bf18 100644 --- a/src/librustdoc/html/render/write_shared.rs +++ b/src/librustdoc/html/render/write_shared.rs @@ -3,7 +3,6 @@ use std::fs::{self, File}; use std::io::prelude::*; use std::io::{self, BufReader}; use std::path::{Component, Path, PathBuf}; -use std::rc::Rc; use std::sync::LazyLock as Lazy; use itertools::Itertools; @@ -233,10 +232,10 @@ pub(super) fn write_shared( themes.insert(theme.to_owned()); } - if (*cx.shared).layout.logo.is_empty() { + if cx.shared.layout.logo.is_empty() { write_toolchain("rust-logo.svg", static_files::RUST_LOGO_SVG)?; } - if (*cx.shared).layout.favicon.is_empty() { + if cx.shared.layout.favicon.is_empty() { write_toolchain("favicon.svg", static_files::RUST_FAVICON_SVG)?; write_toolchain("favicon-16x16.png", static_files::RUST_FAVICON_PNG_16)?; write_toolchain("favicon-32x32.png", static_files::RUST_FAVICON_PNG_32)?; @@ -254,7 +253,7 @@ pub(super) fn write_shared( write_minify("search.js", static_files::SEARCH_JS, cx, options)?; write_minify("settings.js", static_files::SETTINGS_JS, cx, options)?; - if cx.include_sources { + if cx.include_sources() { write_minify("source-script.js", static_files::sidebar::SOURCE_SCRIPT, cx, options)?; } @@ -414,7 +413,7 @@ pub(super) fn write_shared( } } - if cx.include_sources { + if cx.include_sources() { let mut hierarchy = Hierarchy::new(OsString::new()); for source in cx .shared @@ -499,12 +498,12 @@ if (typeof exports !== 'undefined') {exports.searchIndex = searchIndex}; if let Some(index_page) = options.index_page.clone() { let mut md_opts = options.clone(); md_opts.output = cx.dst.clone(); - md_opts.external_html = (*cx.shared).layout.external_html.clone(); + md_opts.external_html = cx.shared.layout.external_html.clone(); crate::markdown::render(&index_page, md_opts, cx.shared.edition()) .map_err(|e| Error::new(e, &index_page))?; } else { - let shared = Rc::clone(&cx.shared); + let shared = &cx.shared; let dst = cx.dst.join("index.html"); let page = layout::Page { title: "Index of crates", diff --git a/src/librustdoc/html/sources.rs b/src/librustdoc/html/sources.rs index f37c54e42983f..6c2e615f07fce 100644 --- a/src/librustdoc/html/sources.rs +++ b/src/librustdoc/html/sources.rs @@ -16,7 +16,6 @@ use rustc_span::source_map::FileName; use std::ffi::OsStr; use std::fs; use std::path::{Component, Path, PathBuf}; -use std::rc::Rc; pub(crate) fn render(cx: &mut Context<'_>, krate: &clean::Crate) -> Result<(), Error> { info!("emitting source files"); @@ -104,7 +103,7 @@ struct SourceCollector<'a, 'tcx> { impl DocVisitor for SourceCollector<'_, '_> { fn visit_item(&mut self, item: &clean::Item) { - if !self.cx.include_sources { + if !self.cx.include_sources() { return; } @@ -126,7 +125,7 @@ impl DocVisitor for SourceCollector<'_, '_> { // something like that), so just don't include sources for the // entire crate. The other option is maintaining this mapping on a // per-file basis, but that's probably not worth it... - self.cx.include_sources = match self.emit_source(&filename, file_span) { + let include_sources = match self.emit_source(&filename, file_span) { Ok(()) => true, Err(e) => { self.cx.shared.tcx.sess.span_err( @@ -140,6 +139,7 @@ impl DocVisitor for SourceCollector<'_, '_> { false } }; + self.cx.set_include_sources(include_sources); } self.visit_item_recur(item) @@ -178,7 +178,7 @@ impl SourceCollector<'_, '_> { // Remove the utf-8 BOM if any let contents = contents.strip_prefix('\u{feff}').unwrap_or(&contents); - let shared = Rc::clone(&self.cx.shared); + let shared = &self.cx.shared; // Create the intermediate directories let mut cur = self.dst.clone(); let mut root_path = String::from("../../"); @@ -210,7 +210,7 @@ impl SourceCollector<'_, '_> { &page, "", |buf: &mut _| { - let cx = &mut self.cx; + let cx = &self.cx; print_src( buf, contents, diff --git a/src/librustdoc/json/mod.rs b/src/librustdoc/json/mod.rs index 6a1409c739424..0914ab01223d0 100644 --- a/src/librustdoc/json/mod.rs +++ b/src/librustdoc/json/mod.rs @@ -176,10 +176,6 @@ 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.