Skip to content

Commit 4fa95b3

Browse files
committed
Calculate span info on-demand instead of ahead of time
This should *vastly* reduce memory usage.
1 parent af6aa9f commit 4fa95b3

File tree

7 files changed

+69
-69
lines changed

7 files changed

+69
-69
lines changed

src/librustdoc/clean/mod.rs

+3-21
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ use rustc_middle::ty::{self, AdtKind, Lift, Ty, TyCtxt};
2525
use rustc_mir::const_eval::{is_const_fn, is_min_const_fn, is_unstable_const_fn};
2626
use rustc_span::hygiene::{AstPass, MacroKind};
2727
use rustc_span::symbol::{kw, sym, Ident, Symbol};
28-
use rustc_span::{self, ExpnKind, Pos};
28+
use rustc_span::{self, ExpnKind};
2929
use rustc_typeck::hir_ty_to_ty;
3030

3131
use std::collections::hash_map::Entry;
@@ -1881,29 +1881,11 @@ impl Clean<VariantKind> for hir::VariantData<'_> {
18811881
}
18821882

18831883
impl Clean<Span> for rustc_span::Span {
1884-
fn clean(&self, cx: &DocContext<'_>) -> Span {
1885-
if self.is_dummy() {
1886-
return Span::empty();
1887-
}
1888-
1884+
fn clean(&self, _cx: &DocContext<'_>) -> Span {
18891885
// Get the macro invocation instead of the definition,
18901886
// in case the span is result of a macro expansion.
18911887
// (See rust-lang/rust#39726)
1892-
let span = self.source_callsite();
1893-
1894-
let sm = cx.sess().source_map();
1895-
let filename = sm.span_to_filename(span);
1896-
let lo = sm.lookup_char_pos(span.lo());
1897-
let hi = sm.lookup_char_pos(span.hi());
1898-
Span {
1899-
filename,
1900-
cnum: lo.file.cnum,
1901-
loline: lo.line,
1902-
locol: lo.col.to_usize(),
1903-
hiline: hi.line,
1904-
hicol: hi.col.to_usize(),
1905-
original: span,
1906-
}
1888+
Span { original: self.source_callsite() }
19071889
}
19081890
}
19091891

src/librustdoc/clean/types.rs

+23-18
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,16 @@ use rustc_data_structures::fx::{FxHashMap, FxHashSet};
1717
use rustc_feature::UnstableFeatures;
1818
use rustc_hir as hir;
1919
use rustc_hir::def::Res;
20-
use rustc_hir::def_id::{CrateNum, DefId, LOCAL_CRATE};
20+
use rustc_hir::def_id::{CrateNum, DefId};
2121
use rustc_hir::lang_items::LangItem;
2222
use rustc_hir::Mutability;
2323
use rustc_index::vec::IndexVec;
2424
use rustc_middle::ty::{AssocKind, TyCtxt};
25+
use rustc_session::Session;
2526
use rustc_span::hygiene::MacroKind;
2627
use rustc_span::source_map::DUMMY_SP;
2728
use rustc_span::symbol::{kw, sym, Ident, Symbol, SymbolStr};
28-
use rustc_span::{self, FileName};
29+
use rustc_span::{self, FileName, Loc};
2930
use rustc_target::abi::VariantIdx;
3031
use rustc_target::spec::abi::Abi;
3132
use smallvec::{smallvec, SmallVec};
@@ -1609,33 +1610,37 @@ crate enum VariantKind {
16091610
Struct(VariantStruct),
16101611
}
16111612

1613+
/// Small wrapper around `rustc_span::Span` that adds helper methods.
16121614
#[derive(Clone, Debug)]
16131615
crate struct Span {
1614-
crate filename: FileName,
1615-
crate cnum: CrateNum,
1616-
crate loline: usize,
1617-
crate locol: usize,
1618-
crate hiline: usize,
1619-
crate hicol: usize,
16201616
crate original: rustc_span::Span,
16211617
}
16221618

16231619
impl Span {
1624-
crate fn empty() -> Span {
1625-
Span {
1626-
filename: FileName::Anon(0),
1627-
cnum: LOCAL_CRATE,
1628-
loline: 0,
1629-
locol: 0,
1630-
hiline: 0,
1631-
hicol: 0,
1632-
original: rustc_span::DUMMY_SP,
1633-
}
1620+
crate fn empty() -> Self {
1621+
Self { original: rustc_span::DUMMY_SP }
16341622
}
16351623

16361624
crate fn span(&self) -> rustc_span::Span {
16371625
self.original
16381626
}
1627+
1628+
crate fn filename(&self, sess: &Session) -> FileName {
1629+
sess.source_map().span_to_filename(self.original)
1630+
}
1631+
1632+
crate fn lo(&self, sess: &Session) -> Loc {
1633+
sess.source_map().lookup_char_pos(self.original.lo())
1634+
}
1635+
1636+
crate fn hi(&self, sess: &Session) -> Loc {
1637+
sess.source_map().lookup_char_pos(self.original.hi())
1638+
}
1639+
1640+
crate fn cnum(&self, sess: &Session) -> CrateNum {
1641+
// FIXME: is there a time when the lo and hi crate would be different?
1642+
self.lo(sess).file.cnum
1643+
}
16391644
}
16401645

16411646
#[derive(Clone, PartialEq, Eq, Debug, Hash)]

src/librustdoc/html/render/mod.rs

+14-11
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,6 @@ crate fn ensure_trailing_slash(v: &str) -> impl fmt::Display + '_ {
103103
/// rustdoc tree).
104104
#[derive(Clone)]
105105
crate struct Context {
106-
crate sess: Lrc<Session>,
107106
/// Current hierarchy of components leading down to what's currently being
108107
/// rendered
109108
crate current: Vec<String>,
@@ -124,6 +123,7 @@ crate struct Context {
124123
}
125124

126125
crate struct SharedContext {
126+
crate sess: Lrc<Session>,
127127
/// The path to the crate root source minus the file name.
128128
/// Used for simplifying paths to the highlighted source code files.
129129
crate src_root: PathBuf,
@@ -176,6 +176,10 @@ impl Context {
176176
let filename = format!("{}{}.{}", base, self.shared.resource_suffix, ext,);
177177
self.dst.join(&filename)
178178
}
179+
180+
fn sess(&self) -> &Session {
181+
&self.shared.sess
182+
}
179183
}
180184

181185
impl SharedContext {
@@ -459,6 +463,7 @@ impl FormatRenderer for Context {
459463
}
460464
let (sender, receiver) = channel();
461465
let mut scx = SharedContext {
466+
sess,
462467
collapsed: krate.collapsed,
463468
src_root,
464469
include_sources,
@@ -498,7 +503,6 @@ impl FormatRenderer for Context {
498503

499504
let cache = Arc::new(cache);
500505
let mut cx = Context {
501-
sess,
502506
current: Vec::new(),
503507
dst,
504508
render_redirect_pages: false,
@@ -1636,24 +1640,24 @@ impl Context {
16361640
/// of their crate documentation isn't known.
16371641
fn src_href(&self, item: &clean::Item, cache: &Cache) -> Option<String> {
16381642
let mut root = self.root_path();
1639-
16401643
let mut path = String::new();
1644+
let cnum = item.source.cnum(self.sess());
16411645

16421646
// We can safely ignore synthetic `SourceFile`s.
1643-
let file = match item.source.filename {
1647+
let file = match item.source.filename(self.sess()) {
16441648
FileName::Real(ref path) => path.local_path().to_path_buf(),
16451649
_ => return None,
16461650
};
16471651
let file = &file;
16481652

1649-
let (krate, path) = if item.source.cnum == LOCAL_CRATE {
1653+
let (krate, path) = if cnum == LOCAL_CRATE {
16501654
if let Some(path) = self.shared.local_sources.get(file) {
16511655
(&self.shared.layout.krate, path)
16521656
} else {
16531657
return None;
16541658
}
16551659
} else {
1656-
let (krate, src_root) = match *cache.extern_locations.get(&item.source.cnum)? {
1660+
let (krate, src_root) = match *cache.extern_locations.get(&cnum)? {
16571661
(ref name, ref src, ExternalLocation::Local) => (name, src),
16581662
(ref name, ref src, ExternalLocation::Remote(ref s)) => {
16591663
root = s.to_string();
@@ -1672,11 +1676,10 @@ impl Context {
16721676
(krate, &path)
16731677
};
16741678

1675-
let lines = if item.source.loline == item.source.hiline {
1676-
item.source.loline.to_string()
1677-
} else {
1678-
format!("{}-{}", item.source.loline, item.source.hiline)
1679-
};
1679+
let loline = item.source.lo(self.sess()).line;
1680+
let hiline = item.source.hi(self.sess()).line;
1681+
let lines =
1682+
if loline == hiline { loline.to_string() } else { format!("{}-{}", loline, hiline) };
16801683
Some(format!(
16811684
"{root}src/{krate}/{path}#{lines}",
16821685
root = Escape(&root),

src/librustdoc/html/sources.rs

+13-4
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use crate::html::highlight;
77
use crate::html::layout;
88
use crate::html::render::{SharedContext, BASIC_KEYWORDS};
99
use rustc_hir::def_id::LOCAL_CRATE;
10+
use rustc_session::Session;
1011
use rustc_span::source_map::FileName;
1112
use std::ffi::OsStr;
1213
use std::fs;
@@ -34,37 +35,45 @@ struct SourceCollector<'a> {
3435

3536
impl<'a> DocFolder for SourceCollector<'a> {
3637
fn fold_item(&mut self, item: clean::Item) -> Option<clean::Item> {
38+
// If we're not rendering sources, there's nothing to do.
3739
// If we're including source files, and we haven't seen this file yet,
3840
// then we need to render it out to the filesystem.
3941
if self.scx.include_sources
4042
// skip all synthetic "files"
41-
&& item.source.filename.is_real()
43+
&& item.source.filename(self.sess()).is_real()
4244
// skip non-local files
43-
&& item.source.cnum == LOCAL_CRATE
45+
&& item.source.cnum(self.sess()) == LOCAL_CRATE
4446
{
47+
let filename = item.source.filename(self.sess());
4548
// If it turns out that we couldn't read this file, then we probably
4649
// can't read any of the files (generating html output from json or
4750
// something like that), so just don't include sources for the
4851
// entire crate. The other option is maintaining this mapping on a
4952
// per-file basis, but that's probably not worth it...
50-
self.scx.include_sources = match self.emit_source(&item.source.filename) {
53+
self.scx.include_sources = match self.emit_source(&filename) {
5154
Ok(()) => true,
5255
Err(e) => {
5356
println!(
5457
"warning: source code was requested to be rendered, \
5558
but processing `{}` had an error: {}",
56-
item.source.filename, e
59+
filename, e
5760
);
5861
println!(" skipping rendering of source code");
5962
false
6063
}
6164
};
6265
}
66+
// FIXME: if `include_sources` isn't set and DocFolder didn't require consuming the crate by value,
67+
// we could return None here without having to walk the rest of the crate.
6368
Some(self.fold_item_recur(item))
6469
}
6570
}
6671

6772
impl<'a> SourceCollector<'a> {
73+
fn sess(&self) -> &Session {
74+
&self.scx.sess
75+
}
76+
6877
/// Renders the given filename into its corresponding HTML source file.
6978
fn emit_source(&mut self, filename: &FileName) -> Result<(), Error> {
7079
let p = match *filename {

src/librustdoc/json/conversions.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,21 @@ impl From<clean::Item> for Option<Item> {
5656
}
5757

5858
impl From<clean::Span> for Option<Span> {
59+
#[allow(unreachable_code)]
5960
fn from(span: clean::Span) -> Self {
60-
let clean::Span { loline, locol, hiline, hicol, .. } = span;
61-
match span.filename {
61+
// TODO: this should actually work
62+
// Unfortunately this requires rethinking the whole framework,
63+
// since this now needs a context and not just .into().
64+
match span.filename(todo!()) {
6265
rustc_span::FileName::Real(name) => Some(Span {
6366
filename: match name {
6467
rustc_span::RealFileName::Named(path) => path,
6568
rustc_span::RealFileName::Devirtualized { local_path, virtual_name: _ } => {
6669
local_path
6770
}
6871
},
69-
begin: (loline, locol),
70-
end: (hiline, hicol),
72+
begin: todo!(),
73+
end: todo!(),
7174
}),
7275
_ => None,
7376
}

src/librustdoc/lib.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -555,13 +555,14 @@ fn main_options(options: config::Options) -> MainResult {
555555
info!("going to format");
556556
let (error_format, edition, debugging_options) = diag_opts;
557557
let diag = core::new_handler(error_format, None, &debugging_options);
558+
let sess_time = sess.clone();
558559
match output_format {
559-
None | Some(config::OutputFormat::Html) => sess.time("render_html", || {
560+
None | Some(config::OutputFormat::Html) => sess_time.time("render_html", || {
560561
run_renderer::<html::render::Context>(
561562
krate, renderopts, renderinfo, &diag, edition, sess,
562563
)
563564
}),
564-
Some(config::OutputFormat::Json) => sess.time("render_json", || {
565+
Some(config::OutputFormat::Json) => sess_time.time("render_json", || {
565566
run_renderer::<json::JsonRenderer>(krate, renderopts, renderinfo, &diag, edition, sess)
566567
}),
567568
}

src/librustdoc/passes/calculate_doc_coverage.rs

+6-9
Original file line numberDiff line numberDiff line change
@@ -216,13 +216,9 @@ impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
216216
return Some(i);
217217
}
218218
clean::ImplItem(ref impl_) => {
219+
let filename = i.source.filename(self.ctx.sess());
219220
if let Some(ref tr) = impl_.trait_ {
220-
debug!(
221-
"impl {:#} for {:#} in {}",
222-
tr.print(),
223-
impl_.for_.print(),
224-
i.source.filename
225-
);
221+
debug!("impl {:#} for {:#} in {}", tr.print(), impl_.for_.print(), filename,);
226222

227223
// don't count trait impls, the missing-docs lint doesn't so we shouldn't
228224
// either
@@ -231,7 +227,7 @@ impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
231227
// inherent impls *can* be documented, and those docs show up, but in most
232228
// cases it doesn't make sense, as all methods on a type are in one single
233229
// impl block
234-
debug!("impl {:#} in {}", impl_.for_.print(), i.source.filename);
230+
debug!("impl {:#} in {}", impl_.for_.print(), filename);
235231
}
236232
}
237233
_ => {
@@ -251,15 +247,16 @@ impl<'a, 'b> fold::DocFolder for CoverageCalculator<'a, 'b> {
251247
None,
252248
);
253249

250+
let filename = i.source.filename(self.ctx.sess());
254251
let has_doc_example = tests.found_tests != 0;
255252
let hir_id = self.ctx.tcx.hir().local_def_id_to_hir_id(i.def_id.expect_local());
256253
let (level, source) = self.ctx.tcx.lint_level_at_node(MISSING_DOCS, hir_id);
257254
// `missing_docs` is allow-by-default, so don't treat this as ignoring the item
258255
// unless the user had an explicit `allow`
259256
let should_have_docs =
260257
level != lint::Level::Allow || matches!(source, LintSource::Default);
261-
debug!("counting {:?} {:?} in {}", i.type_(), i.name, i.source.filename);
262-
self.items.entry(i.source.filename.clone()).or_default().count_item(
258+
debug!("counting {:?} {:?} in {}", i.type_(), i.name, filename);
259+
self.items.entry(filename).or_default().count_item(
263260
has_docs,
264261
has_doc_example,
265262
should_have_doc_example(self.ctx, &i),

0 commit comments

Comments
 (0)