Skip to content

Commit 3c53781

Browse files
committed
Reinstate {Early,Late}LintPassObjects.
I removed these in #105291, and subsequently learned they are necessary for performance. This commit reinstates them with the new and more descriptive names `RuntimeCombined{Early,Late}LintPass`, similar to the existing passes like `BuiltinCombinedEarlyLintPass`. It also adds some comments, particularly emphasising how we have ways to combine passes at both compile-time and runtime. And it moves some comments around.
1 parent 2b05f84 commit 3c53781

File tree

4 files changed

+106
-40
lines changed

4 files changed

+106
-40
lines changed

compiler/rustc_lint/src/early.rs

+52-22
Original file line numberDiff line numberDiff line change
@@ -20,23 +20,23 @@ use rustc_ast::ptr::P;
2020
use rustc_ast::visit::{self as ast_visit, Visitor};
2121
use rustc_ast::{self as ast, walk_list, HasAttrs};
2222
use rustc_middle::ty::RegisteredTools;
23-
use rustc_session::lint::{BufferedEarlyLint, LintBuffer};
23+
use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass};
2424
use rustc_session::Session;
2525
use rustc_span::symbol::Ident;
2626
use rustc_span::Span;
2727

2828
macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
29-
for pass in $cx.passes.iter_mut() {
30-
pass.$f(&$cx.context, $($args),*);
31-
}
29+
$cx.pass.$f(&$cx.context, $($args),*);
3230
}) }
3331

34-
pub struct EarlyContextAndPasses<'a> {
32+
/// Implements the AST traversal for early lint passes. `T` provides the the
33+
/// `check_*` methods.
34+
pub struct EarlyContextAndPass<'a, T: EarlyLintPass> {
3535
context: EarlyContext<'a>,
36-
passes: Vec<EarlyLintPassObject>,
36+
pass: T,
3737
}
3838

39-
impl<'a> EarlyContextAndPasses<'a> {
39+
impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> {
4040
// This always-inlined function is for the hot call site.
4141
#[inline(always)]
4242
fn inlined_check_id(&mut self, id: ast::NodeId) {
@@ -78,7 +78,7 @@ impl<'a> EarlyContextAndPasses<'a> {
7878
}
7979
}
8080

81-
impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
81+
impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> {
8282
fn visit_param(&mut self, param: &'a ast::Param) {
8383
self.with_lint_attrs(param.id, &param.attrs, |cx| {
8484
lint_callback!(cx, check_param, param);
@@ -296,14 +296,43 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> {
296296
}
297297
}
298298

299+
// Combines multiple lint passes into a single pass, at runtime. Each
300+
// `check_foo` method in `$methods` within this pass simply calls `check_foo`
301+
// once per `$pass`. Compare with `declare_combined_early_lint_pass`, which is
302+
// similar, but combines lint passes at compile time.
303+
struct RuntimeCombinedEarlyLintPass<'a> {
304+
passes: &'a mut [EarlyLintPassObject],
305+
}
306+
307+
#[allow(rustc::lint_pass_impl_without_macro)]
308+
impl LintPass for RuntimeCombinedEarlyLintPass<'_> {
309+
fn name(&self) -> &'static str {
310+
panic!()
311+
}
312+
}
313+
314+
macro_rules! impl_early_lint_pass {
315+
([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => (
316+
impl EarlyLintPass for RuntimeCombinedEarlyLintPass<'_> {
317+
$(fn $f(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) {
318+
for pass in self.passes.iter_mut() {
319+
pass.$f(context, $($param),*);
320+
}
321+
})*
322+
}
323+
)
324+
}
325+
326+
crate::early_lint_methods!(impl_early_lint_pass, []);
327+
299328
/// Early lints work on different nodes - either on the crate root, or on freshly loaded modules.
300329
/// This trait generalizes over those nodes.
301330
pub trait EarlyCheckNode<'a>: Copy {
302331
fn id(self) -> ast::NodeId;
303332
fn attrs<'b>(self) -> &'b [ast::Attribute]
304333
where
305334
'a: 'b;
306-
fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>)
335+
fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
307336
where
308337
'a: 'b;
309338
}
@@ -318,7 +347,7 @@ impl<'a> EarlyCheckNode<'a> for &'a ast::Crate {
318347
{
319348
&self.attrs
320349
}
321-
fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>)
350+
fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
322351
where
323352
'a: 'b,
324353
{
@@ -338,7 +367,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P<ast::
338367
{
339368
self.1
340369
}
341-
fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>)
370+
fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>)
342371
where
343372
'a: 'b,
344373
{
@@ -356,21 +385,22 @@ pub fn check_ast_node<'a>(
356385
builtin_lints: impl EarlyLintPass + 'static,
357386
check_node: impl EarlyCheckNode<'a>,
358387
) {
388+
let context = EarlyContext::new(
389+
sess,
390+
!pre_expansion,
391+
lint_store,
392+
registered_tools,
393+
lint_buffer.unwrap_or_default(),
394+
);
395+
359396
let passes =
360397
if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes };
361-
let mut passes: Vec<EarlyLintPassObject> = passes.iter().map(|p| (p)()).collect();
398+
let mut passes: Vec<EarlyLintPassObject> = passes.iter().map(|mk_pass| (mk_pass)()).collect();
362399
passes.push(Box::new(builtin_lints));
400+
let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] };
401+
402+
let mut cx = EarlyContextAndPass { context, pass };
363403

364-
let mut cx = EarlyContextAndPasses {
365-
context: EarlyContext::new(
366-
sess,
367-
!pre_expansion,
368-
lint_store,
369-
registered_tools,
370-
lint_buffer.unwrap_or_default(),
371-
),
372-
passes,
373-
};
374404
cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx));
375405

376406
// All of the buffered lints should have been emitted at this point.

compiler/rustc_lint/src/late.rs

+44-12
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ use rustc_hir::intravisit as hir_visit;
2323
use rustc_hir::intravisit::Visitor;
2424
use rustc_middle::hir::nested_filter;
2525
use rustc_middle::ty::{self, TyCtxt};
26+
use rustc_session::lint::LintPass;
2627
use rustc_span::Span;
2728

2829
use std::any::Any;
@@ -36,17 +37,17 @@ pub fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore {
3637
}
3738

3839
macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({
39-
for pass in $cx.passes.iter_mut() {
40-
pass.$f(&$cx.context, $($args),*);
41-
}
40+
$cx.pass.$f(&$cx.context, $($args),*);
4241
}) }
4342

44-
struct LateContextAndPasses<'tcx> {
43+
/// Implements the AST traversal for late lint passes. `T` provides the the
44+
/// `check_*` methods.
45+
pub struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> {
4546
context: LateContext<'tcx>,
46-
passes: Vec<LateLintPassObject<'tcx>>,
47+
pass: T,
4748
}
4849

49-
impl<'tcx> LateContextAndPasses<'tcx> {
50+
impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> {
5051
/// Merge the lints specified by any lint attributes into the
5152
/// current lint context, call the provided function, then reset the
5253
/// lints in effect to their previous state.
@@ -82,7 +83,7 @@ impl<'tcx> LateContextAndPasses<'tcx> {
8283
}
8384
}
8485

85-
impl<'tcx> hir_visit::Visitor<'tcx> for LateContextAndPasses<'tcx> {
86+
impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> {
8687
type NestedFilter = nested_filter::All;
8788

8889
/// Because lints are scoped lexically, we want to walk nested
@@ -302,6 +303,35 @@ impl<'tcx> hir_visit::Visitor<'tcx> for LateContextAndPasses<'tcx> {
302303
}
303304
}
304305

306+
// Combines multiple lint passes into a single pass, at runtime. Each
307+
// `check_foo` method in `$methods` within this pass simply calls `check_foo`
308+
// once per `$pass`. Compare with `declare_combined_late_lint_pass`, which is
309+
// similar, but combines lint passes at compile time.
310+
struct RuntimeCombinedLateLintPass<'a, 'tcx> {
311+
passes: &'a mut [LateLintPassObject<'tcx>],
312+
}
313+
314+
#[allow(rustc::lint_pass_impl_without_macro)]
315+
impl LintPass for RuntimeCombinedLateLintPass<'_, '_> {
316+
fn name(&self) -> &'static str {
317+
panic!()
318+
}
319+
}
320+
321+
macro_rules! impl_late_lint_pass {
322+
([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => {
323+
impl<'tcx> LateLintPass<'tcx> for RuntimeCombinedLateLintPass<'_, 'tcx> {
324+
$(fn $f(&mut self, context: &LateContext<'tcx>, $($param: $arg),*) {
325+
for pass in self.passes.iter_mut() {
326+
pass.$f(context, $($param),*);
327+
}
328+
})*
329+
}
330+
};
331+
}
332+
333+
crate::late_lint_methods!(impl_late_lint_pass, []);
334+
305335
pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
306336
tcx: TyCtxt<'tcx>,
307337
module_def_id: LocalDefId,
@@ -320,10 +350,11 @@ pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>(
320350
};
321351

322352
let mut passes: Vec<_> =
323-
unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect();
353+
unerased_lint_store(tcx).late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
324354
passes.push(Box::new(builtin_lints));
355+
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
325356

326-
let mut cx = LateContextAndPasses { context, passes };
357+
let mut cx = LateContextAndPass { context, pass };
327358

328359
let (module, _span, hir_id) = tcx.hir().get_module(module_def_id);
329360
cx.process_mod(module, hir_id);
@@ -349,11 +380,12 @@ fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builti
349380
only_module: false,
350381
};
351382

352-
let mut passes =
353-
unerased_lint_store(tcx).late_passes.iter().map(|p| (p)(tcx)).collect::<Vec<_>>();
383+
let mut passes: Vec<_> =
384+
unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect();
354385
passes.push(Box::new(builtin_lints));
386+
let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] };
355387

356-
let mut cx = LateContextAndPasses { context, passes };
388+
let mut cx = LateContextAndPass { context, pass };
357389

358390
// Visit the whole crate.
359391
cx.with_lint_attrs(hir::CRATE_HIR_ID, |cx| {

compiler/rustc_lint/src/lib.rs

-6
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
127127
late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new());
128128
}
129129

130-
// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar.
131130
early_lint_methods!(
132131
declare_combined_early_lint_pass,
133132
[
@@ -138,9 +137,6 @@ early_lint_methods!(
138137
]
139138
);
140139

141-
// Declare `BuiltinCombinedEarlyLintPass`, a lint pass that combines multiple
142-
// lint passes into a single pass for maximum speed. Each `check_foo` method
143-
// within this pass simply calls `check_foo` once per listed lint.
144140
early_lint_methods!(
145141
declare_combined_early_lint_pass,
146142
[
@@ -168,7 +164,6 @@ early_lint_methods!(
168164

169165
// FIXME: Make a separate lint type which does not require typeck tables.
170166

171-
// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar.
172167
late_lint_methods!(
173168
declare_combined_late_lint_pass,
174169
[
@@ -188,7 +183,6 @@ late_lint_methods!(
188183
]
189184
);
190185

191-
// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar.
192186
late_lint_methods!(
193187
declare_combined_late_lint_pass,
194188
[

compiler/rustc_lint/src/passes.rs

+10
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,11 @@ macro_rules! expand_combined_late_lint_pass_methods {
9595
)
9696
}
9797

98+
/// Combines multiple lints passes into a single lint pass, at compile time,
99+
/// for maximum speed. Each `check_foo` method in `$methods` within this pass
100+
/// simply calls `check_foo` once per `$pass`. Compare with
101+
/// `LateLintPassObjects`, which is similar, but combines lint passes at
102+
/// runtime.
98103
#[macro_export]
99104
macro_rules! declare_combined_late_lint_pass {
100105
([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (
@@ -198,6 +203,11 @@ macro_rules! expand_combined_early_lint_pass_methods {
198203
)
199204
}
200205

206+
/// Combines multiple lints passes into a single lint pass, at compile time,
207+
/// for maximum speed. Each `check_foo` method in `$methods` within this pass
208+
/// simply calls `check_foo` once per `$pass`. Compare with
209+
/// `EarlyLintPassObjects`, which is similar, but combines lint passes at
210+
/// runtime.
201211
#[macro_export]
202212
macro_rules! declare_combined_early_lint_pass {
203213
([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (

0 commit comments

Comments
 (0)