Skip to content

Commit 69577e4

Browse files
committed
Auto merge of #156355 - fmease:rustdoc-ltd-lazy-cached-typck, r=<try>
[perf-only] rustdoc: Unconditionally enable `--generate-link-to-definition` and lazily typeck nested bodies with a cache
2 parents fb0a5a5 + 4da0f6c commit 69577e4

2 files changed

Lines changed: 48 additions & 42 deletions

File tree

src/librustdoc/config.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -824,7 +824,7 @@ impl Options {
824824
let generate_redirect_map = matches.opt_present("generate-redirect-map");
825825
let show_type_layout = matches.opt_present("show-type-layout");
826826
let no_capture = matches.opt_present("no-capture");
827-
let generate_link_to_definition = matches.opt_present("generate-link-to-definition");
827+
let generate_link_to_definition = true;
828828
let generate_macro_expansion = matches.opt_present("generate-macro-expansion");
829829
let extern_html_root_takes_precedence =
830830
matches.opt_present("extern-html-root-takes-precedence");

src/librustdoc/html/render/span_map.rs

Lines changed: 47 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,12 @@ use std::path::{Path, PathBuf};
22

33
use rustc_data_structures::fx::{FxHashMap, FxIndexMap};
44
use rustc_hir::def::{DefKind, Res};
5-
use rustc_hir::def_id::{DefId, LOCAL_CRATE, LocalDefId};
5+
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
66
use rustc_hir::intravisit::{self, Visitor, VisitorExt};
77
use rustc_hir::{ExprKind, HirId, Item, ItemKind, Mod, Node, QPath};
8+
use rustc_hir as hir;
89
use rustc_middle::hir::nested_filter;
9-
use rustc_middle::ty::TyCtxt;
10+
use rustc_middle::ty::{self, TyCtxt};
1011
use rustc_span::{BytePos, ExpnKind};
1112

1213
use crate::clean::{self, PrimitiveType, rustc_span};
@@ -82,7 +83,7 @@ pub(crate) fn collect_spans_and_sources(
8283
generate_link_to_definition: bool,
8384
) -> (FxIndexMap<PathBuf, String>, FxHashMap<Span, LinkFromSrc>) {
8485
if include_sources {
85-
let mut visitor = SpanMapVisitor { tcx, matches: FxHashMap::default() };
86+
let mut visitor = SpanMapVisitor { tcx, cached_typeck_results: None, matches: FxHashMap::default() };
8687

8788
if generate_link_to_definition {
8889
tcx.hir_walk_toplevel_module(&mut visitor);
@@ -96,12 +97,24 @@ pub(crate) fn collect_spans_and_sources(
9697

9798
struct SpanMapVisitor<'tcx> {
9899
pub(crate) tcx: TyCtxt<'tcx>,
100+
pub(crate) cached_typeck_results: Option<(hir::BodyId, Option<&'tcx ty::TypeckResults<'tcx>>)>,
99101
pub(crate) matches: FxHashMap<Span, LinkFromSrc>,
100102
}
101103

102-
impl SpanMapVisitor<'_> {
104+
impl<'tcx> SpanMapVisitor<'tcx> {
105+
fn maybe_typeck_results(&mut self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
106+
let (body_id, cached_typeck_results) = self.cached_typeck_results.as_mut()?;
107+
108+
//FIXME: Allegedly, we might actually typeck code here that's cfg'ed out
109+
// which could lead to errors getting reported which would be wild.
110+
// Re-audit this! Do we have any tests demonstrating this?
111+
// And certainly don't silence diagnostics in the compiler. Historical context:
112+
// <https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352>.
113+
Some(cached_typeck_results.get_or_insert_with(|| self.tcx.typeck_body(*body_id)))
114+
}
115+
103116
/// This function is where we handle `hir::Path` elements and add them into the "span map".
104-
fn handle_path(&mut self, path: &rustc_hir::Path<'_>, only_use_last_segment: bool) {
117+
fn handle_path(&mut self, path: &hir::Path<'_>, only_use_last_segment: bool) {
105118
match path.res {
106119
// FIXME: For now, we handle `DefKind` if it's not a `DefKind::TyParam`.
107120
// Would be nice to support them too alongside the other `DefKind`
@@ -217,19 +230,16 @@ impl SpanMapVisitor<'_> {
217230
true
218231
}
219232

233+
// FIXME: Find a better name for this? One that's more specific?
234+
// Or inline this function into all callers and create better helpers?
220235
fn infer_id(&mut self, hir_id: HirId, expr_hir_id: Option<HirId>, span: Span) {
221-
let tcx = self.tcx;
222-
let body_id = tcx.hir_enclosing_body_owner(hir_id);
223-
// FIXME: this is showing error messages for parts of the code that are not
224-
// compiled (because of cfg)!
225-
//
226-
// See discussion in https://github.com/rust-lang/rust/issues/69426#issuecomment-1019412352
227-
let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id());
236+
let Some(typeck_results) = self.maybe_typeck_results() else { return };
237+
228238
// Interestingly enough, for method calls, we need the whole expression whereas for static
229239
// method/function calls, we need the call expression specifically.
230240
if let Some(def_id) = typeck_results.type_dependent_def_id(expr_hir_id.unwrap_or(hir_id)) {
231241
let link = if def_id.as_local().is_some() {
232-
LinkFromSrc::Local(rustc_span(def_id, tcx))
242+
LinkFromSrc::Local(rustc_span(def_id, self.tcx))
233243
} else {
234244
LinkFromSrc::External(def_id)
235245
};
@@ -238,31 +248,20 @@ impl SpanMapVisitor<'_> {
238248
}
239249
}
240250

241-
// This is a reimplementation of `hir_enclosing_body_owner` which allows to fail without
242-
// panicking.
243-
fn hir_enclosing_body_owner(tcx: TyCtxt<'_>, hir_id: HirId) -> Option<LocalDefId> {
244-
for (_, node) in tcx.hir_parent_iter(hir_id) {
245-
// FIXME: associated type impl items don't have an associated body, so we don't handle
246-
// them currently.
247-
if let Node::ImplItem(impl_item) = node
248-
&& matches!(impl_item.kind, rustc_hir::ImplItemKind::Type(_))
249-
{
250-
return None;
251-
} else if let Some((def_id, _)) = node.associated_body() {
252-
return Some(def_id);
253-
}
254-
}
255-
None
256-
}
257-
258251
impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
259252
type NestedFilter = nested_filter::All;
260253

261254
fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt {
262255
self.tcx
263256
}
264257

265-
fn visit_path(&mut self, path: &rustc_hir::Path<'tcx>, _id: HirId) {
258+
fn visit_nested_body(&mut self, body_id: hir::BodyId) -> Self::Result {
259+
let cached_typeck_results = self.cached_typeck_results.replace((body_id, None));
260+
self.visit_body(self.tcx.hir_body(body_id));
261+
self.cached_typeck_results = cached_typeck_results;
262+
}
263+
264+
fn visit_path(&mut self, path: &hir::Path<'tcx>, _id: HirId) {
266265
if self.handle_macro(path.span) {
267266
return;
268267
}
@@ -272,25 +271,28 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
272271

273272
fn visit_qpath(&mut self, qpath: &QPath<'tcx>, id: HirId, _span: rustc_span::Span) {
274273
match *qpath {
275-
QPath::TypeRelative(qself, path) => {
276-
if matches!(path.res, Res::Err) {
277-
let tcx = self.tcx;
278-
if let Some(body_id) = hir_enclosing_body_owner(tcx, id) {
279-
let typeck_results = tcx.typeck_body(tcx.hir_body_owned_by(body_id).id());
280-
let path = rustc_hir::Path {
274+
QPath::TypeRelative(qself, segment) => {
275+
if let Res::Err = segment.res {
276+
if let Some(typeck_results) = self.maybe_typeck_results()
277+
&& typeck_results.hir_owner == id.owner
278+
{
279+
let path = hir::Path {
281280
// We change the span to not include parens.
282-
span: path.ident.span,
281+
span: segment.ident.span,
283282
res: typeck_results.qpath_res(qpath, id),
283+
// FIXME: Don't create a path with zero segments!
284+
// Can't we just pass `std::slice::from_ref(segment)` or
285+
// would that affect the behavior of `handle_path`?
284286
segments: &[],
285287
};
286288
self.handle_path(&path, false);
287289
}
288290
} else {
289-
self.infer_id(path.hir_id, Some(id), path.ident.span.into());
291+
self.infer_id(segment.hir_id, Some(id), segment.ident.span.into());
290292
}
291293

292294
rustc_ast::visit::try_visit!(self.visit_ty_unambig(qself));
293-
self.visit_path_segment(path);
295+
self.visit_path_segment(segment);
294296
}
295297
QPath::Resolved(maybe_qself, path) => {
296298
self.handle_path(path, true);
@@ -323,11 +325,15 @@ impl<'tcx> Visitor<'tcx> for SpanMapVisitor<'tcx> {
323325
intravisit::walk_mod(self, m);
324326
}
325327

326-
fn visit_expr(&mut self, expr: &'tcx rustc_hir::Expr<'tcx>) {
328+
fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
327329
match expr.kind {
328330
ExprKind::MethodCall(segment, ..) => {
329331
self.infer_id(segment.hir_id, Some(expr.hir_id), segment.ident.span.into())
330332
}
333+
// FIXME: Don't we register matches twice if `call` was a `TypeRelative` path?
334+
// I don't think we can just cease walking `call` since then generic args
335+
// inside the path won't get resolved I'm pretty sure.
336+
// Double-resolving probably doesn't matter though anyway wrt. perf.
331337
ExprKind::Call(call, ..) => self.infer_id(call.hir_id, None, call.span.into()),
332338
_ => {
333339
if self.handle_macro(expr.span) {

0 commit comments

Comments
 (0)