Skip to content

Commit 4ff9023

Browse files
committed
Auto merge of rust-lang#90475 - camelid:docvisitor, r=notriddle
rustdoc: Add `DocVisitor` and use it where possible `DocFolder` allows transforming the docs, accomplished by making its methods take and return types by-value. However, several of the rustdoc `DocFolder` impls only *visit* the docs; they don't change anything. Passing around types by-value is thus unnecessary, confusing, and potentially inefficient for those impls. `DocVisitor` is very similar to `DocFolder`, except that its methods take shared references and return nothing (i.e., the unit type). This should both be more efficient and make the code clearer. There is an additional reason to add `DocVisitor`, too. As part of my cleanup of `external_traits`, I'm planning to add a `fn cache(&mut self) -> &mut Cache` method to `DocFolder` so that `external_traits` can be retrieved explicitly from the `Cache`, rather than implicitly via `Crate.external_traits` (which is an `Rc<RefCell<...>>`). However, some of the `DocFolder` impls that could be turned into `DocVisitor` impls only have a shared reference to the `Cache`, because they are used during rendering. (They have to access the `Cache` via `html::render::Context.shared.cache`, which involves an `Rc`.) Since `DocVisitor` does not mutate any of the types it's visiting, its equivalent `cache()` method will only need a shared reference to the `Cache`, avoiding the problem described above. r? `@GuillaumeGomez` cc `@jyn514`
2 parents baba668 + 8e4bcdf commit 4ff9023

13 files changed

+218
-125
lines changed

src/librustdoc/fold.rs

+38-24
Original file line numberDiff line numberDiff line change
@@ -46,24 +46,40 @@ crate trait DocFolder: Sized {
4646
i.items = i.items.into_iter().filter_map(|x| self.fold_item(x)).collect();
4747
ImplItem(i)
4848
}
49-
VariantItem(i) => {
50-
let i2 = i.clone(); // this clone is small
51-
match i {
52-
Variant::Struct(mut j) => {
53-
let num_fields = j.fields.len();
54-
j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
55-
j.fields_stripped |= num_fields != j.fields.len()
56-
|| j.fields.iter().any(|f| f.is_stripped());
57-
VariantItem(Variant::Struct(j))
58-
}
59-
Variant::Tuple(fields) => {
60-
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
61-
VariantItem(Variant::Tuple(fields))
62-
}
63-
_ => VariantItem(i2),
49+
VariantItem(i) => match i {
50+
Variant::Struct(mut j) => {
51+
let num_fields = j.fields.len();
52+
j.fields = j.fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
53+
j.fields_stripped |=
54+
num_fields != j.fields.len() || j.fields.iter().any(|f| f.is_stripped());
55+
VariantItem(Variant::Struct(j))
6456
}
65-
}
66-
x => x,
57+
Variant::Tuple(fields) => {
58+
let fields = fields.into_iter().filter_map(|x| self.fold_item(x)).collect();
59+
VariantItem(Variant::Tuple(fields))
60+
}
61+
Variant::CLike => VariantItem(Variant::CLike),
62+
},
63+
ExternCrateItem { src: _ }
64+
| ImportItem(_)
65+
| FunctionItem(_)
66+
| TypedefItem(_, _)
67+
| OpaqueTyItem(_)
68+
| StaticItem(_)
69+
| ConstantItem(_)
70+
| TraitAliasItem(_)
71+
| TyMethodItem(_)
72+
| MethodItem(_, _)
73+
| StructFieldItem(_)
74+
| ForeignFunctionItem(_)
75+
| ForeignStaticItem(_)
76+
| ForeignTypeItem
77+
| MacroItem(_)
78+
| ProcMacroItem(_)
79+
| PrimitiveItem(_)
80+
| AssocConstItem(_, _)
81+
| AssocTypeItem(_, _)
82+
| KeywordItem(_) => kind,
6783
}
6884
}
6985

@@ -86,14 +102,12 @@ crate trait DocFolder: Sized {
86102
fn fold_crate(&mut self, mut c: Crate) -> Crate {
87103
c.module = self.fold_item(c.module).unwrap();
88104

89-
{
90-
let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) };
91-
for (k, mut v) in external_traits {
92-
v.trait_.items =
93-
v.trait_.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
94-
c.external_traits.borrow_mut().insert(k, v);
95-
}
105+
let external_traits = { std::mem::take(&mut *c.external_traits.borrow_mut()) };
106+
for (k, mut v) in external_traits {
107+
v.trait_.items = v.trait_.items.into_iter().filter_map(|i| self.fold_item(i)).collect();
108+
c.external_traits.borrow_mut().insert(k, v);
96109
}
110+
97111
c
98112
}
99113
}

src/librustdoc/html/render/context.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -461,9 +461,9 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
461461
}
462462
}
463463

464-
let (mut krate, local_sources, matches) = collect_spans_and_sources(
464+
let (local_sources, matches) = collect_spans_and_sources(
465465
tcx,
466-
krate,
466+
&krate,
467467
&src_root,
468468
include_sources,
469469
generate_link_to_definition,
@@ -522,7 +522,7 @@ impl<'tcx> FormatRenderer<'tcx> for Context<'tcx> {
522522
};
523523

524524
if emit_crate {
525-
krate = sources::render(&mut cx, krate)?;
525+
sources::render(&mut cx, &krate)?;
526526
}
527527

528528
// Build our search index

src/librustdoc/html/render/span_map.rs

+5-5
Original file line numberDiff line numberDiff line change
@@ -37,21 +37,21 @@ crate enum LinkFromSrc {
3737
/// only keep the `lo` and `hi`.
3838
crate fn collect_spans_and_sources(
3939
tcx: TyCtxt<'_>,
40-
krate: clean::Crate,
40+
krate: &clean::Crate,
4141
src_root: &Path,
4242
include_sources: bool,
4343
generate_link_to_definition: bool,
44-
) -> (clean::Crate, FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
44+
) -> (FxHashMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
4545
let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
4646

4747
if include_sources {
4848
if generate_link_to_definition {
4949
tcx.hir().walk_toplevel_module(&mut visitor);
5050
}
51-
let (krate, sources) = sources::collect_local_sources(tcx, src_root, krate);
52-
(krate, sources, visitor.matches)
51+
let sources = sources::collect_local_sources(tcx, src_root, &krate);
52+
(sources, visitor.matches)
5353
} else {
54-
(krate, Default::default(), Default::default())
54+
(Default::default(), Default::default())
5555
}
5656
}
5757

src/librustdoc/html/sources.rs

+32-28
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
use crate::clean;
22
use crate::docfs::PathError;
33
use crate::error::Error;
4-
use crate::fold::DocFolder;
54
use crate::html::format::Buffer;
65
use crate::html::highlight;
76
use crate::html::layout;
87
use crate::html::render::{Context, BASIC_KEYWORDS};
8+
use crate::visit::DocVisitor;
99
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1010
use rustc_hir::def_id::LOCAL_CRATE;
1111
use rustc_middle::ty::TyCtxt;
@@ -16,23 +16,25 @@ use std::ffi::OsStr;
1616
use std::fs;
1717
use std::path::{Component, Path, PathBuf};
1818

19-
crate fn render(cx: &mut Context<'_>, krate: clean::Crate) -> Result<clean::Crate, Error> {
19+
crate fn render(cx: &mut Context<'_>, krate: &clean::Crate) -> Result<(), Error> {
2020
info!("emitting source files");
21+
2122
let dst = cx.dst.join("src").join(&*krate.name(cx.tcx()).as_str());
2223
cx.shared.ensure_dir(&dst)?;
23-
let mut folder = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() };
24-
Ok(folder.fold_crate(krate))
24+
25+
let mut collector = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() };
26+
collector.visit_crate(krate);
27+
Ok(())
2528
}
2629

2730
crate fn collect_local_sources<'tcx>(
2831
tcx: TyCtxt<'tcx>,
2932
src_root: &Path,
30-
krate: clean::Crate,
31-
) -> (clean::Crate, FxHashMap<PathBuf, String>) {
33+
krate: &clean::Crate,
34+
) -> FxHashMap<PathBuf, String> {
3235
let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root };
33-
34-
let krate = lsc.fold_crate(krate);
35-
(krate, lsc.local_sources)
36+
lsc.visit_crate(krate);
37+
lsc.local_sources
3638
}
3739

3840
struct LocalSourcesCollector<'a, 'tcx> {
@@ -42,7 +44,7 @@ struct LocalSourcesCollector<'a, 'tcx> {
4244
}
4345

4446
fn is_real_and_local(span: clean::Span, sess: &Session) -> bool {
45-
span.filename(sess).is_real() && span.cnum(sess) == LOCAL_CRATE
47+
span.cnum(sess) == LOCAL_CRATE && span.filename(sess).is_real()
4648
}
4749

4850
impl LocalSourcesCollector<'_, '_> {
@@ -54,12 +56,13 @@ impl LocalSourcesCollector<'_, '_> {
5456
return;
5557
}
5658
let filename = span.filename(sess);
57-
let p = match filename {
58-
FileName::Real(ref file) => match file.local_path() {
59-
Some(p) => p.to_path_buf(),
60-
_ => return,
61-
},
62-
_ => return,
59+
let p = if let FileName::Real(file) = filename {
60+
match file.into_local_path() {
61+
Some(p) => p,
62+
None => return,
63+
}
64+
} else {
65+
return;
6366
};
6467
if self.local_sources.contains_key(&*p) {
6568
// We've already emitted this source
@@ -79,13 +82,11 @@ impl LocalSourcesCollector<'_, '_> {
7982
}
8083
}
8184

82-
impl DocFolder for LocalSourcesCollector<'_, '_> {
83-
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
84-
self.add_local_source(&item);
85+
impl DocVisitor for LocalSourcesCollector<'_, '_> {
86+
fn visit_item(&mut self, item: &clean::Item) {
87+
self.add_local_source(item);
8588

86-
// FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
87-
// we could return None here without having to walk the rest of the crate.
88-
Some(self.fold_item_recur(item))
89+
self.visit_item_recur(item)
8990
}
9091
}
9192

@@ -98,16 +99,20 @@ struct SourceCollector<'a, 'tcx> {
9899
emitted_local_sources: FxHashSet<PathBuf>,
99100
}
100101

101-
impl DocFolder for SourceCollector<'_, '_> {
102-
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
102+
impl DocVisitor for SourceCollector<'_, '_> {
103+
fn visit_item(&mut self, item: &clean::Item) {
104+
if !self.cx.include_sources {
105+
return;
106+
}
107+
103108
let tcx = self.cx.tcx();
104109
let span = item.span(tcx);
105110
let sess = tcx.sess;
106111

107112
// If we're not rendering sources, there's nothing to do.
108113
// If we're including source files, and we haven't seen this file yet,
109114
// then we need to render it out to the filesystem.
110-
if self.cx.include_sources && is_real_and_local(span, sess) {
115+
if is_real_and_local(span, sess) {
111116
let filename = span.filename(sess);
112117
let span = span.inner();
113118
let pos = sess.source_map().lookup_source_file(span.lo());
@@ -132,9 +137,8 @@ impl DocFolder for SourceCollector<'_, '_> {
132137
}
133138
};
134139
}
135-
// FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
136-
// we could return None here without having to walk the rest of the crate.
137-
Some(self.fold_item_recur(item))
140+
141+
self.visit_item_recur(item)
138142
}
139143
}
140144

src/librustdoc/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,7 @@ mod markdown;
121121
mod passes;
122122
mod scrape_examples;
123123
mod theme;
124+
mod visit;
124125
mod visit_ast;
125126
mod visit_lib;
126127

src/librustdoc/passes/bare_urls.rs

+7-6
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
use super::Pass;
22
use crate::clean::*;
33
use crate::core::DocContext;
4-
use crate::fold::DocFolder;
54
use crate::html::markdown::main_body_opts;
5+
use crate::visit::DocVisitor;
66
use core::ops::Range;
77
use pulldown_cmark::{Event, Parser, Tag};
88
use regex::Regex;
@@ -53,16 +53,17 @@ impl<'a, 'tcx> BareUrlsLinter<'a, 'tcx> {
5353
}
5454

5555
crate fn check_bare_urls(krate: Crate, cx: &mut DocContext<'_>) -> Crate {
56-
BareUrlsLinter { cx }.fold_crate(krate)
56+
BareUrlsLinter { cx }.visit_crate(&krate);
57+
krate
5758
}
5859

59-
impl<'a, 'tcx> DocFolder for BareUrlsLinter<'a, 'tcx> {
60-
fn fold_item(&mut self, item: Item) -> Option<Item> {
60+
impl<'a, 'tcx> DocVisitor for BareUrlsLinter<'a, 'tcx> {
61+
fn visit_item(&mut self, item: &Item) {
6162
let hir_id = match DocContext::as_local_hir_id(self.cx.tcx, item.def_id) {
6263
Some(hir_id) => hir_id,
6364
None => {
6465
// If non-local, no need to check anything.
65-
return Some(self.fold_item_recur(item));
66+
return;
6667
}
6768
};
6869
let dox = item.attrs.collapsed_doc_value().unwrap_or_default();
@@ -106,6 +107,6 @@ impl<'a, 'tcx> DocFolder for BareUrlsLinter<'a, 'tcx> {
106107
}
107108
}
108109

109-
Some(self.fold_item_recur(item))
110+
self.visit_item_recur(item)
110111
}
111112
}

src/librustdoc/passes/calculate_doc_coverage.rs

+12-11
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
use crate::clean;
22
use crate::core::DocContext;
3-
use crate::fold::{self, DocFolder};
43
use crate::html::markdown::{find_testable_code, ErrorCodes};
54
use crate::passes::check_doc_test_visibility::{should_have_doc_example, Tests};
65
use crate::passes::Pass;
6+
use crate::visit::DocVisitor;
77
use rustc_hir as hir;
88
use rustc_lint::builtin::MISSING_DOCS;
99
use rustc_middle::lint::LintLevelSource;
@@ -23,7 +23,7 @@ crate const CALCULATE_DOC_COVERAGE: Pass = Pass {
2323

2424
fn calculate_doc_coverage(krate: clean::Crate, ctx: &mut DocContext<'_>) -> clean::Crate {
2525
let mut calc = CoverageCalculator { items: Default::default(), ctx };
26-
let krate = calc.fold_crate(krate);
26+
calc.visit_crate(&krate);
2727

2828
calc.print_results();
2929

@@ -182,17 +182,18 @@ impl<'a, 'b> CoverageCalculator<'a, 'b> {
182182
}
183183
}
184184

185-
impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
186-
fn fold_item(&mut self, i: clean::Item) -> Option<clean::Item> {
185+
impl<'a, 'b> DocVisitor for CoverageCalculator<'a, 'b> {
186+
fn visit_item(&mut self, i: &clean::Item) {
187+
if !i.def_id.is_local() {
188+
// non-local items are skipped because they can be out of the users control,
189+
// especially in the case of trait impls, which rustdoc eagerly inlines
190+
return;
191+
}
192+
187193
match *i.kind {
188-
_ if !i.def_id.is_local() => {
189-
// non-local items are skipped because they can be out of the users control,
190-
// especially in the case of trait impls, which rustdoc eagerly inlines
191-
return Some(i);
192-
}
193194
clean::StrippedItem(..) => {
194195
// don't count items in stripped modules
195-
return Some(i);
196+
return;
196197
}
197198
// docs on `use` and `extern crate` statements are not displayed, so they're not
198199
// worth counting
@@ -269,6 +270,6 @@ impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
269270
}
270271
}
271272

272-
Some(self.fold_item_recur(i))
273+
self.visit_item_recur(i)
273274
}
274275
}

src/librustdoc/passes/check_code_block_syntax.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,9 @@ use rustc_span::{hygiene::AstPass, ExpnData, ExpnKind, FileName, InnerSpan, DUMM
88

99
use crate::clean;
1010
use crate::core::DocContext;
11-
use crate::fold::DocFolder;
1211
use crate::html::markdown::{self, RustCodeBlock};
1312
use crate::passes::Pass;
13+
use crate::visit::DocVisitor;
1414

1515
crate const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
1616
name: "check-code-block-syntax",
@@ -19,7 +19,8 @@ crate const CHECK_CODE_BLOCK_SYNTAX: Pass = Pass {
1919
};
2020

2121
crate fn check_code_block_syntax(krate: clean::Crate, cx: &mut DocContext<'_>) -> clean::Crate {
22-
SyntaxChecker { cx }.fold_crate(krate)
22+
SyntaxChecker { cx }.visit_crate(&krate);
23+
krate
2324
}
2425

2526
struct SyntaxChecker<'a, 'tcx> {
@@ -141,8 +142,8 @@ impl<'a, 'tcx> SyntaxChecker<'a, 'tcx> {
141142
}
142143
}
143144

144-
impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> {
145-
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
145+
impl<'a, 'tcx> DocVisitor for SyntaxChecker<'a, 'tcx> {
146+
fn visit_item(&mut self, item: &clean::Item) {
146147
if let Some(dox) = &item.attrs.collapsed_doc_value() {
147148
let sp = item.attr_span(self.cx.tcx);
148149
let extra = crate::html::markdown::ExtraInfo::new_did(
@@ -155,7 +156,7 @@ impl<'a, 'tcx> DocFolder for SyntaxChecker<'a, 'tcx> {
155156
}
156157
}
157158

158-
Some(self.fold_item_recur(item))
159+
self.visit_item_recur(item)
159160
}
160161
}
161162

0 commit comments

Comments
 (0)