-
Notifications
You must be signed in to change notification settings - Fork 13.4k
rustdoc: Add DocVisitor
and use it where possible
#90475
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
537b82c
705dec1
5a9bbba
28bdf89
6215f7c
4ee2d03
10606c3
b742870
3f0f510
c03cab3
8e4bcdf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,11 +1,11 @@ | ||
use crate::clean; | ||
use crate::docfs::PathError; | ||
use crate::error::Error; | ||
use crate::fold::DocFolder; | ||
use crate::html::format::Buffer; | ||
use crate::html::highlight; | ||
use crate::html::layout; | ||
use crate::html::render::{Context, BASIC_KEYWORDS}; | ||
use crate::visit::DocVisitor; | ||
use rustc_data_structures::fx::{FxHashMap, FxHashSet}; | ||
use rustc_hir::def_id::LOCAL_CRATE; | ||
use rustc_middle::ty::TyCtxt; | ||
|
@@ -16,23 +16,25 @@ use std::ffi::OsStr; | |
use std::fs; | ||
use std::path::{Component, Path, PathBuf}; | ||
|
||
crate fn render(cx: &mut Context<'_>, krate: clean::Crate) -> Result<clean::Crate, Error> { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 👍 That's way cleaner. I like this change; the other comments really are just nitpicking, and the PR could easily be merged as-is. |
||
crate fn render(cx: &mut Context<'_>, krate: &clean::Crate) -> Result<(), Error> { | ||
info!("emitting source files"); | ||
|
||
let dst = cx.dst.join("src").join(&*krate.name(cx.tcx()).as_str()); | ||
cx.shared.ensure_dir(&dst)?; | ||
let mut folder = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() }; | ||
Ok(folder.fold_crate(krate)) | ||
|
||
let mut collector = SourceCollector { dst, cx, emitted_local_sources: FxHashSet::default() }; | ||
collector.visit_crate(krate); | ||
Ok(()) | ||
} | ||
|
||
crate fn collect_local_sources<'tcx>( | ||
tcx: TyCtxt<'tcx>, | ||
src_root: &Path, | ||
krate: clean::Crate, | ||
) -> (clean::Crate, FxHashMap<PathBuf, String>) { | ||
krate: &clean::Crate, | ||
) -> FxHashMap<PathBuf, String> { | ||
let mut lsc = LocalSourcesCollector { tcx, local_sources: FxHashMap::default(), src_root }; | ||
|
||
let krate = lsc.fold_crate(krate); | ||
(krate, lsc.local_sources) | ||
lsc.visit_crate(krate); | ||
lsc.local_sources | ||
} | ||
|
||
struct LocalSourcesCollector<'a, 'tcx> { | ||
|
@@ -42,7 +44,7 @@ struct LocalSourcesCollector<'a, 'tcx> { | |
} | ||
|
||
fn is_real_and_local(span: clean::Span, sess: &Session) -> bool { | ||
span.filename(sess).is_real() && span.cnum(sess) == LOCAL_CRATE | ||
span.cnum(sess) == LOCAL_CRATE && span.filename(sess).is_real() | ||
} | ||
|
||
impl LocalSourcesCollector<'_, '_> { | ||
|
@@ -54,12 +56,13 @@ impl LocalSourcesCollector<'_, '_> { | |
return; | ||
} | ||
let filename = span.filename(sess); | ||
let p = match filename { | ||
FileName::Real(ref file) => match file.local_path() { | ||
Some(p) => p.to_path_buf(), | ||
_ => return, | ||
}, | ||
_ => return, | ||
let p = if let FileName::Real(file) = filename { | ||
match file.into_local_path() { | ||
Some(p) => p, | ||
None => return, | ||
} | ||
} else { | ||
return; | ||
}; | ||
if self.local_sources.contains_key(&*p) { | ||
// We've already emitted this source | ||
|
@@ -79,13 +82,11 @@ impl LocalSourcesCollector<'_, '_> { | |
} | ||
} | ||
|
||
impl DocFolder for LocalSourcesCollector<'_, '_> { | ||
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { | ||
self.add_local_source(&item); | ||
impl DocVisitor for LocalSourcesCollector<'_, '_> { | ||
fn visit_item(&mut self, item: &clean::Item) { | ||
self.add_local_source(item); | ||
|
||
// FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value, | ||
// we could return None here without having to walk the rest of the crate. | ||
Some(self.fold_item_recur(item)) | ||
self.visit_item_recur(item) | ||
} | ||
} | ||
|
||
|
@@ -98,16 +99,20 @@ struct SourceCollector<'a, 'tcx> { | |
emitted_local_sources: FxHashSet<PathBuf>, | ||
} | ||
|
||
impl DocFolder for SourceCollector<'_, '_> { | ||
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> { | ||
impl DocVisitor for SourceCollector<'_, '_> { | ||
fn visit_item(&mut self, item: &clean::Item) { | ||
if !self.cx.include_sources { | ||
return; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This line of code cries out for a more explicit way to do control flow than this. This conditional seems to stop it from trying to recurse into child nodes, but it can't stop the parent from trying to visit all of the siblings (and that seems wasteful), and I really only figured that out by carefully reading the implementation. The API used by There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I'd be happy to try out switching |
||
} | ||
|
||
let tcx = self.cx.tcx(); | ||
let span = item.span(tcx); | ||
let sess = tcx.sess; | ||
|
||
// If we're not rendering sources, there's nothing to do. | ||
// If we're including source files, and we haven't seen this file yet, | ||
// then we need to render it out to the filesystem. | ||
if self.cx.include_sources && is_real_and_local(span, sess) { | ||
if is_real_and_local(span, sess) { | ||
let filename = span.filename(sess); | ||
let span = span.inner(); | ||
let pos = sess.source_map().lookup_source_file(span.lo()); | ||
|
@@ -132,9 +137,8 @@ impl DocFolder for SourceCollector<'_, '_> { | |
} | ||
}; | ||
} | ||
// FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value, | ||
// we could return None here without having to walk the rest of the crate. | ||
Some(self.fold_item_recur(item)) | ||
|
||
self.visit_item_recur(item) | ||
} | ||
} | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I know it was done this way before, but any idea why it isn't using a conditional, or a short-circuiting OR, for this?
The length comparison probably isn't a problem, but unconditionally iterating over all of the fields seems like leaving performance on the table (or gambling on a compiler optimization pass that may or may not fire).
From a dive through Blame, this use of the
|=
operator dates back to a version of the code that was only doing the length comparison:d6d31d7#diff-3ba434e74829d360308543dff0f880edbd86bdc9fd90f025d990d91319b7002cR82
The code that started doing the
any()
loop doesn't have any documentation that justifies why it isn't a perf problem, either. It looks like they just added theany()
loop without reconsidering the use of a non-short-circuiting OR.b1543a1#diff-3ba434e74829d360308543dff0f880edbd86bdc9fd90f025d990d91319b7002cR48-R49
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Probably the reason it doesn't use a conditional is that no one thought of it ;)
It's a good idea; do you want to open a followup PR for it? Honestly, I think we should just remove the
fields_stripped
fields altogether and compute this information on-demand, but that'll take some time.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, I'll do that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You can assign me on it if you like.