Skip to content

Commit 8df0dbc

Browse files
committed
Preemptively fetch the typeck results when linting bodies.
1 parent d62fb1c commit 8df0dbc

4 files changed

Lines changed: 66 additions & 44 deletions

File tree

compiler/rustc_lint/src/context.rs

Lines changed: 4 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,6 @@
33
//! See <https://rustc-dev-guide.rust-lang.org/diagnostics.html> for an
44
//! overview of how lints are implemented.
55
6-
use std::cell::Cell;
76
use std::slice;
87

98
use rustc_abi as abi;
@@ -484,11 +483,8 @@ pub struct LateContext<'tcx> {
484483
/// Current body, or `None` if outside a body.
485484
pub enclosing_body: Option<hir::BodyId>,
486485

487-
/// Type-checking results for the current body. Access using the `typeck_results`
488-
/// and `maybe_typeck_results` methods, which handle querying the typeck results on demand.
489-
// FIXME(eddyb) move all the code accessing internal fields like this,
490-
// to this module, to avoid exposing it to lint logic.
491-
pub(super) cached_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
486+
/// Type-checking results for the current body.
487+
pub typeck_results: Option<&'tcx ty::TypeckResults<'tcx>>,
492488

493489
/// Parameter environment for the item we are in.
494490
pub param_env: ty::ParamEnv<'tcx>,
@@ -666,21 +662,15 @@ impl<'tcx> LateContext<'tcx> {
666662
/// Gets the type-checking results for the current body,
667663
/// or `None` if outside a body.
668664
pub fn maybe_typeck_results(&self) -> Option<&'tcx ty::TypeckResults<'tcx>> {
669-
self.cached_typeck_results.get().or_else(|| {
670-
self.enclosing_body.map(|body| {
671-
let typeck_results = self.tcx.typeck_body(body);
672-
self.cached_typeck_results.set(Some(typeck_results));
673-
typeck_results
674-
})
675-
})
665+
self.typeck_results
676666
}
677667

678668
/// Gets the type-checking results for the current body.
679669
/// As this will ICE if called outside bodies, only call when working with
680670
/// `Expr` or `Pat` nodes (they are guaranteed to be found only in bodies).
681671
#[track_caller]
682672
pub fn typeck_results(&self) -> &'tcx ty::TypeckResults<'tcx> {
683-
self.maybe_typeck_results().expect("`LateContext::typeck_results` called outside of body")
673+
self.typeck_results.expect("`LateContext::typeck_results` called outside of body")
684674
}
685675

686676
/// Returns the final resolution of a `QPath`, or `Res::Err` if unavailable.

compiler/rustc_lint/src/late.rs

Lines changed: 59 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@
44
//! borrow checking, etc.). These lints have full type information available.
55
66
use std::any::Any;
7-
use std::cell::Cell;
87

98
use rustc_data_structures::stack::ensure_sufficient_stack;
109
use rustc_data_structures::sync::par_join;
@@ -17,6 +16,7 @@ use rustc_session::lint::LintPass;
1716
use rustc_span::Span;
1817
use tracing::debug;
1918

19+
use crate::builtin::MissingDoc;
2020
use crate::passes::LateLintPassObject;
2121
use crate::{LateContext, LateLintPass, LintStore, is_lint_pass_required};
2222

@@ -34,12 +34,16 @@ macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
3434

3535
/// Implements the AST traversal for late lint passes. `T` provides the
3636
/// `check_*` methods.
37-
struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> {
37+
///
38+
/// `TYPECK_BODY` determines whether we fetch typeck results while walking the HIR tree.
39+
/// When set to `false` we do not ever assign to the `typeck_results` field during the
40+
/// tree walk. See [`lint_missing_docs`] for why this exists.
41+
struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>, const TYPECK_BODY: bool> {
3842
context: LateContext<'tcx>,
3943
pass: T,
4044
}
4145

42-
impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
46+
impl<'tcx, T: LateLintPass<'tcx>, const TYPECK_BODY: bool> LateContextAndPass<'tcx, T, TYPECK_BODY> {
4347
/// Merge the lints specified by any lint attributes into the
4448
/// current lint context, call the provided function, then reset the
4549
/// lints in effect to their previous state.
@@ -77,7 +81,9 @@ impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
7781
}
7882
}
7983

80-
impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> {
84+
impl<'tcx, T: LateLintPass<'tcx>, const TYPECK_BODY: bool> hir_visit::Visitor<'tcx>
85+
for LateContextAndPass<'tcx, T, TYPECK_BODY>
86+
{
8187
type NestedFilter = nested_filter::All;
8288

8389
/// Because lints are scoped lexically, we want to walk nested
@@ -89,22 +95,19 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
8995

9096
fn visit_nested_body(&mut self, body_id: hir::BodyId) {
9197
let old_enclosing_body = self.context.enclosing_body.replace(body_id);
92-
let old_cached_typeck_results = self.context.cached_typeck_results.get();
98+
let old_typeck_results = self.context.typeck_results;
9399

94-
// HACK(eddyb) avoid trashing `cached_typeck_results` when we're
95-
// nested in `visit_fn`, which may have already resulted in them
96-
// being queried.
97-
if old_enclosing_body != Some(body_id) {
98-
self.context.cached_typeck_results.set(None);
100+
// The body and typeck results are also set in `visit_fn`.
101+
// Only fetch the results if this is for a new body.
102+
if TYPECK_BODY && old_enclosing_body != Some(body_id) {
103+
self.context.typeck_results = Some(self.context.tcx.typeck_body(body_id));
99104
}
100105

101106
let body = self.context.tcx.hir_body(body_id);
102107
self.visit_body(body);
103108
self.context.enclosing_body = old_enclosing_body;
104-
105-
// See HACK comment above.
106-
if old_enclosing_body != Some(body_id) {
107-
self.context.cached_typeck_results.set(old_cached_typeck_results);
109+
if TYPECK_BODY {
110+
self.context.typeck_results = old_typeck_results;
108111
}
109112
}
110113

@@ -123,7 +126,10 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
123126
fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) {
124127
let generics = self.context.generics.take();
125128
self.context.generics = it.kind.generics();
126-
let old_cached_typeck_results = self.context.cached_typeck_results.take();
129+
let old_typeck_results = self.context.typeck_results;
130+
if TYPECK_BODY {
131+
self.context.typeck_results = None;
132+
}
127133
let old_enclosing_body = self.context.enclosing_body.take();
128134
self.with_lint_attrs(it.hir_id(), |cx| {
129135
cx.with_param_env(it.owner_id, |cx| {
@@ -133,7 +139,9 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
133139
});
134140
});
135141
self.context.enclosing_body = old_enclosing_body;
136-
self.context.cached_typeck_results.set(old_cached_typeck_results);
142+
if TYPECK_BODY {
143+
self.context.typeck_results = old_typeck_results;
144+
}
137145
self.context.generics = generics;
138146
}
139147

@@ -189,12 +197,17 @@ impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPas
189197
// Wrap in typeck results here, not just in visit_nested_body,
190198
// in order for `check_fn` to be able to use them.
191199
let old_enclosing_body = self.context.enclosing_body.replace(body_id);
192-
let old_cached_typeck_results = self.context.cached_typeck_results.take();
200+
let old_typeck_results = self.context.typeck_results;
201+
if TYPECK_BODY {
202+
self.context.typeck_results = Some(self.context.tcx.typeck_body(body_id));
203+
}
193204
let body = self.context.tcx.hir_body(body_id);
194205
lint_callback!(self, check_fn, fk, decl, body, span, id);
195206
hir_visit::walk_fn(self, fk, decl, body_id, id);
196207
self.context.enclosing_body = old_enclosing_body;
197-
self.context.cached_typeck_results.set(old_cached_typeck_results);
208+
if TYPECK_BODY {
209+
self.context.typeck_results = old_typeck_results;
210+
}
198211
}
199212

200213
fn visit_variant_data(&mut self, s: &'tcx hir::VariantData<'tcx>) {
@@ -333,6 +346,27 @@ macro_rules! impl_late_lint_pass {
333346

334347
crate::late_lint_methods!(impl_late_lint_pass, []);
335348

349+
/// Runs only the `MissingDoc` lint pass without type checking bodies.
350+
///
351+
/// **DO NOT** use this for anything other than rustdoc. This exists solely to workaround
352+
/// the fact that rustdoc parses functions which would not pass type checking. See:
353+
/// <https://github.com/rust-lang/rust/pull/73566>.
354+
pub fn lint_missing_docs<'tcx>(tcx: TyCtxt<'tcx>, mod_id: LocalModId) {
355+
if is_lint_pass_required(tcx.skippable_lints(()), &MissingDoc.get_lints()) {
356+
let context = LateContext {
357+
tcx,
358+
enclosing_body: None,
359+
typeck_results: None,
360+
param_env: ty::ParamEnv::empty(),
361+
effective_visibilities: tcx.effective_visibilities(()),
362+
last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(mod_id),
363+
generics: None,
364+
only_module: true,
365+
};
366+
late_lint_mod_inner::<'_, _, false>(tcx, mod_id, context, MissingDoc);
367+
}
368+
}
369+
336370
pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
337371
tcx: TyCtxt<'tcx>,
338372
mod_id: LocalModId,
@@ -341,7 +375,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
341375
let context = LateContext {
342376
tcx,
343377
enclosing_body: None,
344-
cached_typeck_results: Cell::new(None),
378+
typeck_results: None,
345379
param_env: ty::ParamEnv::empty(),
346380
effective_visibilities: tcx.effective_visibilities(()),
347381
last_node_with_lint_attrs: tcx.local_def_id_to_hir_id(mod_id),
@@ -363,24 +397,24 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
363397
let builtin_lints_must_run = is_lint_pass_required(skippable_lints, &builtin_lints.get_lints());
364398
if passes.is_empty() {
365399
if builtin_lints_must_run {
366-
late_lint_mod_inner(tcx, mod_id, context, builtin_lints);
400+
late_lint_mod_inner::<'_, _, true>(tcx, mod_id, context, builtin_lints);
367401
}
368402
} else {
369403
if builtin_lints_must_run {
370404
passes.push(Box::new(builtin_lints) as Box<dyn LateLintPass<'tcx>>);
371405
}
372406
let pass = RuntimeCombinedLateLintPass { passes };
373-
late_lint_mod_inner(tcx, mod_id, context, pass);
407+
late_lint_mod_inner::<'_, _, true>(tcx, mod_id, context, pass);
374408
}
375409
}
376410

377-
fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>(
411+
fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>, const TYPECK_BODY: bool>(
378412
tcx: TyCtxt<'tcx>,
379413
mod_id: LocalModId,
380414
context: LateContext<'tcx>,
381415
pass: T,
382416
) {
383-
let mut cx = LateContextAndPass { context, pass };
417+
let mut cx = LateContextAndPass::<'tcx, T, TYPECK_BODY> { context, pass };
384418

385419
let (module, _span, hir_id) = tcx.hir_get_module(mod_id);
386420

@@ -415,7 +449,7 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
415449
let context = LateContext {
416450
tcx,
417451
enclosing_body: None,
418-
cached_typeck_results: Cell::new(None),
452+
typeck_results: None,
419453
param_env: ty::ParamEnv::empty(),
420454
effective_visibilities: tcx.effective_visibilities(()),
421455
last_node_with_lint_attrs: hir::CRATE_HIR_ID,
@@ -424,7 +458,7 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) {
424458
};
425459

426460
let pass = RuntimeCombinedLateLintPass { passes };
427-
let mut cx = LateContextAndPass { context, pass };
461+
let mut cx = LateContextAndPass::<'_, _, true> { context, pass };
428462

429463
// Visit the whole crate.
430464
cx.with_lint_attrs(hir::CRATE_HIR_ID, |cx| {

compiler/rustc_lint/src/lib.rs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,11 +136,10 @@ use unused::must_use::*;
136136
use unused::*;
137137

138138
#[rustfmt::skip]
139-
pub use builtin::MissingDoc;
140139
pub use context::{CheckLintNameResult, EarlyContext, LateContext, LintContext, LintStore};
141140
pub use early::diagnostics::DiagAndSess;
142141
pub use early::{EarlyCheckNode, check_ast_node};
143-
pub use late::{check_crate, late_lint_mod, unerased_lint_store};
142+
pub use late::{check_crate, late_lint_mod, lint_missing_docs, unerased_lint_store};
144143
pub use levels::LintLevelsBuilder;
145144
pub use passes::{EarlyLintPass, LateLintPass};
146145
pub use rustc_errors::BufferedEarlyLint;

src/librustdoc/core.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ use rustc_hir::def::Res;
1414
use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId};
1515
use rustc_hir::intravisit::{self, Visitor};
1616
use rustc_hir::{HirId, Path};
17-
use rustc_lint::{MissingDoc, late_lint_mod};
17+
use rustc_lint::lint_missing_docs;
1818
use rustc_middle::hir::nested_filter;
1919
use rustc_middle::ty::{self, ParamEnv, Ty, TyCtxt};
2020
use rustc_session::config::{
@@ -316,8 +316,7 @@ pub(crate) fn create_config(
316316
override_queries: Some(|_sess, providers| {
317317
// We do not register late module lints, so this only runs `MissingDoc`.
318318
// Most lints will require typechecking, so just don't run them.
319-
providers.queries.lint_mod =
320-
|tcx, module_def_id| late_lint_mod(tcx, module_def_id, MissingDoc);
319+
providers.queries.lint_mod = |tcx, module_def_id| lint_missing_docs(tcx, module_def_id);
321320
// hack so that `used_trait_imports` won't try to call typeck
322321
providers.queries.used_trait_imports = |_, _| {
323322
static EMPTY_SET: LazyLock<UnordSet<LocalDefId>> = LazyLock::new(UnordSet::default);

0 commit comments

Comments
 (0)