Skip to content

Commit 4b9f5cc

Browse files
committed
Auto merge of rust-lang#82356 - camelid:render-cleanup, r=GuillaumeGomez
rustdoc: Cleanup `html::render::Context` - Move most shared fields to `SharedContext` (except for `cache`, which isn't mutated anyway) - Replace a use of `Arc` with `Rc` - Make a bunch of fields private - Add static size assertion for `Context` - Don't share `id_map` and `deref_id_map`
2 parents bb3afe1 + 5b74097 commit 4b9f5cc

File tree

6 files changed

+84
-60
lines changed

6 files changed

+84
-60
lines changed

src/librustdoc/formats/renderer.rs

+6-3
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ use crate::formats::cache::Cache;
99
/// Allows for different backends to rustdoc to be used with the `run_format()` function. Each
1010
/// backend renderer has hooks for initialization, documenting an item, entering and exiting a
1111
/// module, and cleanup/finalizing output.
12-
crate trait FormatRenderer<'tcx>: Clone {
12+
crate trait FormatRenderer<'tcx>: Sized {
1313
/// Gives a description of the renderer. Used for performance profiling.
1414
fn descr() -> &'static str;
1515

@@ -23,6 +23,9 @@ crate trait FormatRenderer<'tcx>: Clone {
2323
tcx: TyCtxt<'tcx>,
2424
) -> Result<(Self, clean::Crate), Error>;
2525

26+
/// Make a new renderer to render a child of the item currently being rendered.
27+
fn make_child_renderer(&self) -> Self;
28+
2629
/// Renders a single non-module item. This means no recursive sub-item rendering is required.
2730
fn item(&mut self, item: clean::Item) -> Result<(), Error>;
2831

@@ -67,7 +70,7 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>(
6770
item.name = Some(krate.name);
6871

6972
// Render the crate documentation
70-
let mut work = vec![(format_renderer.clone(), item)];
73+
let mut work = vec![(format_renderer.make_child_renderer(), item)];
7174

7275
let unknown = rustc_span::Symbol::intern("<unknown item>");
7376
while let Some((mut cx, item)) = work.pop() {
@@ -87,7 +90,7 @@ crate fn run_format<'tcx, T: FormatRenderer<'tcx>>(
8790
};
8891
for it in module.items {
8992
debug!("Adding {:?} to worklist", it.name);
90-
work.push((cx.clone(), it));
93+
work.push((cx.make_child_renderer(), it));
9194
}
9295

9396
cx.mod_item_out(&name)?;

src/librustdoc/html/markdown.rs

-4
Original file line numberDiff line numberDiff line change
@@ -1373,10 +1373,6 @@ impl IdMap {
13731373
}
13741374
}
13751375

1376-
crate fn reset(&mut self) {
1377-
self.map = init_id_map();
1378-
}
1379-
13801376
crate fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
13811377
let id = match self.map.get_mut(candidate.as_ref()) {
13821378
None => candidate.to_string(),

src/librustdoc/html/markdown/tests.rs

+3-10
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use super::{plain_text_summary, short_markdown_summary};
22
use super::{ErrorCodes, IdMap, Ignore, LangString, Markdown, MarkdownHtml};
33
use rustc_span::edition::{Edition, DEFAULT_EDITION};
4-
use std::cell::RefCell;
54

65
#[test]
76
fn test_unique_id() {
@@ -38,15 +37,9 @@ fn test_unique_id() {
3837
"assoc_type.Item-1",
3938
];
4039

41-
let map = RefCell::new(IdMap::new());
42-
let test = || {
43-
let mut map = map.borrow_mut();
44-
let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
45-
assert_eq!(&actual[..], expected);
46-
};
47-
test();
48-
map.borrow_mut().reset();
49-
test();
40+
let mut map = IdMap::new();
41+
let actual: Vec<String> = input.iter().map(|s| map.derive(s.to_string())).collect();
42+
assert_eq!(&actual[..], expected);
5043
}
5144

5245
#[test]

src/librustdoc/html/render/context.rs

+56-38
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ use std::collections::BTreeMap;
33
use std::io;
44
use std::path::PathBuf;
55
use std::rc::Rc;
6-
use std::sync::mpsc::{channel, Receiver};
7-
use std::sync::Arc;
6+
use std::sync::mpsc::channel;
87

98
use rustc_data_structures::fx::FxHashMap;
109
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
@@ -41,35 +40,44 @@ use crate::html::{layout, sources};
4140
/// It is intended that this context is a lightweight object which can be fairly
4241
/// easily cloned because it is cloned per work-job (about once per item in the
4342
/// rustdoc tree).
44-
#[derive(Clone)]
4543
crate struct Context<'tcx> {
4644
/// Current hierarchy of components leading down to what's currently being
4745
/// rendered
48-
crate current: Vec<String>,
46+
pub(super) current: Vec<String>,
4947
/// The current destination folder of where HTML artifacts should be placed.
5048
/// This changes as the context descends into the module hierarchy.
51-
crate dst: PathBuf,
49+
pub(super) dst: PathBuf,
5250
/// A flag, which when `true`, will render pages which redirect to the
5351
/// real location of an item. This is used to allow external links to
5452
/// publicly reused items to redirect to the right location.
55-
crate render_redirect_pages: bool,
56-
/// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set
57-
/// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of
58-
/// the crate.
59-
crate redirections: Option<Rc<RefCell<FxHashMap<String, String>>>>,
53+
pub(super) render_redirect_pages: bool,
6054
/// The map used to ensure all generated 'id=' attributes are unique.
61-
pub(super) id_map: Rc<RefCell<IdMap>>,
55+
pub(super) id_map: RefCell<IdMap>,
6256
/// Tracks section IDs for `Deref` targets so they match in both the main
6357
/// body and the sidebar.
64-
pub(super) deref_id_map: Rc<RefCell<FxHashMap<DefId, String>>>,
65-
crate shared: Arc<SharedContext<'tcx>>,
66-
all: Rc<RefCell<AllTypes>>,
67-
/// Storage for the errors produced while generating documentation so they
68-
/// can be printed together at the end.
69-
crate errors: Rc<Receiver<String>>,
70-
crate cache: Rc<Cache>,
58+
pub(super) deref_id_map: RefCell<FxHashMap<DefId, String>>,
59+
/// Shared mutable state.
60+
///
61+
/// Issue for improving the situation: [#82381][]
62+
///
63+
/// [#82381]: https://github.com/rust-lang/rust/issues/82381
64+
pub(super) shared: Rc<SharedContext<'tcx>>,
65+
/// The [`Cache`] used during rendering.
66+
///
67+
/// Ideally the cache would be in [`SharedContext`], but it's mutated
68+
/// between when the `SharedContext` is created and when `Context`
69+
/// is created, so more refactoring would be needed.
70+
///
71+
/// It's immutable once in `Context`, so it's not as bad that it's not in
72+
/// `SharedContext`.
73+
// FIXME: move `cache` to `SharedContext`
74+
pub(super) cache: Rc<Cache>,
7175
}
7276

77+
// `Context` is cloned a lot, so we don't want the size to grow unexpectedly.
78+
#[cfg(target_arch = "x86_64")]
79+
rustc_data_structures::static_assert_size!(Context<'_>, 152);
80+
7381
impl<'tcx> Context<'tcx> {
7482
pub(super) fn path(&self, filename: &str) -> PathBuf {
7583
// We use splitn vs Path::extension here because we might get a filename
@@ -148,11 +156,6 @@ impl<'tcx> Context<'tcx> {
148156
static_extra_scripts: &[],
149157
};
150158

151-
{
152-
self.id_map.borrow_mut().reset();
153-
self.id_map.borrow_mut().populate(&INITIAL_IDS);
154-
}
155-
156159
if !self.render_redirect_pages {
157160
layout::render(
158161
&self.shared.layout,
@@ -169,7 +172,7 @@ impl<'tcx> Context<'tcx> {
169172
path.push('/');
170173
}
171174
path.push_str(&item_path(ty, names.last().unwrap()));
172-
match self.redirections {
175+
match self.shared.redirections {
173176
Some(ref redirections) => {
174177
let mut current_path = String::new();
175178
for name in &self.current {
@@ -383,6 +386,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
383386
edition,
384387
codes: ErrorCodes::from(unstable_features.is_nightly_build()),
385388
playground,
389+
all: RefCell::new(AllTypes::new()),
390+
errors: receiver,
391+
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
386392
};
387393

388394
// Add the default themes to the `Vec` of stylepaths
@@ -409,24 +415,36 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
409415
current: Vec::new(),
410416
dst,
411417
render_redirect_pages: false,
412-
id_map: Rc::new(RefCell::new(id_map)),
413-
deref_id_map: Rc::new(RefCell::new(FxHashMap::default())),
414-
shared: Arc::new(scx),
415-
all: Rc::new(RefCell::new(AllTypes::new())),
416-
errors: Rc::new(receiver),
418+
id_map: RefCell::new(id_map),
419+
deref_id_map: RefCell::new(FxHashMap::default()),
420+
shared: Rc::new(scx),
417421
cache: Rc::new(cache),
418-
redirections: if generate_redirect_map { Some(Default::default()) } else { None },
419422
};
420423

421424
CURRENT_DEPTH.with(|s| s.set(0));
422425

423426
// Write shared runs within a flock; disable thread dispatching of IO temporarily.
424-
Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
427+
Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(true);
425428
write_shared(&cx, &krate, index, &md_opts)?;
426-
Arc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
429+
Rc::get_mut(&mut cx.shared).unwrap().fs.set_sync_only(false);
427430
Ok((cx, krate))
428431
}
429432

433+
fn make_child_renderer(&self) -> Self {
434+
let mut id_map = IdMap::new();
435+
id_map.populate(&INITIAL_IDS);
436+
437+
Self {
438+
current: self.current.clone(),
439+
dst: self.dst.clone(),
440+
render_redirect_pages: self.render_redirect_pages,
441+
id_map: RefCell::new(id_map),
442+
deref_id_map: RefCell::new(FxHashMap::default()),
443+
shared: Rc::clone(&self.shared),
444+
cache: Rc::clone(&self.cache),
445+
}
446+
}
447+
430448
fn after_krate(
431449
&mut self,
432450
krate: &clean::Crate,
@@ -464,7 +482,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
464482
} else {
465483
String::new()
466484
};
467-
let all = self.all.replace(AllTypes::new());
485+
let all = self.shared.all.replace(AllTypes::new());
468486
let v = layout::render(
469487
&self.shared.layout,
470488
&page,
@@ -494,7 +512,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
494512
&style_files,
495513
);
496514
self.shared.fs.write(&settings_file, v.as_bytes())?;
497-
if let Some(redirections) = self.redirections.take() {
515+
if let Some(ref redirections) = self.shared.redirections {
498516
if !redirections.borrow().is_empty() {
499517
let redirect_map_path =
500518
self.dst.join(&*krate.name.as_str()).join("redirect-map.json");
@@ -505,8 +523,8 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
505523
}
506524

507525
// Flush pending errors.
508-
Arc::get_mut(&mut self.shared).unwrap().fs.close();
509-
let nb_errors = self.errors.iter().map(|err| diag.struct_err(&err).emit()).count();
526+
Rc::get_mut(&mut self.shared).unwrap().fs.close();
527+
let nb_errors = self.shared.errors.iter().map(|err| diag.struct_err(&err).emit()).count();
510528
if nb_errors > 0 {
511529
Err(Error::new(io::Error::new(io::ErrorKind::Other, "I/O error"), ""))
512530
} else {
@@ -585,13 +603,13 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
585603
self.shared.fs.write(&joint_dst, buf.as_bytes())?;
586604

587605
if !self.render_redirect_pages {
588-
self.all.borrow_mut().append(full_path(self, &item), &item_type);
606+
self.shared.all.borrow_mut().append(full_path(self, &item), &item_type);
589607
}
590608
// If the item is a macro, redirect from the old macro URL (with !)
591609
// to the new one (without).
592610
if item_type == ItemType::Macro {
593611
let redir_name = format!("{}.{}!.html", item_type, name);
594-
if let Some(ref redirections) = self.redirections {
612+
if let Some(ref redirections) = self.shared.redirections {
595613
let crate_name = &self.shared.layout.krate;
596614
redirections.borrow_mut().insert(
597615
format!("{}/{}", crate_name, redir_name),

src/librustdoc/html/render/mod.rs

+15-5
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ use std::fmt;
4242
use std::path::{Path, PathBuf};
4343
use std::str;
4444
use std::string::ToString;
45+
use std::sync::mpsc::Receiver;
4546

4647
use itertools::Itertools;
4748
use rustc_ast_pretty::pprust;
@@ -81,6 +82,7 @@ crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
8182
})
8283
}
8384

85+
/// Shared mutable state used in [`Context`] and elsewhere.
8486
crate struct SharedContext<'tcx> {
8587
crate tcx: TyCtxt<'tcx>,
8688
/// The path to the crate root source minus the file name.
@@ -96,16 +98,16 @@ crate struct SharedContext<'tcx> {
9698
/// The local file sources we've emitted and their respective url-paths.
9799
crate local_sources: FxHashMap<PathBuf, String>,
98100
/// Whether the collapsed pass ran
99-
crate collapsed: bool,
101+
collapsed: bool,
100102
/// The base-URL of the issue tracker for when an item has been tagged with
101103
/// an issue number.
102-
crate issue_tracker_base_url: Option<String>,
104+
issue_tracker_base_url: Option<String>,
103105
/// The directories that have already been created in this doc run. Used to reduce the number
104106
/// of spurious `create_dir_all` calls.
105-
crate created_dirs: RefCell<FxHashSet<PathBuf>>,
107+
created_dirs: RefCell<FxHashSet<PathBuf>>,
106108
/// This flag indicates whether listings of modules (in the side bar and documentation itself)
107109
/// should be ordered alphabetically or in order of appearance (in the source code).
108-
crate sort_modules_alphabetically: bool,
110+
sort_modules_alphabetically: bool,
109111
/// Additional CSS files to be added to the generated docs.
110112
crate style_files: Vec<StylePath>,
111113
/// Suffix to be added on resource files (if suffix is "-v2" then "light.css" becomes
@@ -118,8 +120,16 @@ crate struct SharedContext<'tcx> {
118120
crate fs: DocFS,
119121
/// The default edition used to parse doctests.
120122
crate edition: Edition,
121-
crate codes: ErrorCodes,
123+
codes: ErrorCodes,
122124
playground: Option<markdown::Playground>,
125+
all: RefCell<AllTypes>,
126+
/// Storage for the errors produced while generating documentation so they
127+
/// can be printed together at the end.
128+
errors: Receiver<String>,
129+
/// `None` by default, depends on the `generate-redirect-map` option flag. If this field is set
130+
/// to `Some(...)`, it'll store redirections and then generate a JSON file at the top level of
131+
/// the crate.
132+
redirections: Option<RefCell<FxHashMap<String, String>>>,
123133
}
124134

125135
impl SharedContext<'_> {

src/librustdoc/json/mod.rs

+4
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,10 @@ impl<'tcx> FormatRenderer<'tcx> for JsonRenderer<'tcx> {
148148
))
149149
}
150150

151+
fn make_child_renderer(&self) -> Self {
152+
self.clone()
153+
}
154+
151155
/// Inserts an item into the index. This should be used rather than directly calling insert on
152156
/// the hashmap because certain items (traits and types) need to have their mappings for trait
153157
/// implementations filled out before they're inserted.

0 commit comments

Comments
 (0)