diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 126ab8d4ac5..d6a7a8c65ef 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -20,23 +20,23 @@ use rustc_ast::ptr::P; use rustc_ast::visit::{self as ast_visit, Visitor}; use rustc_ast::{self as ast, walk_list, HasAttrs}; use rustc_middle::ty::RegisteredTools; -use rustc_session::lint::{BufferedEarlyLint, LintBuffer}; +use rustc_session::lint::{BufferedEarlyLint, LintBuffer, LintPass}; use rustc_session::Session; use rustc_span::symbol::Ident; use rustc_span::Span; macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ - for pass in $cx.passes.iter_mut() { - pass.$f(&$cx.context, $($args),*); - } + $cx.pass.$f(&$cx.context, $($args),*); }) } -pub struct EarlyContextAndPasses<'a> { +/// Implements the AST traversal for early lint passes. `T` provides the the +/// `check_*` methods. +pub struct EarlyContextAndPass<'a, T: EarlyLintPass> { context: EarlyContext<'a>, - passes: Vec, + pass: T, } -impl<'a> EarlyContextAndPasses<'a> { +impl<'a, T: EarlyLintPass> EarlyContextAndPass<'a, T> { // This always-inlined function is for the hot call site. #[inline(always)] fn inlined_check_id(&mut self, id: ast::NodeId) { @@ -78,7 +78,7 @@ impl<'a> EarlyContextAndPasses<'a> { } } -impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { +impl<'a, T: EarlyLintPass> ast_visit::Visitor<'a> for EarlyContextAndPass<'a, T> { fn visit_param(&mut self, param: &'a ast::Param) { self.with_lint_attrs(param.id, ¶m.attrs, |cx| { lint_callback!(cx, check_param, param); @@ -296,6 +296,35 @@ impl<'a> ast_visit::Visitor<'a> for EarlyContextAndPasses<'a> { } } +// Combines multiple lint passes into a single pass, at runtime. Each +// `check_foo` method in `$methods` within this pass simply calls `check_foo` +// once per `$pass`. Compare with `declare_combined_early_lint_pass`, which is +// similar, but combines lint passes at compile time. +struct RuntimeCombinedEarlyLintPass<'a> { + passes: &'a mut [EarlyLintPassObject], +} + +#[allow(rustc::lint_pass_impl_without_macro)] +impl LintPass for RuntimeCombinedEarlyLintPass<'_> { + fn name(&self) -> &'static str { + panic!() + } +} + +macro_rules! impl_early_lint_pass { + ([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => ( + impl EarlyLintPass for RuntimeCombinedEarlyLintPass<'_> { + $(fn $f(&mut self, context: &EarlyContext<'_>, $($param: $arg),*) { + for pass in self.passes.iter_mut() { + pass.$f(context, $($param),*); + } + })* + } + ) +} + +crate::early_lint_methods!(impl_early_lint_pass, []); + /// Early lints work on different nodes - either on the crate root, or on freshly loaded modules. /// This trait generalizes over those nodes. pub trait EarlyCheckNode<'a>: Copy { @@ -303,7 +332,7 @@ pub trait EarlyCheckNode<'a>: Copy { fn attrs<'b>(self) -> &'b [ast::Attribute] where 'a: 'b; - fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>) + fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) where 'a: 'b; } @@ -318,7 +347,7 @@ impl<'a> EarlyCheckNode<'a> for &'a ast::Crate { { &self.attrs } - fn check<'b>(self, cx: &mut EarlyContextAndPasses<'b>) + fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) where 'a: 'b, { @@ -338,7 +367,7 @@ impl<'a> EarlyCheckNode<'a> for (ast::NodeId, &'a [ast::Attribute], &'a [P(self, cx: &mut EarlyContextAndPasses<'b>) + fn check<'b, T: EarlyLintPass>(self, cx: &mut EarlyContextAndPass<'b, T>) where 'a: 'b, { @@ -356,21 +385,22 @@ pub fn check_ast_node<'a>( builtin_lints: impl EarlyLintPass + 'static, check_node: impl EarlyCheckNode<'a>, ) { + let context = EarlyContext::new( + sess, + !pre_expansion, + lint_store, + registered_tools, + lint_buffer.unwrap_or_default(), + ); + let passes = if pre_expansion { &lint_store.pre_expansion_passes } else { &lint_store.early_passes }; - let mut passes: Vec = passes.iter().map(|p| (p)()).collect(); + let mut passes: Vec = passes.iter().map(|mk_pass| (mk_pass)()).collect(); passes.push(Box::new(builtin_lints)); + let pass = RuntimeCombinedEarlyLintPass { passes: &mut passes[..] }; + + let mut cx = EarlyContextAndPass { context, pass }; - let mut cx = EarlyContextAndPasses { - context: EarlyContext::new( - sess, - !pre_expansion, - lint_store, - registered_tools, - lint_buffer.unwrap_or_default(), - ), - passes, - }; cx.with_lint_attrs(check_node.id(), check_node.attrs(), |cx| check_node.check(cx)); // All of the buffered lints should have been emitted at this point. diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 8a50cb1f19e..11cd246c725 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -23,6 +23,7 @@ use rustc_hir::intravisit as hir_visit; use rustc_hir::intravisit::Visitor; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; +use rustc_session::lint::LintPass; use rustc_span::Span; use std::any::Any; @@ -36,17 +37,17 @@ pub fn unerased_lint_store(tcx: TyCtxt<'_>) -> &LintStore { } macro_rules! lint_callback { ($cx:expr, $f:ident, $($args:expr),*) => ({ - for pass in $cx.passes.iter_mut() { - pass.$f(&$cx.context, $($args),*); - } + $cx.pass.$f(&$cx.context, $($args),*); }) } -struct LateContextAndPasses<'tcx> { +/// Implements the AST traversal for late lint passes. `T` provides the the +/// `check_*` methods. +pub struct LateContextAndPass<'tcx, T: LateLintPass<'tcx>> { context: LateContext<'tcx>, - passes: Vec>, + pass: T, } -impl<'tcx> LateContextAndPasses<'tcx> { +impl<'tcx, T: LateLintPass<'tcx>> LateContextAndPass<'tcx, T> { /// Merge the lints specified by any lint attributes into the /// current lint context, call the provided function, then reset the /// lints in effect to their previous state. @@ -82,7 +83,7 @@ impl<'tcx> LateContextAndPasses<'tcx> { } } -impl<'tcx> hir_visit::Visitor<'tcx> for LateContextAndPasses<'tcx> { +impl<'tcx, T: LateLintPass<'tcx>> hir_visit::Visitor<'tcx> for LateContextAndPass<'tcx, T> { type NestedFilter = nested_filter::All; /// Because lints are scoped lexically, we want to walk nested @@ -302,6 +303,35 @@ impl<'tcx> hir_visit::Visitor<'tcx> for LateContextAndPasses<'tcx> { } } +// Combines multiple lint passes into a single pass, at runtime. Each +// `check_foo` method in `$methods` within this pass simply calls `check_foo` +// once per `$pass`. Compare with `declare_combined_late_lint_pass`, which is +// similar, but combines lint passes at compile time. +struct RuntimeCombinedLateLintPass<'a, 'tcx> { + passes: &'a mut [LateLintPassObject<'tcx>], +} + +#[allow(rustc::lint_pass_impl_without_macro)] +impl LintPass for RuntimeCombinedLateLintPass<'_, '_> { + fn name(&self) -> &'static str { + panic!() + } +} + +macro_rules! impl_late_lint_pass { + ([], [$($(#[$attr:meta])* fn $f:ident($($param:ident: $arg:ty),*);)*]) => { + impl<'tcx> LateLintPass<'tcx> for RuntimeCombinedLateLintPass<'_, 'tcx> { + $(fn $f(&mut self, context: &LateContext<'tcx>, $($param: $arg),*) { + for pass in self.passes.iter_mut() { + pass.$f(context, $($param),*); + } + })* + } + }; +} + +crate::late_lint_methods!(impl_late_lint_pass, []); + pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( tcx: TyCtxt<'tcx>, module_def_id: LocalDefId, @@ -320,10 +350,11 @@ pub(super) fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( }; let mut passes: Vec<_> = - unerased_lint_store(tcx).late_module_passes.iter().map(|pass| (pass)(tcx)).collect(); + unerased_lint_store(tcx).late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); passes.push(Box::new(builtin_lints)); + let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; - let mut cx = LateContextAndPasses { context, passes }; + let mut cx = LateContextAndPass { context, pass }; let (module, _span, hir_id) = tcx.hir().get_module(module_def_id); cx.process_mod(module, hir_id); @@ -349,11 +380,12 @@ fn late_lint_crate<'tcx, T: LateLintPass<'tcx> + 'tcx>(tcx: TyCtxt<'tcx>, builti only_module: false, }; - let mut passes = - unerased_lint_store(tcx).late_passes.iter().map(|p| (p)(tcx)).collect::>(); + let mut passes: Vec<_> = + unerased_lint_store(tcx).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); passes.push(Box::new(builtin_lints)); + let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; - let mut cx = LateContextAndPasses { context, passes }; + let mut cx = LateContextAndPass { context, pass }; // Visit the whole crate. cx.with_lint_attrs(hir::CRATE_HIR_ID, |cx| { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 1990a74841b..11022eb80ea 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -127,7 +127,6 @@ fn lint_mod(tcx: TyCtxt<'_>, module_def_id: LocalDefId) { late::late_lint_mod(tcx, module_def_id, BuiltinCombinedModuleLateLintPass::new()); } -// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar. early_lint_methods!( declare_combined_early_lint_pass, [ @@ -138,9 +137,6 @@ early_lint_methods!( ] ); -// Declare `BuiltinCombinedEarlyLintPass`, a lint pass that combines multiple -// lint passes into a single pass for maximum speed. Each `check_foo` method -// within this pass simply calls `check_foo` once per listed lint. early_lint_methods!( declare_combined_early_lint_pass, [ @@ -168,7 +164,6 @@ early_lint_methods!( // FIXME: Make a separate lint type which does not require typeck tables. -// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar. late_lint_methods!( declare_combined_late_lint_pass, [ @@ -188,7 +183,6 @@ late_lint_methods!( ] ); -// See the comment on `BuiltinCombinedEarlyLintPass`, which is similar. late_lint_methods!( declare_combined_late_lint_pass, [ diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 00922cef384..5558156a4b9 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -95,6 +95,11 @@ macro_rules! expand_combined_late_lint_pass_methods { ) } +/// Combines multiple lints passes into a single lint pass, at compile time, +/// for maximum speed. Each `check_foo` method in `$methods` within this pass +/// simply calls `check_foo` once per `$pass`. Compare with +/// `LateLintPassObjects`, which is similar, but combines lint passes at +/// runtime. #[macro_export] macro_rules! declare_combined_late_lint_pass { ([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => ( @@ -198,6 +203,11 @@ macro_rules! expand_combined_early_lint_pass_methods { ) } +/// Combines multiple lints passes into a single lint pass, at compile time, +/// for maximum speed. Each `check_foo` method in `$methods` within this pass +/// simply calls `check_foo` once per `$pass`. Compare with +/// `EarlyLintPassObjects`, which is similar, but combines lint passes at +/// runtime. #[macro_export] macro_rules! declare_combined_early_lint_pass { ([$v:vis $name:ident, [$($pass:ident: $constructor:expr,)*]], $methods:tt) => (