about summary refs log tree commit diff
path: root/compiler/rustc_lint/src
diff options
context:
space:
mode:
authorblyxyas <blyxyas@gmail.com>2023-11-13 14:35:37 +0100
committerblyxyas <blyxyas@gmail.com>2024-10-19 16:19:44 +0200
commitb4da0585959ec8b2c111bc9f8fd0e34e78c7f260 (patch)
treeca92dd0b22c057b4c1632a91e8967126124b2c74 /compiler/rustc_lint/src
parentc926476d013fbb2ca43bd5259d0a7228009a9cb2 (diff)
downloadrust-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.rs14
-rw-r--r--compiler/rustc_lint/src/early.rs3
-rw-r--r--compiler/rustc_lint/src/internal.rs6
-rw-r--r--compiler/rustc_lint/src/late.rs45
-rw-r--r--compiler/rustc_lint/src/levels.rs121
-rw-r--r--compiler/rustc_lint/src/lib.rs24
-rw-r--r--compiler/rustc_lint/src/passes.rs10
-rw-r--r--compiler/rustc_lint/src/types.rs2
-rw-r--r--compiler/rustc_lint/src/unused.rs4
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() }
     }
 }