rust/compiler/rustc_lint/src/passes.rs
bors 4d88de2acd Auto merge of #125116 - blyxyas:ignore-allowed-lints-final, r=cjgillot
(Big performance change) Do not run lints that cannot emit

Before this change, adding a lint was a difficult matter because it always had some overhead involved. This was because all lints would run, no matter their default level, or if the user had `#![allow]`ed them. This PR changes that. This change would improve both the Rust lint infrastructure and Clippy, but Clippy will see the most benefit, as it has about 900 registered lints (and growing!)

So yeah, with this little patch we filter all lints pre-linting, and remove any lint that is either:
- Manually `#![allow]`ed in the whole crate,
- Allowed in the command line, or
- Not manually enabled with `#[warn]` or similar, and its default level is `Allow`

As some lints **need** to run, this PR also adds **loadbearing lints**. On a lint declaration, you can use the ``@eval_always` = true` marker to label it as loadbearing. A loadbearing lint will never be filtered (it will always run)

Fixes #106983
2024-10-26 16:37:43 +00:00

252 lines
9.6 KiB
Rust

use rustc_session::lint::LintPass;
use rustc_session::lint::builtin::HardwiredLints;
use crate::context::{EarlyContext, LateContext};
#[macro_export]
macro_rules! late_lint_methods {
($macro:path, $args:tt) => (
$macro!($args, [
fn check_body(a: &rustc_hir::Body<'tcx>);
fn check_body_post(a: &rustc_hir::Body<'tcx>);
fn check_crate();
fn check_crate_post();
fn check_mod(a: &'tcx rustc_hir::Mod<'tcx>, b: rustc_hir::HirId);
fn check_foreign_item(a: &'tcx rustc_hir::ForeignItem<'tcx>);
fn check_item(a: &'tcx rustc_hir::Item<'tcx>);
/// This is called *after* recursing into the item
/// (in contrast to `check_item`, which is checked before).
fn check_item_post(a: &'tcx rustc_hir::Item<'tcx>);
fn check_local(a: &'tcx rustc_hir::LetStmt<'tcx>);
fn check_block(a: &'tcx rustc_hir::Block<'tcx>);
fn check_block_post(a: &'tcx rustc_hir::Block<'tcx>);
fn check_stmt(a: &'tcx rustc_hir::Stmt<'tcx>);
fn check_arm(a: &'tcx rustc_hir::Arm<'tcx>);
fn check_pat(a: &'tcx rustc_hir::Pat<'tcx>);
fn check_expr(a: &'tcx rustc_hir::Expr<'tcx>);
fn check_expr_post(a: &'tcx rustc_hir::Expr<'tcx>);
fn check_ty(a: &'tcx rustc_hir::Ty<'tcx>);
fn check_generic_param(a: &'tcx rustc_hir::GenericParam<'tcx>);
fn check_generics(a: &'tcx rustc_hir::Generics<'tcx>);
fn check_poly_trait_ref(a: &'tcx rustc_hir::PolyTraitRef<'tcx>);
fn check_fn(
a: rustc_hir::intravisit::FnKind<'tcx>,
b: &'tcx rustc_hir::FnDecl<'tcx>,
c: &'tcx rustc_hir::Body<'tcx>,
d: rustc_span::Span,
e: rustc_span::def_id::LocalDefId);
fn check_trait_item(a: &'tcx rustc_hir::TraitItem<'tcx>);
fn check_impl_item(a: &'tcx rustc_hir::ImplItem<'tcx>);
fn check_impl_item_post(a: &'tcx rustc_hir::ImplItem<'tcx>);
fn check_struct_def(a: &'tcx rustc_hir::VariantData<'tcx>);
fn check_field_def(a: &'tcx rustc_hir::FieldDef<'tcx>);
fn check_variant(a: &'tcx rustc_hir::Variant<'tcx>);
fn check_path(a: &rustc_hir::Path<'tcx>, b: rustc_hir::HirId);
fn check_attribute(a: &'tcx rustc_ast::Attribute);
fn check_attributes(a: &'tcx [rustc_ast::Attribute]);
fn check_attributes_post(a: &'tcx [rustc_ast::Attribute]);
]);
)
}
/// Trait for types providing lint checks.
///
/// Each `check` method checks a single syntax node, and should not
/// invoke methods recursively (unlike `Visitor`). By default they
/// do nothing.
//
// FIXME: eliminate the duplication with `Visitor`. But this also
// contains a few lint-specific methods with no equivalent in `Visitor`.
macro_rules! declare_late_lint_pass {
([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
pub trait LateLintPass<'tcx>: LintPass {
$(#[inline(always)] fn $name(&mut self, _: &LateContext<'tcx>, $(_: $arg),*) {})*
}
)
}
// Declare the `LateLintPass` trait, which contains empty default definitions
// for all the `check_*` methods.
late_lint_methods!(declare_late_lint_pass, []);
impl LateLintPass<'_> for HardwiredLints {}
#[macro_export]
macro_rules! expand_combined_late_lint_pass_method {
([$($pass:ident),*], $self: ident, $name: ident, $params:tt) => ({
$($self.$pass.$name $params;)*
})
}
#[macro_export]
macro_rules! expand_combined_late_lint_pass_methods {
($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
$(fn $name(&mut self, context: &$crate::LateContext<'tcx>, $($param: $arg),*) {
$crate::expand_combined_late_lint_pass_method!($passes, self, $name, (context, $($param),*));
})*
)
}
/// 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) => (
#[allow(non_snake_case)]
$v struct $name {
$($pass: $pass,)*
}
impl $name {
$v fn new() -> Self {
Self {
$($pass: $constructor,)*
}
}
$v fn get_lints() -> $crate::LintVec {
let mut lints = Vec::new();
$(lints.extend_from_slice(&$pass::lint_vec());)*
lints
}
}
impl<'tcx> $crate::LateLintPass<'tcx> for $name {
$crate::expand_combined_late_lint_pass_methods!([$($pass),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
impl $crate::LintPass for $name {
fn name(&self) -> &'static str {
panic!()
}
fn get_lints(&self) -> LintVec {
panic!()
}
}
)
}
#[macro_export]
macro_rules! early_lint_methods {
($macro:path, $args:tt) => (
$macro!($args, [
fn check_param(a: &rustc_ast::Param);
fn check_ident(a: &rustc_span::symbol::Ident);
fn check_crate(a: &rustc_ast::Crate);
fn check_crate_post(a: &rustc_ast::Crate);
fn check_item(a: &rustc_ast::Item);
/// This is called *after* recursing into the item
/// (in contrast to `check_item`, which is checked before).
fn check_item_post(a: &rustc_ast::Item);
fn check_local(a: &rustc_ast::Local);
fn check_block(a: &rustc_ast::Block);
fn check_stmt(a: &rustc_ast::Stmt);
fn check_arm(a: &rustc_ast::Arm);
fn check_pat(a: &rustc_ast::Pat);
fn check_pat_post(a: &rustc_ast::Pat);
fn check_expr(a: &rustc_ast::Expr);
fn check_expr_post(a: &rustc_ast::Expr);
fn check_ty(a: &rustc_ast::Ty);
fn check_generic_arg(a: &rustc_ast::GenericArg);
fn check_generic_param(a: &rustc_ast::GenericParam);
fn check_generics(a: &rustc_ast::Generics);
fn check_poly_trait_ref(a: &rustc_ast::PolyTraitRef);
fn check_fn(
a: rustc_ast::visit::FnKind<'_>,
c: rustc_span::Span,
d_: rustc_ast::NodeId);
fn check_trait_item(a: &rustc_ast::AssocItem);
fn check_impl_item(a: &rustc_ast::AssocItem);
fn check_variant(a: &rustc_ast::Variant);
fn check_attribute(a: &rustc_ast::Attribute);
fn check_attributes(a: &[rustc_ast::Attribute]);
fn check_attributes_post(a: &[rustc_ast::Attribute]);
fn check_mac_def(a: &rustc_ast::MacroDef);
fn check_mac(a: &rustc_ast::MacCall);
fn enter_where_predicate(a: &rustc_ast::WherePredicate);
fn exit_where_predicate(a: &rustc_ast::WherePredicate);
]);
)
}
macro_rules! declare_early_lint_pass {
([], [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
pub trait EarlyLintPass: LintPass {
$(#[inline(always)] fn $name(&mut self, _: &EarlyContext<'_>, $(_: $arg),*) {})*
}
)
}
// Declare the `EarlyLintPass` trait, which contains empty default definitions
// for all the `check_*` methods.
early_lint_methods!(declare_early_lint_pass, []);
#[macro_export]
macro_rules! expand_combined_early_lint_pass_method {
([$($pass:ident),*], $self: ident, $name: ident, $params:tt) => ({
$($self.$pass.$name $params;)*
})
}
#[macro_export]
macro_rules! expand_combined_early_lint_pass_methods {
($passes:tt, [$($(#[$attr:meta])* fn $name:ident($($param:ident: $arg:ty),*);)*]) => (
$(fn $name(&mut self, context: &$crate::EarlyContext<'_>, $($param: $arg),*) {
$crate::expand_combined_early_lint_pass_method!($passes, self, $name, (context, $($param),*));
})*
)
}
/// 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) => (
#[allow(non_snake_case)]
$v struct $name {
$($pass: $pass,)*
}
impl $name {
$v fn new() -> Self {
Self {
$($pass: $constructor,)*
}
}
$v fn get_lints() -> $crate::LintVec {
let mut lints = Vec::new();
$(lints.extend_from_slice(&$pass::lint_vec());)*
lints
}
}
impl $crate::EarlyLintPass for $name {
$crate::expand_combined_early_lint_pass_methods!([$($pass),*], $methods);
}
#[allow(rustc::lint_pass_impl_without_macro)]
impl $crate::LintPass for $name {
fn name(&self) -> &'static str {
panic!()
}
fn get_lints(&self) -> LintVec {
panic!()
}
}
)
}
/// A lint pass boxed up as a trait object.
pub(crate) type EarlyLintPassObject = Box<dyn EarlyLintPass + 'static>;
pub(crate) type LateLintPassObject<'tcx> = Box<dyn LateLintPass<'tcx> + 'tcx>;