diff options
| author | blyxyas <blyxyas@gmail.com> | 2023-11-13 14:35:37 +0100 |
|---|---|---|
| committer | blyxyas <blyxyas@gmail.com> | 2024-10-19 16:19:44 +0200 |
| commit | b4da0585959ec8b2c111bc9f8fd0e34e78c7f260 (patch) | |
| tree | ca92dd0b22c057b4c1632a91e8967126124b2c74 /compiler/rustc_lint/src | |
| parent | c926476d013fbb2ca43bd5259d0a7228009a9cb2 (diff) | |
| download | rust-b4da0585959ec8b2c111bc9f8fd0e34e78c7f260.tar.gz rust-b4da0585959ec8b2c111bc9f8fd0e34e78c7f260.zip | |
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
Diffstat (limited to 'compiler/rustc_lint/src')
| -rw-r--r-- | compiler/rustc_lint/src/builtin.rs | 14 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/early.rs | 3 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/internal.rs | 6 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/late.rs | 45 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/levels.rs | 121 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/lib.rs | 24 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/passes.rs | 10 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/types.rs | 2 | ||||
| -rw-r--r-- | compiler/rustc_lint/src/unused.rs | 4 |
9 files changed, 198 insertions, 31 deletions
diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 8bd9c899a62..acd12f7f20d 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -74,6 +74,12 @@ use crate::{ fluent_generated as fluent, }; +use std::default::Default; +use std::fmt::Write; + +// hardwired lints from rustc_lint_defs +pub use rustc_session::lint::builtin::*; + declare_lint! { /// The `while_true` lint detects `while true { }`. /// @@ -241,7 +247,8 @@ declare_lint! { /// behavior. UNSAFE_CODE, Allow, - "usage of `unsafe` code and other potentially unsound constructs" + "usage of `unsafe` code and other potentially unsound constructs", + [loadbearing: true] } declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); @@ -389,6 +396,7 @@ declare_lint! { report_in_external_macro } +#[derive(Default)] pub struct MissingDoc; impl_lint_pass!(MissingDoc => [MISSING_DOCS]); @@ -819,8 +827,8 @@ pub struct DeprecatedAttr { impl_lint_pass!(DeprecatedAttr => []); -impl DeprecatedAttr { - pub fn new() -> DeprecatedAttr { +impl Default for DeprecatedAttr { + fn default() -> Self { DeprecatedAttr { depr_attrs: deprecated_attributes() } } } diff --git a/compiler/rustc_lint/src/early.rs b/compiler/rustc_lint/src/early.rs index 2285877c9ef..a63a0833541 100644 --- a/compiler/rustc_lint/src/early.rs +++ b/compiler/rustc_lint/src/early.rs @@ -312,6 +312,9 @@ impl LintPass for RuntimeCombinedEarlyLintPass<'_> { fn name(&self) -> &'static str { panic!() } + fn get_lints(&self) -> crate::LintVec { + panic!() + } } macro_rules! impl_early_lint_pass { diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 94cc58e4956..0f4f58efd8e 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -429,7 +429,8 @@ declare_tool_lint! { pub rustc::UNTRANSLATABLE_DIAGNOSTIC, Deny, "prevent creation of diagnostics which cannot be translated", - report_in_external_macro: true + report_in_external_macro: true, + [loadbearing: true] } declare_tool_lint! { @@ -442,7 +443,8 @@ declare_tool_lint! { pub rustc::DIAGNOSTIC_OUTSIDE_OF_IMPL, Deny, "prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls", - report_in_external_macro: true + report_in_external_macro: true, + [loadbearing: true] } declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]); diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index 6d5903ac467..ccd06ba612b 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -326,6 +326,9 @@ impl LintPass for RuntimeCombinedLateLintPass<'_, '_> { fn name(&self) -> &'static str { panic!() } + fn get_lints(&self) -> crate::LintVec { + panic!() + } } macro_rules! impl_late_lint_pass { @@ -361,13 +364,38 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( // Note: `passes` is often empty. In that case, it's faster to run // `builtin_lints` directly rather than bundling it up into the // `RuntimeCombinedLateLintPass`. - let late_module_passes = &unerased_lint_store(tcx.sess).late_module_passes; - if late_module_passes.is_empty() { + let store = unerased_lint_store(tcx.sess); + + if store.late_module_passes.is_empty() { late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); } else { - let mut passes: Vec<_> = late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); - passes.push(Box::new(builtin_lints)); - let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; + let passes: Vec<_> = + store.late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); + + // Filter unused lints + let (lints_to_emit, lints_allowed) = &**tcx.lints_that_can_emit(()); + // let lints_to_emit = &lints_that_can_emit.0; + // let lints_allowed = &lints_that_can_emit.1; + + // Now, we'll filtered passes in a way that discards any lint that won't trigger. + // If any lint is a given pass is detected to be emitted, we will keep that pass. + // Otherwise, we don't + let mut filtered_passes: Vec<Box<dyn LateLintPass<'tcx>>> = passes + .into_iter() + .filter(|pass| { + let pass = LintPass::get_lints(pass); + pass.iter().any(|&lint| { + let lint_name = name_without_tool(&lint.name.to_lowercase()).to_string(); + lints_to_emit.contains(&lint_name) + || (!lints_allowed.contains(&lint_name) + && lint.default_level != crate::Level::Allow) + }) + }) + .collect(); + + filtered_passes.push(Box::new(builtin_lints)); + + let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] }; late_lint_mod_inner(tcx, module_def_id, context, pass); } } @@ -454,3 +482,10 @@ pub fn check_crate<'tcx>(tcx: TyCtxt<'tcx>) { }, ); } + +/// Format name ignoring the name, useful for filtering non-used lints. +/// For example, 'clippy::my_lint' will turn into 'my_lint' +pub(crate) fn name_without_tool(name: &str) -> &str { + // Doing some calculations here to account for those separators + name.rsplit("::").next().unwrap_or(name) +} diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 89a67fc0d89..7ee8618bc55 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,6 +1,6 @@ use rustc_ast_pretty::pprust; -use rustc_data_structures::fx::FxIndexMap; -use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; +use rustc_data_structures::{fx::FxIndexMap, sync::Lrc}; +use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan}; use rustc_feature::{Features, GateIssue}; use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; @@ -31,7 +31,7 @@ use crate::errors::{ OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup, }; use crate::fluent_generated as fluent; -use crate::late::unerased_lint_store; +use crate::late::{unerased_lint_store, name_without_tool}; use crate::lints::{ DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified, OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint, @@ -115,6 +115,36 @@ impl LintLevelSets { } } +/// Walk the whole crate collecting nodes where lint levels change +/// (e.g. `#[allow]` attributes), and joins that list with the warn-by-default +/// (and not allowed in the crate) and CLI lints. The returned value is a tuple +/// of 1. The lints that will emit (or at least, should run), and 2. +/// The lints that are allowed at the crate level and will not emit. +pub fn lints_that_can_emit(tcx: TyCtxt<'_>, (): ()) -> Lrc<(Vec<String>, Vec<String>)> { + let mut visitor = LintLevelMinimum::new(tcx); + visitor.process_opts(); + tcx.hir().walk_attributes(&mut visitor); + + let store = unerased_lint_store(&tcx.sess); + + let lint_groups = store.get_lint_groups(); + for group in lint_groups { + let binding = group.0.to_lowercase(); + let group_name = name_without_tool(&binding).to_string(); + if visitor.lints_to_emit.contains(&group_name) { + for lint in group.1 { + visitor.lints_to_emit.push(name_without_tool(&lint.to_string()).to_string()); + } + } else if visitor.lints_allowed.contains(&group_name) { + for lint in &group.1 { + visitor.lints_allowed.push(name_without_tool(&lint.to_string()).to_string()); + } + } + } + + Lrc::new((visitor.lints_to_emit, visitor.lints_allowed)) +} + #[instrument(level = "trace", skip(tcx), ret)] fn shallow_lint_levels_on(tcx: TyCtxt<'_>, owner: hir::OwnerId) -> ShallowLintLevelMap { let store = unerased_lint_store(tcx.sess); @@ -301,6 +331,88 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { } } +/// Visitor with the only function of visiting every item-like in a crate and +/// computing the highest level that every lint gets put to. +/// +/// E.g., if a crate has a global #![allow(lint)] attribute, but a single item +/// uses #[warn(lint)], this visitor will set that lint level as `Warn` +struct LintLevelMinimum<'tcx> { + tcx: TyCtxt<'tcx>, + /// The actual list of detected lints. + lints_to_emit: Vec<String>, + lints_allowed: Vec<String>, +} + +impl<'tcx> LintLevelMinimum<'tcx> { + pub fn new(tcx: TyCtxt<'tcx>) -> Self { + Self { + tcx, + // That magic number is the current number of lints + some more for possible future lints + lints_to_emit: Vec::with_capacity(230), + lints_allowed: Vec::with_capacity(100), + } + } + + fn process_opts(&mut self) { + for (lint, level) in &self.tcx.sess.opts.lint_opts { + if *level == Level::Allow { + self.lints_allowed.push(lint.clone()); + } else { + self.lints_to_emit.push(lint.to_string()); + } + } + } +} + +impl<'tcx> Visitor<'tcx> for LintLevelMinimum<'tcx> { + type NestedFilter = nested_filter::All; + + fn nested_visit_map(&mut self) -> Self::Map { + self.tcx.hir() + } + + fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) { + if let Some(meta) = attribute.meta() { + if [sym::warn, sym::deny, sym::forbid, sym::expect] + .iter() + .any(|kind| meta.has_name(*kind)) + { + // SAFETY: Lint attributes are always a metalist inside a + // metalist (even with just one lint). + for meta_list in meta.meta_item_list().unwrap() { + // If it's a tool lint (e.g. clippy::my_clippy_lint) + if let ast::NestedMetaItem::MetaItem(meta_item) = meta_list { + if meta_item.path.segments.len() == 1 { + self.lints_to_emit.push( + // SAFETY: Lint attributes can only have literals + meta_list.ident().unwrap().name.as_str().to_string(), + ); + } else { + self.lints_to_emit + .push(meta_item.path.segments[1].ident.name.as_str().to_string()); + } + } + } + // We handle #![allow]s differently, as these remove checking rather than adding. + } else if meta.has_name(sym::allow) + && let ast::AttrStyle::Inner = attribute.style + { + for meta_list in meta.meta_item_list().unwrap() { + // If it's a tool lint (e.g. clippy::my_clippy_lint) + if let ast::NestedMetaItem::MetaItem(meta_item) = meta_list { + if meta_item.path.segments.len() == 1 { + self.lints_allowed.push(meta_list.name_or_empty().as_str().to_string()) + } else { + self.lints_allowed + .push(meta_item.path.segments[1].ident.name.as_str().to_string()); + } + } + } + } + } + } +} + pub struct LintLevelsBuilder<'s, P> { sess: &'s Session, features: &'s Features, @@ -931,7 +1043,8 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } pub(crate) fn provide(providers: &mut Providers) { - *providers = Providers { shallow_lint_levels_on, ..*providers }; + *providers = + Providers { shallow_lint_levels_on, lints_that_can_emit, ..*providers }; } pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option<Symbol>, &str) { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 81352af3d48..5984810961f 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -170,7 +170,7 @@ early_lint_methods!( [ pub BuiltinCombinedEarlyLintPass, [ - UnusedParens: UnusedParens::new(), + UnusedParens: UnusedParens::default(), UnusedBraces: UnusedBraces, UnusedImportBraces: UnusedImportBraces, UnsafeCode: UnsafeCode, @@ -178,7 +178,7 @@ early_lint_methods!( AnonymousParameters: AnonymousParameters, EllipsisInclusiveRangePatterns: EllipsisInclusiveRangePatterns::default(), NonCamelCaseTypes: NonCamelCaseTypes, - DeprecatedAttr: DeprecatedAttr::new(), + DeprecatedAttr: DeprecatedAttr::default(), WhileTrue: WhileTrue, NonAsciiIdents: NonAsciiIdents, HiddenUnicodeCodepoints: HiddenUnicodeCodepoints, @@ -601,25 +601,25 @@ fn register_builtins(store: &mut LintStore) { } fn register_internals(store: &mut LintStore) { - store.register_lints(&LintPassImpl::get_lints()); + store.register_lints(&LintPassImpl::default().get_lints()); store.register_early_pass(|| Box::new(LintPassImpl)); - store.register_lints(&DefaultHashTypes::get_lints()); + store.register_lints(&DefaultHashTypes::default().get_lints()); store.register_late_mod_pass(|_| Box::new(DefaultHashTypes)); - store.register_lints(&QueryStability::get_lints()); + store.register_lints(&QueryStability::default().get_lints()); store.register_late_mod_pass(|_| Box::new(QueryStability)); - store.register_lints(&ExistingDocKeyword::get_lints()); + store.register_lints(&ExistingDocKeyword::default().get_lints()); store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword)); - store.register_lints(&TyTyKind::get_lints()); + store.register_lints(&TyTyKind::default().get_lints()); store.register_late_mod_pass(|_| Box::new(TyTyKind)); - store.register_lints(&TypeIr::get_lints()); + store.register_lints(&TypeIr::default().get_lints()); store.register_late_mod_pass(|_| Box::new(TypeIr)); - store.register_lints(&Diagnostics::get_lints()); + store.register_lints(&Diagnostics::default().get_lints()); store.register_late_mod_pass(|_| Box::new(Diagnostics)); - store.register_lints(&BadOptAccess::get_lints()); + store.register_lints(&BadOptAccess::default().get_lints()); store.register_late_mod_pass(|_| Box::new(BadOptAccess)); - store.register_lints(&PassByValue::get_lints()); + store.register_lints(&PassByValue::default().get_lints()); store.register_late_mod_pass(|_| Box::new(PassByValue)); - store.register_lints(&SpanUseEqCtxt::get_lints()); + store.register_lints(&SpanUseEqCtxt::default().get_lints()); store.register_late_mod_pass(|_| Box::new(SpanUseEqCtxt)); // FIXME(davidtwco): deliberately do not include `UNTRANSLATABLE_DIAGNOSTIC` and // `DIAGNOSTIC_OUTSIDE_OF_IMPL` here because `-Wrustc::internal` is provided to every crate and diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index a1d436e0d3d..3750f90a044 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -110,7 +110,7 @@ macro_rules! declare_combined_late_lint_pass { $v fn get_lints() -> $crate::LintVec { let mut lints = Vec::new(); - $(lints.extend_from_slice(&$pass::get_lints());)* + $(lints.extend_from_slice(&$pass::default().get_lints());)* lints } } @@ -124,6 +124,9 @@ macro_rules! declare_combined_late_lint_pass { fn name(&self) -> &'static str { panic!() } + fn get_lints(&self) -> LintVec { + panic!() + } } ) } @@ -222,7 +225,7 @@ macro_rules! declare_combined_early_lint_pass { $v fn get_lints() -> $crate::LintVec { let mut lints = Vec::new(); - $(lints.extend_from_slice(&$pass::get_lints());)* + $(lints.extend_from_slice(&$pass::default().get_lints());)* lints } } @@ -236,6 +239,9 @@ macro_rules! declare_combined_early_lint_pass { fn name(&self) -> &'static str { panic!() } + fn get_lints(&self) -> LintVec { + panic!() + } } ) } diff --git a/compiler/rustc_lint/src/types.rs b/compiler/rustc_lint/src/types.rs index db4413149a4..1fafa7d6bde 100644 --- a/compiler/rustc_lint/src/types.rs +++ b/compiler/rustc_lint/src/types.rs @@ -165,7 +165,7 @@ declare_lint! { "detects ambiguous wide pointer comparisons" } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub(crate) struct TypeLimits { /// Id of the last visited negated expression negated_expr_id: Option<hir::HirId>, diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index 1a007250961..a05616bf486 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1025,8 +1025,8 @@ pub(crate) struct UnusedParens { parens_in_cast_in_lt: Vec<ast::NodeId>, } -impl UnusedParens { - pub(crate) fn new() -> Self { +impl Default for UnusedParens { + fpub(crate) fn default() -> Self { Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() } } } |
