From b4da0585959ec8b2c111bc9f8fd0e34e78c7f260 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Mon, 13 Nov 2023 14:35:37 +0100 Subject: 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 --- compiler/rustc_ast/src/attr/mod.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 14 +++- compiler/rustc_lint/src/early.rs | 3 + compiler/rustc_lint/src/internal.rs | 6 +- compiler/rustc_lint/src/late.rs | 45 ++++++++++-- compiler/rustc_lint/src/levels.rs | 121 ++++++++++++++++++++++++++++++-- compiler/rustc_lint/src/lib.rs | 24 +++---- compiler/rustc_lint/src/passes.rs | 10 ++- compiler/rustc_lint/src/types.rs | 2 +- compiler/rustc_lint/src/unused.rs | 4 +- compiler/rustc_lint_defs/src/builtin.rs | 6 +- compiler/rustc_lint_defs/src/lib.rs | 30 ++++++-- compiler/rustc_middle/Cargo.toml | 1 + compiler/rustc_middle/src/lint.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 5 ++ 15 files changed, 235 insertions(+), 40 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_ast/src/attr/mod.rs b/compiler/rustc_ast/src/attr/mod.rs index b73412a4b1d..9311af28f54 100644 --- a/compiler/rustc_ast/src/attr/mod.rs +++ b/compiler/rustc_ast/src/attr/mod.rs @@ -223,7 +223,7 @@ impl AttrItem { self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span)) } - fn meta_item_list(&self) -> Option> { + pub fn meta_item_list(&self) -> Option> { match &self.args { AttrArgs::Delimited(args) if args.delim == Delimiter::Parenthesis => { MetaItemKind::list_from_tokens(args.tokens.clone()) 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>> = 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, Vec)> { + 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, + lints_allowed: Vec, +} + +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, &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, 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, } -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() } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 45a5ce0ca20..5c3b1bb71f3 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -378,7 +378,8 @@ declare_lint! { /// will not overflow. pub ARITHMETIC_OVERFLOW, Deny, - "arithmetic operation overflows" + "arithmetic operation overflows", + [loadbearing: true] } declare_lint! { @@ -633,7 +634,8 @@ declare_lint! { /// is only available in a newer version. pub UNKNOWN_LINTS, Warn, - "unrecognized lint attribute" + "unrecognized lint attribute", + [loadbearing: true] } declare_lint! { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index c01fa5c54d6..691f37f0977 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -312,6 +312,10 @@ pub struct Lint { pub feature_gate: Option, pub crate_level_only: bool, + + /// `true` if this lint should not be filtered out under any circustamces + /// (e.g. the unknown_attributes lint) + pub loadbearing: bool, } /// Extra information for a future incompatibility lint. @@ -456,6 +460,7 @@ impl Lint { future_incompatible: None, feature_gate: None, crate_level_only: false, + loadbearing: false, } } @@ -863,7 +868,7 @@ macro_rules! declare_lint { $(#[$attr])* $vis $NAME, $Level, $desc, ); ); - ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, + ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $([loadbearing: $loadbearing: literal])? $(@feature_gate = $gate:ident;)? $(@future_incompatible = FutureIncompatibleInfo { reason: $reason:expr, @@ -885,6 +890,7 @@ macro_rules! declare_lint { ..$crate::FutureIncompatibleInfo::default_fields_for_macro() }),)? $(edition_lint_opts: Some(($crate::Edition::$lint_edition, $crate::$edition_level)),)? + $(loadbearing: $loadbearing,)? ..$crate::Lint::default_fields_for_macro() }; ); @@ -895,6 +901,7 @@ macro_rules! declare_tool_lint { ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr $(, @feature_gate = $gate:ident;)? + $(, [loadbearing: $loadbearing: literal])? ) => ( $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false $(, @feature_gate = $gate;)?} ); @@ -902,6 +909,7 @@ macro_rules! declare_tool_lint { $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, report_in_external_macro: $rep:expr $(, @feature_gate = $gate:ident;)? + $(, [loadbearing: $loadbearing: literal])? ) => ( $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep $(, @feature_gate = $gate;)?} ); @@ -909,6 +917,7 @@ macro_rules! declare_tool_lint { $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, $external:expr $(, @feature_gate = $gate:ident;)? + $(, [loadbearing: $loadbearing: literal])? ) => ( $(#[$attr])* $vis static $NAME: &$crate::Lint = &$crate::Lint { @@ -921,6 +930,7 @@ macro_rules! declare_tool_lint { is_externally_loaded: true, $(feature_gate: Some(rustc_span::symbol::sym::$gate),)? crate_level_only: false, + $(loadbearing: $loadbearing,)? ..$crate::Lint::default_fields_for_macro() }; ); @@ -930,6 +940,7 @@ pub type LintVec = Vec<&'static Lint>; pub trait LintPass { fn name(&self) -> &'static str; + fn get_lints(&self) -> LintVec; } /// Implements `LintPass for $ty` with the given list of `Lint` statics. @@ -938,9 +949,7 @@ macro_rules! impl_lint_pass { ($ty:ty => [$($lint:expr),* $(,)?]) => { impl $crate::LintPass for $ty { fn name(&self) -> &'static str { stringify!($ty) } - } - impl $ty { - pub fn get_lints() -> $crate::LintVec { vec![$($lint),*] } + fn get_lints(&self) -> $crate::LintVec { vec![$($lint),*] } } }; } @@ -950,7 +959,18 @@ macro_rules! impl_lint_pass { #[macro_export] macro_rules! declare_lint_pass { ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => { - $(#[$m])* #[derive(Copy, Clone)] pub struct $name; + $(#[$m])* #[derive(Copy, Clone, Default)] pub struct $name; $crate::impl_lint_pass!($name => [$($lint),*]); }; } + +#[allow(rustc::lint_pass_impl_without_macro)] +impl LintPass for Box

{ + fn name(&self) -> &'static str { + (**self).name() + } + + fn get_lints(&self) -> LintVec { + (**self).get_lints() + } +} diff --git a/compiler/rustc_middle/Cargo.toml b/compiler/rustc_middle/Cargo.toml index 8cb602d9ea8..485d1c14df3 100644 --- a/compiler/rustc_middle/Cargo.toml +++ b/compiler/rustc_middle/Cargo.toml @@ -27,6 +27,7 @@ rustc_graphviz = { path = "../rustc_graphviz" } rustc_hir = { path = "../rustc_hir" } rustc_hir_pretty = { path = "../rustc_hir_pretty" } rustc_index = { path = "../rustc_index" } +rustc_lint_defs = { path = "../rustc_lint_defs" } rustc_macros = { path = "../rustc_macros" } rustc_query_system = { path = "../rustc_query_system" } rustc_serialize = { path = "../rustc_serialize" } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index b5862565e8e..eaec19700c8 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -117,7 +117,7 @@ impl ShallowLintLevelMap { /// This lint level is not usable for diagnostics, it needs to be corrected by /// `reveal_actual_level` beforehand. #[instrument(level = "trace", skip(self, tcx), ret)] - fn probe_for_lint_level( + pub fn probe_for_lint_level( &self, tcx: TyCtxt<'_>, id: LintId, diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index f8ba606e087..e90c3a4c23d 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -422,6 +422,11 @@ rustc_queries! { desc { "computing `#[expect]`ed lints in this crate" } } + query lints_that_can_emit(_: ()) -> &'tcx Lrc<(Vec, Vec)> { + arena_cache + desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" } + } + query expn_that_defined(key: DefId) -> rustc_span::ExpnId { desc { |tcx| "getting the expansion that defined `{}`", tcx.def_path_str(key) } separate_provide_extern -- cgit 1.4.1-3-g733a5 From edc65776274d14fc7f7f93de66ac3b2d15bbbc37 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Mon, 27 May 2024 13:58:33 +0200 Subject: Change lints_to_emit to lints_that_actually_run --- compiler/rustc_lint/src/late.rs | 6 ++-- compiler/rustc_lint/src/levels.rs | 40 +++++++++++++++------------ compiler/rustc_lint/src/shadowed_into_iter.rs | 2 +- compiler/rustc_middle/src/query/mod.rs | 2 +- 4 files changed, 27 insertions(+), 23 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index ccd06ba612b..c7187ee1b30 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -373,8 +373,8 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( 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_that_actually_run, lints_allowed) = &**tcx.lints_that_can_emit(()); + // let lints_that_actually_run = &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. @@ -386,7 +386,7 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( 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_that_actually_run.contains(&lint_name) || (!lints_allowed.contains(&lint_name) && lint.default_level != crate::Level::Allow) }) diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 7ee8618bc55..527fc117351 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, sync::Lrc}; -use rustc_errors::{Diag, DiagMessage, LintDiagnostic, MultiSpan}; +use rustc_data_structures::{fx::FxIndexMap, fx::FxIndexSet, sync::Lrc}; +use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_feature::{Features, GateIssue}; use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; @@ -120,7 +120,7 @@ impl LintLevelSets { /// (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, Vec)> { +pub fn lints_that_can_emit(tcx: TyCtxt<'_>, (): ()) -> Lrc<(FxIndexSet, FxIndexSet)> { let mut visitor = LintLevelMinimum::new(tcx); visitor.process_opts(); tcx.hir().walk_attributes(&mut visitor); @@ -131,18 +131,18 @@ pub fn lints_that_can_emit(tcx: TyCtxt<'_>, (): ()) -> Lrc<(Vec, Vec Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { struct LintLevelMinimum<'tcx> { tcx: TyCtxt<'tcx>, /// The actual list of detected lints. - lints_to_emit: Vec, - lints_allowed: Vec, + lints_that_actually_run: FxIndexSet, + lints_allowed: FxIndexSet, } impl<'tcx> LintLevelMinimum<'tcx> { pub fn new(tcx: TyCtxt<'tcx>) -> Self { + let mut lints_that_actually_run = FxIndexSet::default(); + lints_that_actually_run.reserve(230); + let mut lints_allowed = FxIndexSet::default(); + lints_allowed.reserve(100); 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), + lints_that_actually_run, + lints_allowed, } } 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()); + self.lints_allowed.insert(lint.clone()); } else { - self.lints_to_emit.push(lint.to_string()); + self.lints_that_actually_run.insert(lint.to_string()); } } } @@ -383,13 +387,13 @@ impl<'tcx> Visitor<'tcx> for LintLevelMinimum<'tcx> { // 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( + self.lints_that_actually_run.insert( // 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()); + self.lints_that_actually_run + .insert(meta_item.path.segments[1].ident.name.as_str().to_string()); } } } @@ -401,10 +405,10 @@ impl<'tcx> Visitor<'tcx> for LintLevelMinimum<'tcx> { // 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()) + self.lints_allowed.insert(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()); + .insert(meta_item.path.segments[1].ident.name.as_str().to_string()); } } } diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index a73904cd776..33a4ae60663 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -64,7 +64,7 @@ declare_lint! { }; } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Default)] pub(crate) struct ShadowedIntoIter; impl_lint_pass!(ShadowedIntoIter => [ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER]); diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index e90c3a4c23d..b6cb11a2a2c 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -422,7 +422,7 @@ rustc_queries! { desc { "computing `#[expect]`ed lints in this crate" } } - query lints_that_can_emit(_: ()) -> &'tcx Lrc<(Vec, Vec)> { + query lints_that_can_emit(_: ()) -> &'tcx Lrc<(FxIndexSet, FxIndexSet)> { arena_cache desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" } } -- cgit 1.4.1-3-g733a5 From 71b4d108c7e71c15c0f61d737ebdc0e1cf559d3c Mon Sep 17 00:00:00 2001 From: blyxyas Date: Tue, 18 Jun 2024 22:44:28 +0200 Subject: Follow review comments (optimize the filtering) --- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 7 +- compiler/rustc_lint/src/late.rs | 64 +- compiler/rustc_lint/src/levels.rs | 202 +++--- compiler/rustc_lint/src/lib.rs | 2 +- compiler/rustc_lint/src/passes.rs | 13 +- compiler/rustc_lint/src/unused.rs | 2 +- compiler/rustc_lint_defs/src/builtin.rs | 5 +- compiler/rustc_middle/src/query/mod.rs | 3 +- compiler/rustc_session/src/session.rs | 1 + src/tools/clippy/clippy_lints/src/asm_syntax.rs | 2 +- .../clippy_lints/src/cognitive_complexity.rs | 54 +- src/tools/clippy/clippy_lints/src/ctfe.rs | 54 ++ src/tools/clippy/clippy_lints/src/lib.rs | 3 + src/tools/clippy/clippy_lints/src/utils/author.rs | 801 ++++++++++++++++++++ .../src/utils/internal_lints/author.rs | 805 --------------------- src/tools/clippy/clippy_lints/src/utils/mod.rs | 1 + 17 files changed, 1073 insertions(+), 948 deletions(-) create mode 100644 src/tools/clippy/clippy_lints/src/ctfe.rs create mode 100644 src/tools/clippy/clippy_lints/src/utils/author.rs delete mode 100644 src/tools/clippy/clippy_lints/src/utils/internal_lints/author.rs (limited to 'compiler') diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index fd850d2f39a..608d66184f0 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -972,7 +972,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { tcx.ensure().check_mod_privacy(module); }); }); - } + } // { sess.time("mir_checking", || { tcx.hir().mir_for }) } ); // This check has to be run after all lints are done processing. We don't diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index acd12f7f20d..33c87bbfb70 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -20,6 +20,7 @@ //! If you define a new `LateLintPass`, you will also need to add it to the //! `late_lint_methods!` invocation in `lib.rs`. +use std::default::Default; use std::fmt::Write; use ast::token::TokenKind; @@ -73,12 +74,10 @@ use crate::{ EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, fluent_generated as fluent, }; - -use std::default::Default; -use std::fmt::Write; +// use std::fmt::Write; // hardwired lints from rustc_lint_defs -pub use rustc_session::lint::builtin::*; +// pub use rustc_session::lint::builtin::*; declare_lint! { /// The `while_true` lint detects `while true { }`. diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index c7187ee1b30..f8bd873cdf5 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -24,13 +24,12 @@ use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::{HirId, intravisit as hir_visit}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::Session; -use rustc_session::lint::LintPass; +use rustc_session::{Session, lint::{LintPass, builtin::HardwiredLints}}; use rustc_span::Span; use tracing::debug; use crate::passes::LateLintPassObject; -use crate::{LateContext, LateLintPass, LintStore}; +use crate::{LateContext, LateLintPass, LintId, LintStore}; /// Extract the [`LintStore`] from [`Session`]. /// @@ -371,29 +370,24 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( } else { let passes: Vec<_> = store.late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); - // Filter unused lints - let (lints_that_actually_run, lints_allowed) = &**tcx.lints_that_can_emit(()); - // let lints_that_actually_run = &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 lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(()); let mut filtered_passes: Vec>> = 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_that_actually_run.contains(&lint_name) - || (!lints_allowed.contains(&lint_name) - && lint.default_level != crate::Level::Allow) - }) + let lints = LintPass::get_lints(pass); + if lints.is_empty() { + true + } else { + lints + .iter() + .any(|lint| !lints_that_dont_need_to_run.contains(&LintId::of(lint))) + } }) .collect(); filtered_passes.push(Box::new(builtin_lints)); + filtered_passes.push(Box::new(HardwiredLints)); let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] }; late_lint_mod_inner(tcx, module_def_id, context, pass); @@ -426,7 +420,7 @@ fn late_lint_mod_inner<'tcx, T: LateLintPass<'tcx>>( fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { // Note: `passes` is often empty. - let mut passes: Vec<_> = + let passes: Vec<_> = unerased_lint_store(tcx.sess).late_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); if passes.is_empty() { @@ -444,7 +438,30 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { only_module: false, }; - let pass = RuntimeCombinedLateLintPass { passes: &mut passes[..] }; + let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(()); + + // dbg!(&lints_that_dont_need_to_run); + let mut filtered_passes: Vec>> = passes + .into_iter() + .filter(|pass| { + let lints = LintPass::get_lints(pass); + !lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint))) + }) + .collect(); + + filtered_passes.push(Box::new(HardwiredLints)); + + // let mut filtered_passes: Vec>> = passes + // .into_iter() + // .filter(|pass| { + // let lints = LintPass::get_lints(pass); + // lints.iter() + // .any(|lint| + // !lints_that_dont_need_to_run.contains(&LintId::of(lint))) + // }).collect(); + // + + let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] }; late_lint_crate_inner(tcx, context, pass); } @@ -482,10 +499,3 @@ 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 527fc117351..f5323c295d0 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -1,5 +1,5 @@ use rustc_ast_pretty::pprust; -use rustc_data_structures::{fx::FxIndexMap, fx::FxIndexSet, sync::Lrc}; +use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_feature::{Features, GateIssue}; use rustc_hir::HirId; @@ -31,7 +31,7 @@ use crate::errors::{ OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup, }; use crate::fluent_generated as fluent; -use crate::late::{unerased_lint_store, name_without_tool}; +use crate::late::{unerased_lint_store /*name_without_tool*/}; use crate::lints::{ DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified, OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint, @@ -115,34 +115,41 @@ 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<(FxIndexSet, FxIndexSet)> { - let mut visitor = LintLevelMinimum::new(tcx); - visitor.process_opts(); - tcx.hir().walk_attributes(&mut visitor); - +fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { 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_that_actually_run.contains(&group_name) { - for lint in group.1 { - visitor.lints_that_actually_run.insert(name_without_tool(&lint.to_string()).to_string()); - } - } else if visitor.lints_allowed.contains(&group_name) { - for lint in &group.1 { - visitor.lints_allowed.insert(name_without_tool(&lint.to_string()).to_string()); + let dont_need_to_run: FxIndexSet = store + .get_lints() + .into_iter() + .filter_map(|lint| { + if !lint.loadbearing && lint.default_level(tcx.sess.edition()) == Level::Allow { + Some(LintId::of(lint)) + } else { + None } - } - } + }) + .collect(); - Lrc::new((visitor.lints_that_actually_run, visitor.lints_allowed)) + let mut visitor = LintLevelMaximum { tcx, dont_need_to_run }; + visitor.process_opts(); + tcx.hir().walk_attributes(&mut visitor); + + // 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_that_actually_run.contains(&group_name) { + // for lint in group.1 { + // visitor.lints_that_actually_run.insert(name_without_tool(&lint.to_string()).to_string()); + // } + // } else if visitor.lints_allowed.contains(&group_name) { + // for lint in &group.1 { + // visitor.lints_allowed.insert(name_without_tool(&lint.to_string()).to_string()); + // } + // } + // } + + visitor.dont_need_to_run } #[instrument(level = "trace", skip(tcx), ret)] @@ -336,39 +343,29 @@ impl<'tcx> Visitor<'tcx> for LintLevelsBuilder<'_, LintLevelQueryMap<'tcx>> { /// /// 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> { +struct LintLevelMaximum<'tcx> { tcx: TyCtxt<'tcx>, /// The actual list of detected lints. - lints_that_actually_run: FxIndexSet, - lints_allowed: FxIndexSet, + dont_need_to_run: FxIndexSet, } -impl<'tcx> LintLevelMinimum<'tcx> { - pub fn new(tcx: TyCtxt<'tcx>) -> Self { - let mut lints_that_actually_run = FxIndexSet::default(); - lints_that_actually_run.reserve(230); - let mut lints_allowed = FxIndexSet::default(); - lints_allowed.reserve(100); - Self { - tcx, - // That magic number is the current number of lints + some more for possible future lints - lints_that_actually_run, - lints_allowed, - } - } - +impl<'tcx> LintLevelMaximum<'tcx> { fn process_opts(&mut self) { - for (lint, level) in &self.tcx.sess.opts.lint_opts { - if *level == Level::Allow { - self.lints_allowed.insert(lint.clone()); - } else { - self.lints_that_actually_run.insert(lint.to_string()); + let store = unerased_lint_store(self.tcx.sess); + for (lint_group, level) in &self.tcx.sess.opts.lint_opts { + if *level != Level::Allow { + let Ok(lints) = store.find_lints(lint_group) else { + return; + }; + for lint in lints { + self.dont_need_to_run.swap_remove(&lint); + } } } } } -impl<'tcx> Visitor<'tcx> for LintLevelMinimum<'tcx> { +impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { type NestedFilter = nested_filter::All; fn nested_visit_map(&mut self) -> Self::Map { @@ -376,42 +373,82 @@ impl<'tcx> Visitor<'tcx> for LintLevelMinimum<'tcx> { } 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)) - { + match Level::from_attr(attribute) { + Some( + Level::Warn + | Level::Deny + | Level::Forbid + | Level::Expect(..) + | Level::ForceWarn(..), + ) => { + let store = unerased_lint_store(self.tcx.sess); + let Some(meta) = attribute.meta() else { return }; // 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_that_actually_run.insert( - // SAFETY: Lint attributes can only have literals - meta_list.ident().unwrap().name.as_str().to_string(), - ); - } else { - self.lints_that_actually_run - .insert(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.insert(meta_list.name_or_empty().as_str().to_string()); - } else { - self.lints_allowed - .insert(meta_item.path.segments[1].ident.name.as_str().to_string()); - } + let Some(meta_item_list) = meta.meta_item_list() else { return }; + + for meta_list in meta_item_list { + // Convert Path to String + let Some(meta_item) = meta_list.meta_item() else { return }; + let ident: &str = &meta_item + .path + .segments + .iter() + .map(|segment| segment.ident.as_str()) + .collect::>() + .join("::"); + let Ok(lints) = store.find_lints( + // SAFETY: Lint attributes can only have literals + ident, + ) else { + return; + }; + for lint in lints { + self.dont_need_to_run.swap_remove(&lint); } + // // 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 { + // let Ok(lints) = store.find_lints( + // // SAFETY: Lint attributes can only have literals + // meta_list.ident().unwrap().name.as_str(), + // ) else { + // return; + // }; + // for lint in lints { + // dbg!("LINT REMOVED", &lint); + // self.dont_need_to_run.swap_remove(&lint); + // } + // } else { + // let Ok(lints) = store.find_lints( + // // SAFETY: Lint attributes can only have literals + // meta_item.path.segments[1].ident.name.as_str(), + // ) else { + // return; + // }; + // for lint in lints { + // dbg!("LINT REMOVED", &lint); + // self.dont_need_to_run.swap_remove(&lint); + // } + // } } + // We handle #![allow]s differently, as these remove checking rather than adding. + } // Some(Level::Allow) if 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 + // .insert(meta_list.name_or_empty().as_str().to_string()); + // } else { + // self.lints_allowed + // .insert(meta_item.path.segments[1].ident.name.as_str().to_string()); + // } + // } + // } + // } + _ => { + return; } } } @@ -1047,8 +1084,7 @@ impl<'s, P: LintLevelsProvider> LintLevelsBuilder<'s, P> { } pub(crate) fn provide(providers: &mut Providers) { - *providers = - Providers { shallow_lint_levels_on, lints_that_can_emit, ..*providers }; + *providers = Providers { shallow_lint_levels_on, lints_that_dont_need_to_run, ..*providers }; } pub(crate) fn parse_lint_and_tool_name(lint_name: &str) -> (Option, &str) { diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 5984810961f..11dd17bcd4a 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -199,7 +199,6 @@ late_lint_methods!( ForLoopsOverFallibles: ForLoopsOverFallibles, DerefIntoDynSupertrait: DerefIntoDynSupertrait, DropForgetUseless: DropForgetUseless, - HardwiredLints: HardwiredLints, ImproperCTypesDeclarations: ImproperCTypesDeclarations, ImproperCTypesDefinitions: ImproperCTypesDefinitions, InvalidFromUtf8: InvalidFromUtf8, @@ -280,6 +279,7 @@ fn register_builtins(store: &mut LintStore) { store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); store.register_lints(&foreign_modules::get_lints()); + store.register_lints(&HardwiredLints::default().get_lints()); add_lint_group!( "nonstandard_style", diff --git a/compiler/rustc_lint/src/passes.rs b/compiler/rustc_lint/src/passes.rs index 3750f90a044..6dbcdefe08d 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -70,7 +70,18 @@ macro_rules! declare_late_lint_pass { // for all the `check_*` methods. late_lint_methods!(declare_late_lint_pass, []); -impl LateLintPass<'_> for HardwiredLints {} +impl LateLintPass<'_> for HardwiredLints { + fn check_fn( + &mut self, + _: &LateContext<'_>, + _: rustc_hir::intravisit::FnKind<'_>, + _: &'_ rustc_hir::FnDecl<'_>, + _: &'_ rustc_hir::Body<'_>, + _: rustc_span::Span, + _: rustc_span::def_id::LocalDefId, + ) { + } +} #[macro_export] macro_rules! expand_combined_late_lint_pass_method { diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index a05616bf486..a7ac847c018 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1026,7 +1026,7 @@ pub(crate) struct UnusedParens { } impl Default for UnusedParens { - fpub(crate) fn default() -> Self { + fn default() -> Self { Self { with_self_ty_parens: false, parens_in_cast_in_lt: Vec::new() } } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index 5c3b1bb71f3..df86e9de22e 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -12,8 +12,6 @@ use rustc_span::edition::Edition; use crate::{FutureIncompatibilityReason, declare_lint, declare_lint_pass}; declare_lint_pass! { - /// Does nothing as a lint pass, but registers some `Lint`s - /// that are used by other parts of the compiler. HardwiredLints => [ // tidy-alphabetical-start ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, @@ -403,7 +401,8 @@ declare_lint! { /// `panic!` or `unreachable!` macro instead in case the panic is intended. pub UNCONDITIONAL_PANIC, Deny, - "operation will cause a panic at runtime" + "operation will cause a panic at runtime", + [loadbearing: true] } declare_lint! { diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index b6cb11a2a2c..99e166286d2 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -29,6 +29,7 @@ use rustc_hir::def_id::{ use rustc_hir::lang_items::{LangItem, LanguageItems}; use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate}; use rustc_index::IndexVec; +use rustc_lint_defs::LintId; use rustc_macros::rustc_queries; use rustc_query_system::ich::StableHashingContext; use rustc_query_system::query::{QueryCache, QueryMode, QueryState, try_get_cached}; @@ -422,7 +423,7 @@ rustc_queries! { desc { "computing `#[expect]`ed lints in this crate" } } - query lints_that_can_emit(_: ()) -> &'tcx Lrc<(FxIndexSet, FxIndexSet)> { + query lints_that_dont_need_to_run(_: ()) -> &'tcx FxIndexSet { arena_cache desc { "Computing all lints that are explicitly enabled or with a default level greater than Allow" } } diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index 27879d817b2..c8f46226a3e 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -187,6 +187,7 @@ pub struct Session { /// errors. pub ctfe_backtrace: Lock, + // pub force_ctfe: bool, /// This tracks where `-Zunleash-the-miri-inside-of-you` was used to get around a /// const check, optionally with the relevant feature gate. We use this to /// warn about unleashing, but with a single diagnostic instead of dozens that diff --git a/src/tools/clippy/clippy_lints/src/asm_syntax.rs b/src/tools/clippy/clippy_lints/src/asm_syntax.rs index 9f3c24a9e80..69a8eb7d94e 100644 --- a/src/tools/clippy/clippy_lints/src/asm_syntax.rs +++ b/src/tools/clippy/clippy_lints/src/asm_syntax.rs @@ -3,7 +3,7 @@ use std::fmt; use clippy_utils::diagnostics::span_lint_and_then; use rustc_ast::ast::{Expr, ExprKind, InlineAsmOptions}; use rustc_ast::{InlineAsm, Item, ItemKind}; -use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintPass, LintContext}; +use rustc_lint::{EarlyContext, EarlyLintPass, Lint, LintContext}; use rustc_session::declare_lint_pass; use rustc_span::Span; use rustc_target::asm::InlineAsmArch; diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 495d8ce3fa7..b027c289a7f 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -8,30 +8,44 @@ use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Expr, ExprKind, FnDecl}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_lint::Level::Allow; +use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; -declare_clippy_lint! { - /// ### What it does - /// Checks for methods with high cognitive complexity. - /// - /// ### Why is this bad? - /// Methods of high cognitive complexity tend to be hard to - /// both read and maintain. Also LLVM will tend to optimize small methods better. - /// - /// ### Known problems - /// Sometimes it's hard to find a way to reduce the - /// complexity. - /// - /// ### Example - /// You'll see it when you get the warning. - #[clippy::version = "1.35.0"] - pub COGNITIVE_COMPLEXITY, - nursery, - "functions that should be split up into multiple functions" -} +use crate::LintInfo; + +pub static COGNITIVE_COMPLEXITY: &Lint = &Lint { + name: &"clippy::COGNITIVE_COMPLEXITY", + default_level: Allow, + desc: "functions that should be split up into multiple functions", + edition_lint_opts: None, + report_in_external_macro: true, + future_incompatible: None, + is_externally_loaded: true, + crate_level_only: false, + loadbearing: true, + ..Lint::default_fields_for_macro() +}; +pub(crate) static COGNITIVE_COMPLEXITY_INFO: &'static LintInfo = &LintInfo { + lint: &COGNITIVE_COMPLEXITY, + category: crate::LintCategory::Nursery, + explanation: r"### What it does +Checks for methods with high cognitive complexity. + +### Why is this bad? +Methods of high cognitive complexity tend to be hard to both read and maintain. +Also LLVM will tend to optimize small methods better. + +### Known problems +Sometimes it's hard to find a way to reduce the complexity. + +### Example +You'll see it when you get the warning.", + version: Some("1.35.0"), + location: "#L0", +}; pub struct CognitiveComplexity { limit: LimitStack, diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs new file mode 100644 index 00000000000..7b9f71810a9 --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/ctfe.rs @@ -0,0 +1,54 @@ +use rustc_hir::def_id::LocalDefId; +use rustc_hir::intravisit::FnKind; +use rustc_hir::{Body, FnDecl}; +use rustc_lint::{LateContext, LateLintPass}; +use rustc_session::declare_lint_pass; +use rustc_span::Span; + +declare_clippy_lint! { + /// ### What it does + /// Checks for comparisons where one side of the relation is + /// either the minimum or maximum value for its type and warns if it involves a + /// case that is always true or always false. Only integer and boolean types are + /// checked. + /// + /// ### Why is this bad? + /// An expression like `min <= x` may misleadingly imply + /// that it is possible for `x` to be less than the minimum. Expressions like + /// `max < x` are probably mistakes. + /// + /// ### Known problems + /// For `usize` the size of the current compile target will + /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such + /// a comparison to detect target pointer width will trigger this lint. One can + /// use `mem::sizeof` and compare its value or conditional compilation + /// attributes + /// like `#[cfg(target_pointer_width = "64")] ..` instead. + /// + /// ### Example + /// ```no_run + /// let vec: Vec = Vec::new(); + /// if vec.len() <= 0 {} + /// if 100 > i32::MAX {} + /// ``` + #[clippy::version = "1.82.0"] + pub CLIPPY_CTFE, + correctness, + "a comparison with a maximum or minimum value that is always true or false" +} + +declare_lint_pass! { ClippyCtfe => [CLIPPY_CTFE] } + +impl<'tcx> LateLintPass<'tcx> for ClippyCtfe { + fn check_fn( + &mut self, + cx: &LateContext<'_>, + _: FnKind<'tcx>, + _: &'tcx FnDecl<'tcx>, + _: &'tcx Body<'tcx>, + _: Span, + defid: LocalDefId, + ) { + cx.tcx.ensure().mir_drops_elaborated_and_const_checked(defid); // Lint + } +} diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 6e29dde2211..a5d2f6a4122 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -65,6 +65,7 @@ extern crate clippy_utils; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; +pub mod ctfe; // VERY important lint (rust#125116) pub mod declared_lints; pub mod deprecated_lints; @@ -605,6 +606,8 @@ pub fn register_lints(store: &mut rustc_lint::LintStore, conf: &'static Conf) { }); } + store.register_late_pass(|_| Box::new(ctfe::ClippyCtfe)); + store.register_late_pass(move |_| Box::new(operators::arithmetic_side_effects::ArithmeticSideEffects::new(conf))); store.register_late_pass(|_| Box::new(utils::dump_hir::DumpHir)); store.register_late_pass(|_| Box::new(utils::author::Author)); diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs new file mode 100644 index 00000000000..f4e166327af --- /dev/null +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -0,0 +1,801 @@ +use clippy_utils::{get_attr, higher}; +use rustc_ast::LitIntType; +use rustc_ast::ast::{LitFloatType, LitKind}; +use rustc_data_structures::fx::FxHashMap; +use rustc_hir::{ + self as hir, ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, + ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, +}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; +use rustc_session::declare_lint_pass; +use rustc_span::symbol::{Ident, Symbol}; +use std::cell::Cell; +use std::fmt::{Display, Formatter, Write as _}; + +declare_lint_pass!( + /// ### What it does + /// Generates clippy code that detects the offending pattern + /// + /// ### Example + /// ```rust,ignore + /// // ./tests/ui/my_lint.rs + /// fn foo() { + /// // detect the following pattern + /// #[clippy::author] + /// if x == 42 { + /// // but ignore everything from here on + /// #![clippy::author = "ignore"] + /// } + /// () + /// } + /// ``` + /// + /// Running `TESTNAME=ui/my_lint cargo uitest` will produce + /// a `./tests/ui/new_lint.stdout` file with the generated code: + /// + /// ```rust,ignore + /// // ./tests/ui/new_lint.stdout + /// if ExprKind::If(ref cond, ref then, None) = item.kind + /// && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind + /// && let ExprKind::Path(ref path) = left.kind + /// && let ExprKind::Lit(ref lit) = right.kind + /// && let LitKind::Int(42, _) = lit.node + /// { + /// // report your lint here + /// } + /// ``` + Author => [] +); + +/// Writes a line of output with indentation added +macro_rules! out { + ($($t:tt)*) => { + println!(" {}", format_args!($($t)*)) + }; +} + +/// The variables passed in are replaced with `&Binding`s where the `value` field is set +/// to the original value of the variable. The `name` field is set to the name of the variable +/// (using `stringify!`) and is adjusted to avoid duplicate names. +/// Note that the `Binding` may be printed directly to output the `name`. +macro_rules! bind { + ($self:ident $(, $name:ident)+) => { + $(let $name = & $self.bind(stringify!($name), $name);)+ + }; +} + +/// Transforms the given `Option` variables into `OptionPat>`. +/// This displays as `Some($name)` or `None` when printed. The name of the inner binding +/// is set to the name of the variable passed to the macro. +macro_rules! opt_bind { + ($self:ident $(, $name:ident)+) => { + $(let $name = OptionPat::new($name.map(|o| $self.bind(stringify!($name), o)));)+ + }; +} + +/// Creates a `Binding` that accesses the field of an existing `Binding` +macro_rules! field { + ($binding:ident.$field:ident) => { + &Binding { + name: $binding.name.to_string() + stringify!(.$field), + value: $binding.value.$field, + } + }; +} + +/// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print +/// `if let Some(x) = y` on the first call and ` && let Some(x) = y` thereafter +macro_rules! chain { + ($self:ident, $($t:tt)*) => { + if $self.first.take() { + println!("if {}", format_args!($($t)*)); + } else { + println!(" && {}", format_args!($($t)*)); + } + } +} + +impl<'tcx> LateLintPass<'tcx> for Author { + fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { + check_item(cx, item.hir_id()); + } + + fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { + check_item(cx, item.hir_id()); + } + + fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { + check_item(cx, item.hir_id()); + } + + fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { + check_node(cx, arm.hir_id, |v| { + v.arm(&v.bind("arm", arm)); + }); + } + + fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { + check_node(cx, expr.hir_id, |v| { + v.expr(&v.bind("expr", expr)); + }); + } + + fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { + match stmt.kind { + StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return, + _ => {}, + } + check_node(cx, stmt.hir_id, |v| { + v.stmt(&v.bind("stmt", stmt)); + }); + } +} + +fn check_item(cx: &LateContext<'_>, hir_id: HirId) { + let hir = cx.tcx.hir(); + if let Some(body) = hir.maybe_body_owned_by(hir_id.expect_owner().def_id) { + check_node(cx, hir_id, |v| { + v.expr(&v.bind("expr", body.value)); + }); + } +} + +fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { + if has_attr(cx, hir_id) { + f(&PrintVisitor::new(cx)); + println!("{{"); + println!(" // report your lint here"); + println!("}}"); + } +} + +struct Binding { + name: String, + value: T, +} + +impl Display for Binding { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.name) + } +} + +struct OptionPat { + pub opt: Option, +} + +impl OptionPat { + fn new(opt: Option) -> Self { + Self { opt } + } + + fn if_some(&self, f: impl Fn(&T)) { + if let Some(t) = &self.opt { + f(t); + } + } +} + +impl Display for OptionPat { + fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { + match &self.opt { + None => f.write_str("None"), + Some(node) => write!(f, "Some({node})"), + } + } +} + +struct PrintVisitor<'a, 'tcx> { + cx: &'a LateContext<'tcx>, + /// Fields are the current index that needs to be appended to pattern + /// binding names + ids: Cell>, + /// Currently at the first condition in the if chain + first: Cell, +} + +#[allow(clippy::unused_self)] +impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { + fn new(cx: &'a LateContext<'tcx>) -> Self { + Self { + cx, + ids: Cell::default(), + first: Cell::new(true), + } + } + + fn next(&self, s: &'static str) -> String { + let mut ids = self.ids.take(); + let out = match *ids.entry(s).and_modify(|n| *n += 1).or_default() { + // first usage of the name, use it as is + 0 => s.to_string(), + // append a number starting with 1 + n => format!("{s}{n}"), + }; + self.ids.set(ids); + out + } + + fn bind(&self, name: &'static str, value: T) -> Binding { + let name = self.next(name); + Binding { name, value } + } + + fn option(&self, option: &Binding>, name: &'static str, f: impl Fn(&Binding)) { + match option.value { + None => chain!(self, "{option}.is_none()"), + Some(value) => { + let value = &self.bind(name, value); + chain!(self, "let Some({value}) = {option}"); + f(value); + }, + } + } + + fn slice(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) { + if slice.value.is_empty() { + chain!(self, "{slice}.is_empty()"); + } else { + chain!(self, "{slice}.len() == {}", slice.value.len()); + for (i, value) in slice.value.iter().enumerate() { + let name = format!("{slice}[{i}]"); + f(&Binding { name, value }); + } + } + } + + fn destination(&self, destination: &Binding) { + self.option(field!(destination.label), "label", |label| { + self.ident(field!(label.ident)); + }); + } + + fn ident(&self, ident: &Binding) { + chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str()); + } + + fn symbol(&self, symbol: &Binding) { + chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); + } + + fn qpath(&self, qpath: &Binding<&QPath<'_>>) { + if let QPath::LangItem(lang_item, ..) = *qpath.value { + chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); + } else if let Ok(path) = path_to_string(qpath.value) { + chain!(self, "match_qpath({qpath}, &[{}])", path); + } + } + + fn const_arg(&self, const_arg: &Binding<&ConstArg<'_>>) { + match const_arg.value.kind { + ConstArgKind::Path(ref qpath) => { + bind!(self, qpath); + chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind"); + self.qpath(qpath); + }, + ConstArgKind::Anon(anon_const) => { + bind!(self, anon_const); + chain!(self, "let ConstArgKind::Anon({anon_const}) = {const_arg}.kind"); + self.body(field!(anon_const.body)); + }, + } + } + + fn lit(&self, lit: &Binding<&Lit>) { + let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + + match lit.value.node { + LitKind::Bool(val) => kind!("Bool({val:?})"), + LitKind::Char(c) => kind!("Char({c:?})"), + LitKind::Err(_) => kind!("Err"), + LitKind::Byte(b) => kind!("Byte({b})"), + LitKind::Int(i, suffix) => { + let int_ty = match suffix { + LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{int_ty:?})"), + LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{uint_ty:?})"), + LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"), + }; + kind!("Int({i}, {int_ty})"); + }, + LitKind::Float(_, suffix) => { + let float_ty = match suffix { + LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{suffix_ty:?})"), + LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"), + }; + kind!("Float(_, {float_ty})"); + }, + LitKind::ByteStr(ref vec, _) => { + bind!(self, vec); + kind!("ByteStr(ref {vec})"); + chain!(self, "let [{:?}] = **{vec}", vec.value); + }, + LitKind::CStr(ref vec, _) => { + bind!(self, vec); + kind!("CStr(ref {vec})"); + chain!(self, "let [{:?}] = **{vec}", vec.value); + }, + LitKind::Str(s, _) => { + bind!(self, s); + kind!("Str({s}, _)"); + self.symbol(s); + }, + } + } + + fn arm(&self, arm: &Binding<&hir::Arm<'_>>) { + self.pat(field!(arm.pat)); + match arm.value.guard { + None => chain!(self, "{arm}.guard.is_none()"), + Some(expr) => { + bind!(self, expr); + chain!(self, "let Some({expr}) = {arm}.guard"); + self.expr(expr); + }, + } + self.expr(field!(arm.body)); + } + + #[allow(clippy::too_many_lines)] + fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { + if let Some(higher::While { condition, body, .. }) = higher::While::hir(expr.value) { + bind!(self, condition, body); + chain!( + self, + "let Some(higher::While {{ condition: {condition}, body: {body} }}) \ + = higher::While::hir({expr})" + ); + self.expr(condition); + self.expr(body); + return; + } + + if let Some(higher::WhileLet { + let_pat, + let_expr, + if_then, + .. + }) = higher::WhileLet::hir(expr.value) + { + bind!(self, let_pat, let_expr, if_then); + chain!( + self, + "let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ + = higher::WhileLet::hir({expr})" + ); + self.pat(let_pat); + self.expr(let_expr); + self.expr(if_then); + return; + } + + if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) { + bind!(self, pat, arg, body); + chain!( + self, + "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ + = higher::ForLoop::hir({expr})" + ); + self.pat(pat); + self.expr(arg); + self.expr(body); + return; + } + + let kind = |kind| chain!(self, "let ExprKind::{kind} = {expr}.kind"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + + match expr.value.kind { + ExprKind::Let(let_expr) => { + bind!(self, let_expr); + kind!("Let({let_expr})"); + self.pat(field!(let_expr.pat)); + // Does what ExprKind::Cast does, only adds a clause for the type + // if it's a path + if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { + bind!(self, qpath); + chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); + self.qpath(qpath); + } + self.expr(field!(let_expr.init)); + }, + ExprKind::Array(elements) => { + bind!(self, elements); + kind!("Array({elements})"); + self.slice(elements, |e| self.expr(e)); + }, + ExprKind::Call(func, args) => { + bind!(self, func, args); + kind!("Call({func}, {args})"); + self.expr(func); + self.slice(args, |e| self.expr(e)); + }, + ExprKind::MethodCall(method_name, receiver, args, _) => { + bind!(self, method_name, receiver, args); + kind!("MethodCall({method_name}, {receiver}, {args}, _)"); + self.ident(field!(method_name.ident)); + self.expr(receiver); + self.slice(args, |e| self.expr(e)); + }, + ExprKind::Tup(elements) => { + bind!(self, elements); + kind!("Tup({elements})"); + self.slice(elements, |e| self.expr(e)); + }, + ExprKind::Binary(op, left, right) => { + bind!(self, op, left, right); + kind!("Binary({op}, {left}, {right})"); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); + self.expr(left); + self.expr(right); + }, + ExprKind::Unary(op, inner) => { + bind!(self, inner); + kind!("Unary(UnOp::{op:?}, {inner})"); + self.expr(inner); + }, + ExprKind::Lit(lit) => { + bind!(self, lit); + kind!("Lit(ref {lit})"); + self.lit(lit); + }, + ExprKind::Cast(expr, cast_ty) => { + bind!(self, expr, cast_ty); + kind!("Cast({expr}, {cast_ty})"); + if let TyKind::Path(ref qpath) = cast_ty.value.kind { + bind!(self, qpath); + chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); + self.qpath(qpath); + } + self.expr(expr); + }, + ExprKind::Type(expr, _ty) => { + bind!(self, expr); + kind!("Type({expr}, _)"); + self.expr(expr); + }, + ExprKind::Loop(body, label, des, _) => { + bind!(self, body); + opt_bind!(self, label); + kind!("Loop({body}, {label}, LoopSource::{des:?}, _)"); + self.block(body); + label.if_some(|l| self.ident(field!(l.ident))); + }, + ExprKind::If(cond, then, else_expr) => { + bind!(self, cond, then); + opt_bind!(self, else_expr); + kind!("If({cond}, {then}, {else_expr})"); + self.expr(cond); + self.expr(then); + else_expr.if_some(|e| self.expr(e)); + }, + ExprKind::Match(scrutinee, arms, des) => { + bind!(self, scrutinee, arms); + kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})"); + self.expr(scrutinee); + self.slice(arms, |arm| self.arm(arm)); + }, + ExprKind::Closure(&Closure { + capture_clause, + fn_decl, + body: body_id, + kind, + .. + }) => { + let capture_clause = match capture_clause { + CaptureBy::Value { .. } => "Value { .. }", + CaptureBy::Ref => "Ref", + }; + + let closure_kind = match kind { + ClosureKind::Closure => "ClosureKind::Closure".to_string(), + ClosureKind::Coroutine(coroutine_kind) => match coroutine_kind { + CoroutineKind::Desugared(desugaring, source) => format!( + "ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::{desugaring:?}, CoroutineSource::{source:?}))" + ), + CoroutineKind::Coroutine(movability) => { + format!("ClosureKind::Coroutine(CoroutineKind::Coroutine(Movability::{movability:?})") + }, + }, + ClosureKind::CoroutineClosure(desugaring) => { + format!("ClosureKind::CoroutineClosure(CoroutineDesugaring::{desugaring:?})") + }, + }; + + let ret_ty = match fn_decl.output { + FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)", + FnRetTy::Return(_) => "FnRetTy::Return(_ty)", + }; + + bind!(self, fn_decl, body_id); + kind!( + "Closure {{ capture_clause: CaptureBy::{capture_clause}, fn_decl: {fn_decl}, body: {body_id}, closure_kind: {closure_kind}, .. }}" + ); + chain!(self, "let {ret_ty} = {fn_decl}.output"); + self.body(body_id); + }, + ExprKind::Yield(sub, source) => { + bind!(self, sub); + kind!("Yield(sub, YieldSource::{source:?})"); + self.expr(sub); + }, + ExprKind::Block(block, label) => { + bind!(self, block); + opt_bind!(self, label); + kind!("Block({block}, {label})"); + self.block(block); + label.if_some(|l| self.ident(field!(l.ident))); + }, + ExprKind::Assign(target, value, _) => { + bind!(self, target, value); + kind!("Assign({target}, {value}, _span)"); + self.expr(target); + self.expr(value); + }, + ExprKind::AssignOp(op, target, value) => { + bind!(self, op, target, value); + kind!("AssignOp({op}, {target}, {value})"); + chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); + self.expr(target); + self.expr(value); + }, + ExprKind::Field(object, field_name) => { + bind!(self, object, field_name); + kind!("Field({object}, {field_name})"); + self.ident(field_name); + self.expr(object); + }, + ExprKind::Index(object, index, _) => { + bind!(self, object, index); + kind!("Index({object}, {index})"); + self.expr(object); + self.expr(index); + }, + ExprKind::Path(ref qpath) => { + bind!(self, qpath); + kind!("Path(ref {qpath})"); + self.qpath(qpath); + }, + ExprKind::AddrOf(kind, mutability, inner) => { + bind!(self, inner); + kind!("AddrOf(BorrowKind::{kind:?}, Mutability::{mutability:?}, {inner})"); + self.expr(inner); + }, + ExprKind::Break(destination, value) => { + bind!(self, destination); + opt_bind!(self, value); + kind!("Break({destination}, {value})"); + self.destination(destination); + value.if_some(|e| self.expr(e)); + }, + ExprKind::Continue(destination) => { + bind!(self, destination); + kind!("Continue({destination})"); + self.destination(destination); + }, + ExprKind::Ret(value) => { + opt_bind!(self, value); + kind!("Ret({value})"); + value.if_some(|e| self.expr(e)); + }, + ExprKind::Become(value) => { + bind!(self, value); + kind!("Become({value})"); + self.expr(value); + }, + ExprKind::InlineAsm(_) => { + kind!("InlineAsm(_)"); + out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment"); + }, + ExprKind::OffsetOf(container, ref fields) => { + bind!(self, container, fields); + kind!("OffsetOf({container}, {fields})"); + }, + ExprKind::Struct(qpath, fields, base) => { + bind!(self, qpath, fields); + opt_bind!(self, base); + kind!("Struct({qpath}, {fields}, {base})"); + self.qpath(qpath); + self.slice(fields, |field| { + self.ident(field!(field.ident)); + self.expr(field!(field.expr)); + }); + base.if_some(|e| self.expr(e)); + }, + ExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), + ExprKind::Repeat(value, length) => { + bind!(self, value, length); + kind!("Repeat({value}, {length})"); + self.expr(value); + match length.value { + ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"), + ArrayLen::Body(const_arg) => { + bind!(self, const_arg); + chain!(self, "let ArrayLen::Body({const_arg}) = {length}"); + self.const_arg(const_arg); + }, + } + }, + ExprKind::Err(_) => kind!("Err(_)"), + ExprKind::DropTemps(expr) => { + bind!(self, expr); + kind!("DropTemps({expr})"); + self.expr(expr); + }, + } + } + + fn block(&self, block: &Binding<&hir::Block<'_>>) { + self.slice(field!(block.stmts), |stmt| self.stmt(stmt)); + self.option(field!(block.expr), "trailing_expr", |expr| { + self.expr(expr); + }); + } + + fn body(&self, body_id: &Binding) { + let expr = self.cx.tcx.hir().body(body_id.value).value; + bind!(self, expr); + chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value"); + self.expr(expr); + } + + fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { + let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + + match pat.value.kind { + PatKind::Wild => kind!("Wild"), + PatKind::Never => kind!("Never"), + PatKind::Binding(ann, _, name, sub) => { + bind!(self, name); + opt_bind!(self, sub); + let ann = match ann { + BindingMode::NONE => "NONE", + BindingMode::REF => "REF", + BindingMode::MUT => "MUT", + BindingMode::REF_MUT => "REF_MUT", + BindingMode::MUT_REF => "MUT_REF", + BindingMode::MUT_REF_MUT => "MUT_REF_MUT", + }; + kind!("Binding(BindingMode::{ann}, _, {name}, {sub})"); + self.ident(name); + sub.if_some(|p| self.pat(p)); + }, + PatKind::Struct(ref qpath, fields, ignore) => { + bind!(self, qpath, fields); + kind!("Struct(ref {qpath}, {fields}, {ignore})"); + self.qpath(qpath); + self.slice(fields, |field| { + self.ident(field!(field.ident)); + self.pat(field!(field.pat)); + }); + }, + PatKind::Or(fields) => { + bind!(self, fields); + kind!("Or({fields})"); + self.slice(fields, |pat| self.pat(pat)); + }, + PatKind::TupleStruct(ref qpath, fields, skip_pos) => { + bind!(self, qpath, fields); + kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); + self.qpath(qpath); + self.slice(fields, |pat| self.pat(pat)); + }, + PatKind::Path(ref qpath) => { + bind!(self, qpath); + kind!("Path(ref {qpath})"); + self.qpath(qpath); + }, + PatKind::Tuple(fields, skip_pos) => { + bind!(self, fields); + kind!("Tuple({fields}, {skip_pos:?})"); + self.slice(fields, |field| self.pat(field)); + }, + PatKind::Box(pat) => { + bind!(self, pat); + kind!("Box({pat})"); + self.pat(pat); + }, + PatKind::Deref(pat) => { + bind!(self, pat); + kind!("Deref({pat})"); + self.pat(pat); + }, + PatKind::Ref(pat, muta) => { + bind!(self, pat); + kind!("Ref({pat}, Mutability::{muta:?})"); + self.pat(pat); + }, + PatKind::Lit(lit_expr) => { + bind!(self, lit_expr); + kind!("Lit({lit_expr})"); + self.expr(lit_expr); + }, + PatKind::Range(start, end, end_kind) => { + opt_bind!(self, start, end); + kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); + start.if_some(|e| self.expr(e)); + end.if_some(|e| self.expr(e)); + }, + PatKind::Slice(start, middle, end) => { + bind!(self, start, end); + opt_bind!(self, middle); + kind!("Slice({start}, {middle}, {end})"); + middle.if_some(|p| self.pat(p)); + self.slice(start, |pat| self.pat(pat)); + self.slice(end, |pat| self.pat(pat)); + }, + PatKind::Err(_) => kind!("Err"), + } + } + + fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) { + let kind = |kind| chain!(self, "let StmtKind::{kind} = {stmt}.kind"); + macro_rules! kind { + ($($t:tt)*) => (kind(format_args!($($t)*))); + } + + match stmt.value.kind { + StmtKind::Let(local) => { + bind!(self, local); + kind!("Let({local})"); + self.option(field!(local.init), "init", |init| { + self.expr(init); + }); + self.pat(field!(local.pat)); + }, + StmtKind::Item(_) => kind!("Item(item_id)"), + StmtKind::Expr(e) => { + bind!(self, e); + kind!("Expr({e})"); + self.expr(e); + }, + StmtKind::Semi(e) => { + bind!(self, e); + kind!("Semi({e})"); + self.expr(e); + }, + } + } +} + +fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { + let attrs = cx.tcx.hir().attrs(hir_id); + get_attr(cx.sess(), attrs, "author").count() > 0 +} + +fn path_to_string(path: &QPath<'_>) -> Result { + fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> { + match *path { + QPath::Resolved(_, path) => { + for (i, segment) in path.segments.iter().enumerate() { + if i > 0 { + *s += ", "; + } + write!(s, "{:?}", segment.ident.as_str()).unwrap(); + } + }, + QPath::TypeRelative(ty, segment) => match &ty.kind { + TyKind::Path(inner_path) => { + inner(s, inner_path)?; + *s += ", "; + write!(s, "{:?}", segment.ident.as_str()).unwrap(); + }, + other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), + }, + QPath::LangItem(..) => return Err(()), + } + + Ok(()) + } + let mut s = String::new(); + inner(&mut s, path)?; + Ok(s) +} + diff --git a/src/tools/clippy/clippy_lints/src/utils/internal_lints/author.rs b/src/tools/clippy/clippy_lints/src/utils/internal_lints/author.rs deleted file mode 100644 index 0ed606a836e..00000000000 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/author.rs +++ /dev/null @@ -1,805 +0,0 @@ -use clippy_utils::{get_attr, higher}; -use rustc_ast::LitIntType; -use rustc_ast::ast::{LitFloatType, LitKind}; -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::{ - self as hir, ArrayLen, BindingMode, CaptureBy, Closure, ClosureKind, ConstArg, ConstArgKind, CoroutineKind, - ExprKind, FnRetTy, HirId, Lit, PatKind, QPath, StmtKind, TyKind, -}; -use rustc_lint::{LateContext, LateLintPass, LintContext}; -use rustc_session::declare_lint_pass; -use rustc_span::symbol::{Ident, Symbol}; -use std::cell::Cell; -use std::fmt::{Display, Formatter, Write as _}; - -declare_clippy_lint!{ - /// ### What it does - /// Generates clippy code that detects the offending pattern - /// - /// ### Example - /// ```rust,ignore - /// // ./tests/ui/my_lint.rs - /// fn foo() { - /// // detect the following pattern - /// #[clippy::author] - /// if x == 42 { - /// // but ignore everything from here on - /// #![clippy::author = "ignore"] - /// } - /// () - /// } - /// ``` - /// - /// Running `TESTNAME=ui/my_lint cargo uitest` will produce - /// a `./tests/ui/new_lint.stdout` file with the generated code: - /// - /// ```rust,ignore - /// // ./tests/ui/new_lint.stdout - /// if ExprKind::If(ref cond, ref then, None) = item.kind - /// && let ExprKind::Binary(BinOp::Eq, ref left, ref right) = cond.kind - /// && let ExprKind::Path(ref path) = left.kind - /// && let ExprKind::Lit(ref lit) = right.kind - /// && let LitKind::Int(42, _) = lit.node - /// { - /// // report your lint here - /// } - /// ``` - #[clippy::version = "1.0.0"] - pub AUTHOR, - internal, - "The author lint, see documentation at " -}; - -declare_lint_pass! { Author => [AUTHOR] } - -/// Writes a line of output with indentation added -macro_rules! out { - ($($t:tt)*) => { - println!(" {}", format_args!($($t)*)) - }; -} - -/// The variables passed in are replaced with `&Binding`s where the `value` field is set -/// to the original value of the variable. The `name` field is set to the name of the variable -/// (using `stringify!`) and is adjusted to avoid duplicate names. -/// Note that the `Binding` may be printed directly to output the `name`. -macro_rules! bind { - ($self:ident $(, $name:ident)+) => { - $(let $name = & $self.bind(stringify!($name), $name);)+ - }; -} - -/// Transforms the given `Option` variables into `OptionPat>`. -/// This displays as `Some($name)` or `None` when printed. The name of the inner binding -/// is set to the name of the variable passed to the macro. -macro_rules! opt_bind { - ($self:ident $(, $name:ident)+) => { - $(let $name = OptionPat::new($name.map(|o| $self.bind(stringify!($name), o)));)+ - }; -} - -/// Creates a `Binding` that accesses the field of an existing `Binding` -macro_rules! field { - ($binding:ident.$field:ident) => { - &Binding { - name: $binding.name.to_string() + stringify!(.$field), - value: $binding.value.$field, - } - }; -} - -/// Print a condition of a let chain, `chain!(self, "let Some(x) = y")` will print -/// `if let Some(x) = y` on the first call and ` && let Some(x) = y` thereafter -macro_rules! chain { - ($self:ident, $($t:tt)*) => { - if $self.first.take() { - println!("if {}", format_args!($($t)*)); - } else { - println!(" && {}", format_args!($($t)*)); - } - } -} - -impl<'tcx> LateLintPass<'tcx> for Author { - fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::Item<'_>) { - check_item(cx, item.hir_id()); - } - - fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::ImplItem<'_>) { - check_item(cx, item.hir_id()); - } - - fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx hir::TraitItem<'_>) { - check_item(cx, item.hir_id()); - } - - fn check_arm(&mut self, cx: &LateContext<'tcx>, arm: &'tcx hir::Arm<'_>) { - check_node(cx, arm.hir_id, |v| { - v.arm(&v.bind("arm", arm)); - }); - } - - fn check_expr(&mut self, cx: &LateContext<'tcx>, expr: &'tcx hir::Expr<'_>) { - check_node(cx, expr.hir_id, |v| { - v.expr(&v.bind("expr", expr)); - }); - } - - fn check_stmt(&mut self, cx: &LateContext<'tcx>, stmt: &'tcx hir::Stmt<'_>) { - match stmt.kind { - StmtKind::Expr(e) | StmtKind::Semi(e) if has_attr(cx, e.hir_id) => return, - _ => {}, - } - check_node(cx, stmt.hir_id, |v| { - v.stmt(&v.bind("stmt", stmt)); - }); - } -} - -fn check_item(cx: &LateContext<'_>, hir_id: HirId) { - let hir = cx.tcx.hir(); - if let Some(body) = hir.maybe_body_owned_by(hir_id.expect_owner().def_id) { - check_node(cx, hir_id, |v| { - v.expr(&v.bind("expr", body.value)); - }); - } -} - -fn check_node(cx: &LateContext<'_>, hir_id: HirId, f: impl Fn(&PrintVisitor<'_, '_>)) { - if has_attr(cx, hir_id) { - f(&PrintVisitor::new(cx)); - println!("{{"); - println!(" // report your lint here"); - println!("}}"); - } -} - -struct Binding { - name: String, - value: T, -} - -impl Display for Binding { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.name) - } -} - -struct OptionPat { - pub opt: Option, -} - -impl OptionPat { - fn new(opt: Option) -> Self { - Self { opt } - } - - fn if_some(&self, f: impl Fn(&T)) { - if let Some(t) = &self.opt { - f(t); - } - } -} - -impl Display for OptionPat { - fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { - match &self.opt { - None => f.write_str("None"), - Some(node) => write!(f, "Some({node})"), - } - } -} - -struct PrintVisitor<'a, 'tcx> { - cx: &'a LateContext<'tcx>, - /// Fields are the current index that needs to be appended to pattern - /// binding names - ids: Cell>, - /// Currently at the first condition in the if chain - first: Cell, -} - -#[allow(clippy::unused_self)] -impl<'a, 'tcx> PrintVisitor<'a, 'tcx> { - fn new(cx: &'a LateContext<'tcx>) -> Self { - Self { - cx, - ids: Cell::default(), - first: Cell::new(true), - } - } - - fn next(&self, s: &'static str) -> String { - let mut ids = self.ids.take(); - let out = match *ids.entry(s).and_modify(|n| *n += 1).or_default() { - // first usage of the name, use it as is - 0 => s.to_string(), - // append a number starting with 1 - n => format!("{s}{n}"), - }; - self.ids.set(ids); - out - } - - fn bind(&self, name: &'static str, value: T) -> Binding { - let name = self.next(name); - Binding { name, value } - } - - fn option(&self, option: &Binding>, name: &'static str, f: impl Fn(&Binding)) { - match option.value { - None => chain!(self, "{option}.is_none()"), - Some(value) => { - let value = &self.bind(name, value); - chain!(self, "let Some({value}) = {option}"); - f(value); - }, - } - } - - fn slice(&self, slice: &Binding<&[T]>, f: impl Fn(&Binding<&T>)) { - if slice.value.is_empty() { - chain!(self, "{slice}.is_empty()"); - } else { - chain!(self, "{slice}.len() == {}", slice.value.len()); - for (i, value) in slice.value.iter().enumerate() { - let name = format!("{slice}[{i}]"); - f(&Binding { name, value }); - } - } - } - - fn destination(&self, destination: &Binding) { - self.option(field!(destination.label), "label", |label| { - self.ident(field!(label.ident)); - }); - } - - fn ident(&self, ident: &Binding) { - chain!(self, "{ident}.as_str() == {:?}", ident.value.as_str()); - } - - fn symbol(&self, symbol: &Binding) { - chain!(self, "{symbol}.as_str() == {:?}", symbol.value.as_str()); - } - - fn qpath(&self, qpath: &Binding<&QPath<'_>>) { - if let QPath::LangItem(lang_item, ..) = *qpath.value { - chain!(self, "matches!({qpath}, QPath::LangItem(LangItem::{lang_item:?}, _))"); - } else if let Ok(path) = path_to_string(qpath.value) { - chain!(self, "match_qpath({qpath}, &[{}])", path); - } - } - - fn const_arg(&self, const_arg: &Binding<&ConstArg<'_>>) { - match const_arg.value.kind { - ConstArgKind::Path(ref qpath) => { - bind!(self, qpath); - chain!(self, "let ConstArgKind::Path(ref {qpath}) = {const_arg}.kind"); - self.qpath(qpath); - }, - ConstArgKind::Anon(anon_const) => { - bind!(self, anon_const); - chain!(self, "let ConstArgKind::Anon({anon_const}) = {const_arg}.kind"); - self.body(field!(anon_const.body)); - }, - } - } - - fn lit(&self, lit: &Binding<&Lit>) { - let kind = |kind| chain!(self, "let LitKind::{kind} = {lit}.node"); - macro_rules! kind { - ($($t:tt)*) => (kind(format_args!($($t)*))); - } - - match lit.value.node { - LitKind::Bool(val) => kind!("Bool({val:?})"), - LitKind::Char(c) => kind!("Char({c:?})"), - LitKind::Err(_) => kind!("Err"), - LitKind::Byte(b) => kind!("Byte({b})"), - LitKind::Int(i, suffix) => { - let int_ty = match suffix { - LitIntType::Signed(int_ty) => format!("LitIntType::Signed(IntTy::{int_ty:?})"), - LitIntType::Unsigned(uint_ty) => format!("LitIntType::Unsigned(UintTy::{uint_ty:?})"), - LitIntType::Unsuffixed => String::from("LitIntType::Unsuffixed"), - }; - kind!("Int({i}, {int_ty})"); - }, - LitKind::Float(_, suffix) => { - let float_ty = match suffix { - LitFloatType::Suffixed(suffix_ty) => format!("LitFloatType::Suffixed(FloatTy::{suffix_ty:?})"), - LitFloatType::Unsuffixed => String::from("LitFloatType::Unsuffixed"), - }; - kind!("Float(_, {float_ty})"); - }, - LitKind::ByteStr(ref vec, _) => { - bind!(self, vec); - kind!("ByteStr(ref {vec})"); - chain!(self, "let [{:?}] = **{vec}", vec.value); - }, - LitKind::CStr(ref vec, _) => { - bind!(self, vec); - kind!("CStr(ref {vec})"); - chain!(self, "let [{:?}] = **{vec}", vec.value); - }, - LitKind::Str(s, _) => { - bind!(self, s); - kind!("Str({s}, _)"); - self.symbol(s); - }, - } - } - - fn arm(&self, arm: &Binding<&hir::Arm<'_>>) { - self.pat(field!(arm.pat)); - match arm.value.guard { - None => chain!(self, "{arm}.guard.is_none()"), - Some(expr) => { - bind!(self, expr); - chain!(self, "let Some({expr}) = {arm}.guard"); - self.expr(expr); - }, - } - self.expr(field!(arm.body)); - } - - #[allow(clippy::too_many_lines)] - fn expr(&self, expr: &Binding<&hir::Expr<'_>>) { - if let Some(higher::While { condition, body, .. }) = higher::While::hir(expr.value) { - bind!(self, condition, body); - chain!( - self, - "let Some(higher::While {{ condition: {condition}, body: {body} }}) \ - = higher::While::hir({expr})" - ); - self.expr(condition); - self.expr(body); - return; - } - - if let Some(higher::WhileLet { - let_pat, - let_expr, - if_then, - .. - }) = higher::WhileLet::hir(expr.value) - { - bind!(self, let_pat, let_expr, if_then); - chain!( - self, - "let Some(higher::WhileLet {{ let_pat: {let_pat}, let_expr: {let_expr}, if_then: {if_then} }}) \ - = higher::WhileLet::hir({expr})" - ); - self.pat(let_pat); - self.expr(let_expr); - self.expr(if_then); - return; - } - - if let Some(higher::ForLoop { pat, arg, body, .. }) = higher::ForLoop::hir(expr.value) { - bind!(self, pat, arg, body); - chain!( - self, - "let Some(higher::ForLoop {{ pat: {pat}, arg: {arg}, body: {body}, .. }}) \ - = higher::ForLoop::hir({expr})" - ); - self.pat(pat); - self.expr(arg); - self.expr(body); - return; - } - - let kind = |kind| chain!(self, "let ExprKind::{kind} = {expr}.kind"); - macro_rules! kind { - ($($t:tt)*) => (kind(format_args!($($t)*))); - } - - match expr.value.kind { - ExprKind::Let(let_expr) => { - bind!(self, let_expr); - kind!("Let({let_expr})"); - self.pat(field!(let_expr.pat)); - // Does what ExprKind::Cast does, only adds a clause for the type - // if it's a path - if let Some(TyKind::Path(ref qpath)) = let_expr.value.ty.as_ref().map(|ty| &ty.kind) { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {let_expr}.ty.kind"); - self.qpath(qpath); - } - self.expr(field!(let_expr.init)); - }, - ExprKind::Array(elements) => { - bind!(self, elements); - kind!("Array({elements})"); - self.slice(elements, |e| self.expr(e)); - }, - ExprKind::Call(func, args) => { - bind!(self, func, args); - kind!("Call({func}, {args})"); - self.expr(func); - self.slice(args, |e| self.expr(e)); - }, - ExprKind::MethodCall(method_name, receiver, args, _) => { - bind!(self, method_name, receiver, args); - kind!("MethodCall({method_name}, {receiver}, {args}, _)"); - self.ident(field!(method_name.ident)); - self.expr(receiver); - self.slice(args, |e| self.expr(e)); - }, - ExprKind::Tup(elements) => { - bind!(self, elements); - kind!("Tup({elements})"); - self.slice(elements, |e| self.expr(e)); - }, - ExprKind::Binary(op, left, right) => { - bind!(self, op, left, right); - kind!("Binary({op}, {left}, {right})"); - chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); - self.expr(left); - self.expr(right); - }, - ExprKind::Unary(op, inner) => { - bind!(self, inner); - kind!("Unary(UnOp::{op:?}, {inner})"); - self.expr(inner); - }, - ExprKind::Lit(lit) => { - bind!(self, lit); - kind!("Lit(ref {lit})"); - self.lit(lit); - }, - ExprKind::Cast(expr, cast_ty) => { - bind!(self, expr, cast_ty); - kind!("Cast({expr}, {cast_ty})"); - if let TyKind::Path(ref qpath) = cast_ty.value.kind { - bind!(self, qpath); - chain!(self, "let TyKind::Path(ref {qpath}) = {cast_ty}.kind"); - self.qpath(qpath); - } - self.expr(expr); - }, - ExprKind::Type(expr, _ty) => { - bind!(self, expr); - kind!("Type({expr}, _)"); - self.expr(expr); - }, - ExprKind::Loop(body, label, des, _) => { - bind!(self, body); - opt_bind!(self, label); - kind!("Loop({body}, {label}, LoopSource::{des:?}, _)"); - self.block(body); - label.if_some(|l| self.ident(field!(l.ident))); - }, - ExprKind::If(cond, then, else_expr) => { - bind!(self, cond, then); - opt_bind!(self, else_expr); - kind!("If({cond}, {then}, {else_expr})"); - self.expr(cond); - self.expr(then); - else_expr.if_some(|e| self.expr(e)); - }, - ExprKind::Match(scrutinee, arms, des) => { - bind!(self, scrutinee, arms); - kind!("Match({scrutinee}, {arms}, MatchSource::{des:?})"); - self.expr(scrutinee); - self.slice(arms, |arm| self.arm(arm)); - }, - ExprKind::Closure(&Closure { - capture_clause, - fn_decl, - body: body_id, - kind, - .. - }) => { - let capture_clause = match capture_clause { - CaptureBy::Value { .. } => "Value { .. }", - CaptureBy::Ref => "Ref", - }; - - let closure_kind = match kind { - ClosureKind::Closure => "ClosureKind::Closure".to_string(), - ClosureKind::Coroutine(coroutine_kind) => match coroutine_kind { - CoroutineKind::Desugared(desugaring, source) => format!( - "ClosureKind::Coroutine(CoroutineKind::Desugared(CoroutineDesugaring::{desugaring:?}, CoroutineSource::{source:?}))" - ), - CoroutineKind::Coroutine(movability) => { - format!("ClosureKind::Coroutine(CoroutineKind::Coroutine(Movability::{movability:?})") - }, - }, - ClosureKind::CoroutineClosure(desugaring) => { - format!("ClosureKind::CoroutineClosure(CoroutineDesugaring::{desugaring:?})") - }, - }; - - let ret_ty = match fn_decl.output { - FnRetTy::DefaultReturn(_) => "FnRetTy::DefaultReturn(_)", - FnRetTy::Return(_) => "FnRetTy::Return(_ty)", - }; - - bind!(self, fn_decl, body_id); - kind!( - "Closure {{ capture_clause: CaptureBy::{capture_clause}, fn_decl: {fn_decl}, body: {body_id}, closure_kind: {closure_kind}, .. }}" - ); - chain!(self, "let {ret_ty} = {fn_decl}.output"); - self.body(body_id); - }, - ExprKind::Yield(sub, source) => { - bind!(self, sub); - kind!("Yield(sub, YieldSource::{source:?})"); - self.expr(sub); - }, - ExprKind::Block(block, label) => { - bind!(self, block); - opt_bind!(self, label); - kind!("Block({block}, {label})"); - self.block(block); - label.if_some(|l| self.ident(field!(l.ident))); - }, - ExprKind::Assign(target, value, _) => { - bind!(self, target, value); - kind!("Assign({target}, {value}, _span)"); - self.expr(target); - self.expr(value); - }, - ExprKind::AssignOp(op, target, value) => { - bind!(self, op, target, value); - kind!("AssignOp({op}, {target}, {value})"); - chain!(self, "BinOpKind::{:?} == {op}.node", op.value.node); - self.expr(target); - self.expr(value); - }, - ExprKind::Field(object, field_name) => { - bind!(self, object, field_name); - kind!("Field({object}, {field_name})"); - self.ident(field_name); - self.expr(object); - }, - ExprKind::Index(object, index, _) => { - bind!(self, object, index); - kind!("Index({object}, {index})"); - self.expr(object); - self.expr(index); - }, - ExprKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, - ExprKind::AddrOf(kind, mutability, inner) => { - bind!(self, inner); - kind!("AddrOf(BorrowKind::{kind:?}, Mutability::{mutability:?}, {inner})"); - self.expr(inner); - }, - ExprKind::Break(destination, value) => { - bind!(self, destination); - opt_bind!(self, value); - kind!("Break({destination}, {value})"); - self.destination(destination); - value.if_some(|e| self.expr(e)); - }, - ExprKind::Continue(destination) => { - bind!(self, destination); - kind!("Continue({destination})"); - self.destination(destination); - }, - ExprKind::Ret(value) => { - opt_bind!(self, value); - kind!("Ret({value})"); - value.if_some(|e| self.expr(e)); - }, - ExprKind::Become(value) => { - bind!(self, value); - kind!("Become({value})"); - self.expr(value); - }, - ExprKind::InlineAsm(_) => { - kind!("InlineAsm(_)"); - out!("// unimplemented: `ExprKind::InlineAsm` is not further destructured at the moment"); - }, - ExprKind::OffsetOf(container, ref fields) => { - bind!(self, container, fields); - kind!("OffsetOf({container}, {fields})"); - }, - ExprKind::Struct(qpath, fields, base) => { - bind!(self, qpath, fields); - opt_bind!(self, base); - kind!("Struct({qpath}, {fields}, {base})"); - self.qpath(qpath); - self.slice(fields, |field| { - self.ident(field!(field.ident)); - self.expr(field!(field.expr)); - }); - base.if_some(|e| self.expr(e)); - }, - ExprKind::ConstBlock(_) => kind!("ConstBlock(_)"), - ExprKind::Repeat(value, length) => { - bind!(self, value, length); - kind!("Repeat({value}, {length})"); - self.expr(value); - match length.value { - ArrayLen::Infer(..) => chain!(self, "let ArrayLen::Infer(..) = length"), - ArrayLen::Body(const_arg) => { - bind!(self, const_arg); - chain!(self, "let ArrayLen::Body({const_arg}) = {length}"); - self.const_arg(const_arg); - }, - } - }, - ExprKind::Err(_) => kind!("Err(_)"), - ExprKind::DropTemps(expr) => { - bind!(self, expr); - kind!("DropTemps({expr})"); - self.expr(expr); - }, - } - } - - fn block(&self, block: &Binding<&hir::Block<'_>>) { - self.slice(field!(block.stmts), |stmt| self.stmt(stmt)); - self.option(field!(block.expr), "trailing_expr", |expr| { - self.expr(expr); - }); - } - - fn body(&self, body_id: &Binding) { - let expr = self.cx.tcx.hir().body(body_id.value).value; - bind!(self, expr); - chain!(self, "{expr} = &cx.tcx.hir().body({body_id}).value"); - self.expr(expr); - } - - fn pat(&self, pat: &Binding<&hir::Pat<'_>>) { - let kind = |kind| chain!(self, "let PatKind::{kind} = {pat}.kind"); - macro_rules! kind { - ($($t:tt)*) => (kind(format_args!($($t)*))); - } - - match pat.value.kind { - PatKind::Wild => kind!("Wild"), - PatKind::Never => kind!("Never"), - PatKind::Binding(ann, _, name, sub) => { - bind!(self, name); - opt_bind!(self, sub); - let ann = match ann { - BindingMode::NONE => "NONE", - BindingMode::REF => "REF", - BindingMode::MUT => "MUT", - BindingMode::REF_MUT => "REF_MUT", - BindingMode::MUT_REF => "MUT_REF", - BindingMode::MUT_REF_MUT => "MUT_REF_MUT", - }; - kind!("Binding(BindingMode::{ann}, _, {name}, {sub})"); - self.ident(name); - sub.if_some(|p| self.pat(p)); - }, - PatKind::Struct(ref qpath, fields, ignore) => { - bind!(self, qpath, fields); - kind!("Struct(ref {qpath}, {fields}, {ignore})"); - self.qpath(qpath); - self.slice(fields, |field| { - self.ident(field!(field.ident)); - self.pat(field!(field.pat)); - }); - }, - PatKind::Or(fields) => { - bind!(self, fields); - kind!("Or({fields})"); - self.slice(fields, |pat| self.pat(pat)); - }, - PatKind::TupleStruct(ref qpath, fields, skip_pos) => { - bind!(self, qpath, fields); - kind!("TupleStruct(ref {qpath}, {fields}, {skip_pos:?})"); - self.qpath(qpath); - self.slice(fields, |pat| self.pat(pat)); - }, - PatKind::Path(ref qpath) => { - bind!(self, qpath); - kind!("Path(ref {qpath})"); - self.qpath(qpath); - }, - PatKind::Tuple(fields, skip_pos) => { - bind!(self, fields); - kind!("Tuple({fields}, {skip_pos:?})"); - self.slice(fields, |field| self.pat(field)); - }, - PatKind::Box(pat) => { - bind!(self, pat); - kind!("Box({pat})"); - self.pat(pat); - }, - PatKind::Deref(pat) => { - bind!(self, pat); - kind!("Deref({pat})"); - self.pat(pat); - }, - PatKind::Ref(pat, muta) => { - bind!(self, pat); - kind!("Ref({pat}, Mutability::{muta:?})"); - self.pat(pat); - }, - PatKind::Lit(lit_expr) => { - bind!(self, lit_expr); - kind!("Lit({lit_expr})"); - self.expr(lit_expr); - }, - PatKind::Range(start, end, end_kind) => { - opt_bind!(self, start, end); - kind!("Range({start}, {end}, RangeEnd::{end_kind:?})"); - start.if_some(|e| self.expr(e)); - end.if_some(|e| self.expr(e)); - }, - PatKind::Slice(start, middle, end) => { - bind!(self, start, end); - opt_bind!(self, middle); - kind!("Slice({start}, {middle}, {end})"); - middle.if_some(|p| self.pat(p)); - self.slice(start, |pat| self.pat(pat)); - self.slice(end, |pat| self.pat(pat)); - }, - PatKind::Err(_) => kind!("Err"), - } - } - - fn stmt(&self, stmt: &Binding<&hir::Stmt<'_>>) { - let kind = |kind| chain!(self, "let StmtKind::{kind} = {stmt}.kind"); - macro_rules! kind { - ($($t:tt)*) => (kind(format_args!($($t)*))); - } - - match stmt.value.kind { - StmtKind::Let(local) => { - bind!(self, local); - kind!("Let({local})"); - self.option(field!(local.init), "init", |init| { - self.expr(init); - }); - self.pat(field!(local.pat)); - }, - StmtKind::Item(_) => kind!("Item(item_id)"), - StmtKind::Expr(e) => { - bind!(self, e); - kind!("Expr({e})"); - self.expr(e); - }, - StmtKind::Semi(e) => { - bind!(self, e); - kind!("Semi({e})"); - self.expr(e); - }, - } - } -} - -fn has_attr(cx: &LateContext<'_>, hir_id: HirId) -> bool { - let attrs = cx.tcx.hir().attrs(hir_id); - get_attr(cx.sess(), attrs, "author").count() > 0 -} - -fn path_to_string(path: &QPath<'_>) -> Result { - fn inner(s: &mut String, path: &QPath<'_>) -> Result<(), ()> { - match *path { - QPath::Resolved(_, path) => { - for (i, segment) in path.segments.iter().enumerate() { - if i > 0 { - *s += ", "; - } - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - } - }, - QPath::TypeRelative(ty, segment) => match &ty.kind { - TyKind::Path(inner_path) => { - inner(s, inner_path)?; - *s += ", "; - write!(s, "{:?}", segment.ident.as_str()).unwrap(); - }, - other => write!(s, "/* unimplemented: {other:?}*/").unwrap(), - }, - QPath::LangItem(..) => return Err(()), - } - - Ok(()) - } - let mut s = String::new(); - inner(&mut s, path)?; - Ok(s) -} diff --git a/src/tools/clippy/clippy_lints/src/utils/mod.rs b/src/tools/clippy/clippy_lints/src/utils/mod.rs index abd10ac024c..13e9ead9a57 100644 --- a/src/tools/clippy/clippy_lints/src/utils/mod.rs +++ b/src/tools/clippy/clippy_lints/src/utils/mod.rs @@ -1,3 +1,4 @@ +pub mod author; pub mod dump_hir; pub mod format_args_collector; #[cfg(feature = "internal")] -- cgit 1.4.1-3-g733a5 From 637d5cc56fcc11afadd4ffaefa237c11f47cdfee Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 7 Sep 2024 12:13:03 +0200 Subject: Remove module passes filtering --- compiler/rustc_interface/src/passes.rs | 2 +- compiler/rustc_lint/src/builtin.rs | 8 +- compiler/rustc_lint/src/internal.rs | 4 +- compiler/rustc_lint/src/late.rs | 47 ++------ compiler/rustc_lint/src/levels.rs | 126 ++++++--------------- compiler/rustc_lint/src/lib.rs | 22 ++-- compiler/rustc_lint/src/passes.rs | 17 +-- compiler/rustc_lint_defs/src/builtin.rs | 8 +- compiler/rustc_lint_defs/src/lib.rs | 23 ++-- compiler/rustc_middle/src/lint.rs | 2 +- compiler/rustc_session/src/session.rs | 1 - src/librustdoc/lint.rs | 7 +- .../clippy_lints/src/cognitive_complexity.rs | 4 +- src/tools/clippy/clippy_lints/src/ctfe.rs | 51 ++++----- src/tools/clippy/clippy_lints/src/lib.rs | 2 +- src/tools/clippy/clippy_lints/src/utils/author.rs | 1 - 16 files changed, 111 insertions(+), 214 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_interface/src/passes.rs b/compiler/rustc_interface/src/passes.rs index 608d66184f0..fd850d2f39a 100644 --- a/compiler/rustc_interface/src/passes.rs +++ b/compiler/rustc_interface/src/passes.rs @@ -972,7 +972,7 @@ fn analysis(tcx: TyCtxt<'_>, (): ()) -> Result<()> { tcx.ensure().check_mod_privacy(module); }); }); - } // { sess.time("mir_checking", || { tcx.hir().mir_for }) } + } ); // This check has to be run after all lints are done processing. We don't diff --git a/compiler/rustc_lint/src/builtin.rs b/compiler/rustc_lint/src/builtin.rs index 33c87bbfb70..b2c7f1d3cec 100644 --- a/compiler/rustc_lint/src/builtin.rs +++ b/compiler/rustc_lint/src/builtin.rs @@ -20,7 +20,6 @@ //! If you define a new `LateLintPass`, you will also need to add it to the //! `late_lint_methods!` invocation in `lib.rs`. -use std::default::Default; use std::fmt::Write; use ast::token::TokenKind; @@ -74,11 +73,6 @@ use crate::{ EarlyContext, EarlyLintPass, LateContext, LateLintPass, Level, LintContext, fluent_generated as fluent, }; -// 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 { }`. /// @@ -247,7 +241,7 @@ declare_lint! { UNSAFE_CODE, Allow, "usage of `unsafe` code and other potentially unsound constructs", - [loadbearing: true] + @eval_always = true } declare_lint_pass!(UnsafeCode => [UNSAFE_CODE]); diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 0f4f58efd8e..11e9e933dd9 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -430,7 +430,7 @@ declare_tool_lint! { Deny, "prevent creation of diagnostics which cannot be translated", report_in_external_macro: true, - [loadbearing: true] + eval_always: true } declare_tool_lint! { @@ -444,7 +444,7 @@ declare_tool_lint! { Deny, "prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls", report_in_external_macro: true, - [loadbearing: true] + eval_always: 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 f8bd873cdf5..ea7a44dd123 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -24,7 +24,9 @@ use rustc_hir::def_id::{LocalDefId, LocalModDefId}; use rustc_hir::{HirId, intravisit as hir_visit}; use rustc_middle::hir::nested_filter; use rustc_middle::ty::{self, TyCtxt}; -use rustc_session::{Session, lint::{LintPass, builtin::HardwiredLints}}; +use rustc_session::Session; +use rustc_session::lint::LintPass; +use rustc_session::lint::builtin::HardwiredLints; use rustc_span::Span; use tracing::debug; @@ -368,28 +370,15 @@ pub fn late_lint_mod<'tcx, T: LateLintPass<'tcx> + 'tcx>( if store.late_module_passes.is_empty() { late_lint_mod_inner(tcx, module_def_id, context, builtin_lints); } else { - let passes: Vec<_> = - store.late_module_passes.iter().map(|mk_pass| (mk_pass)(tcx)).collect(); - // Filter unused lints - let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(()); - let mut filtered_passes: Vec>> = passes - .into_iter() - .filter(|pass| { - let lints = LintPass::get_lints(pass); - if lints.is_empty() { - true - } else { - lints - .iter() - .any(|lint| !lints_that_dont_need_to_run.contains(&LintId::of(lint))) - } - }) - .collect(); - - filtered_passes.push(Box::new(builtin_lints)); - filtered_passes.push(Box::new(HardwiredLints)); - - let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] }; + let builtin_lints = Box::new(builtin_lints) as Box>; + let mut binding = store + .late_module_passes + .iter() + .map(|mk_pass| (mk_pass)(tcx)) + .chain(std::iter::once(builtin_lints)) + .collect::>(); + + let pass = RuntimeCombinedLateLintPass { passes: binding.as_mut_slice() }; late_lint_mod_inner(tcx, module_def_id, context, pass); } } @@ -440,7 +429,6 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { let lints_that_dont_need_to_run = tcx.lints_that_dont_need_to_run(()); - // dbg!(&lints_that_dont_need_to_run); let mut filtered_passes: Vec>> = passes .into_iter() .filter(|pass| { @@ -450,17 +438,6 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { .collect(); filtered_passes.push(Box::new(HardwiredLints)); - - // let mut filtered_passes: Vec>> = passes - // .into_iter() - // .filter(|pass| { - // let lints = LintPass::get_lints(pass); - // lints.iter() - // .any(|lint| - // !lints_that_dont_need_to_run.contains(&LintId::of(lint))) - // }).collect(); - // - let pass = RuntimeCombinedLateLintPass { passes: &mut filtered_passes[..] }; late_lint_crate_inner(tcx, context, pass); } diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index f5323c295d0..64e1cddf248 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -31,7 +31,7 @@ use crate::errors::{ OverruledAttributeSub, RequestedLevel, UnknownToolInScopedLint, UnsupportedGroup, }; use crate::fluent_generated as fluent; -use crate::late::{unerased_lint_store /*name_without_tool*/}; +use crate::late::unerased_lint_store; use crate::lints::{ DeprecatedLintName, DeprecatedLintNameFromCommandLine, IgnoredUnlessCrateSpecified, OverruledAttributeLint, RemovedLint, RemovedLintFromCommandLine, RenamedLint, @@ -122,7 +122,7 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { .get_lints() .into_iter() .filter_map(|lint| { - if !lint.loadbearing && lint.default_level(tcx.sess.edition()) == Level::Allow { + if !lint.eval_always && lint.default_level(tcx.sess.edition()) == Level::Allow { Some(LintId::of(lint)) } else { None @@ -134,21 +134,6 @@ fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { visitor.process_opts(); tcx.hir().walk_attributes(&mut visitor); - // 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_that_actually_run.contains(&group_name) { - // for lint in group.1 { - // visitor.lints_that_actually_run.insert(name_without_tool(&lint.to_string()).to_string()); - // } - // } else if visitor.lints_allowed.contains(&group_name) { - // for lint in &group.1 { - // visitor.lints_allowed.insert(name_without_tool(&lint.to_string()).to_string()); - // } - // } - // } - visitor.dont_need_to_run } @@ -372,83 +357,44 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { self.tcx.hir() } + /// FIXME(blyxyas): In a future revision, we should also graph #![allow]s, + /// but that is handled with more care fn visit_attribute(&mut self, attribute: &'tcx ast::Attribute) { - match Level::from_attr(attribute) { + if matches!( + Level::from_attr(attribute), Some( Level::Warn - | Level::Deny - | Level::Forbid - | Level::Expect(..) - | Level::ForceWarn(..), - ) => { - let store = unerased_lint_store(self.tcx.sess); - let Some(meta) = attribute.meta() else { return }; - // SAFETY: Lint attributes are always a metalist inside a - // metalist (even with just one lint). - let Some(meta_item_list) = meta.meta_item_list() else { return }; - - for meta_list in meta_item_list { - // Convert Path to String - let Some(meta_item) = meta_list.meta_item() else { return }; - let ident: &str = &meta_item - .path - .segments - .iter() - .map(|segment| segment.ident.as_str()) - .collect::>() - .join("::"); - let Ok(lints) = store.find_lints( - // SAFETY: Lint attributes can only have literals - ident, - ) else { - return; - }; - for lint in lints { - self.dont_need_to_run.swap_remove(&lint); - } - // // 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 { - // let Ok(lints) = store.find_lints( - // // SAFETY: Lint attributes can only have literals - // meta_list.ident().unwrap().name.as_str(), - // ) else { - // return; - // }; - // for lint in lints { - // dbg!("LINT REMOVED", &lint); - // self.dont_need_to_run.swap_remove(&lint); - // } - // } else { - // let Ok(lints) = store.find_lints( - // // SAFETY: Lint attributes can only have literals - // meta_item.path.segments[1].ident.name.as_str(), - // ) else { - // return; - // }; - // for lint in lints { - // dbg!("LINT REMOVED", &lint); - // self.dont_need_to_run.swap_remove(&lint); - // } - // } + | Level::Deny + | Level::Forbid + | Level::Expect(..) + | Level::ForceWarn(..), + ) + ) { + let store = unerased_lint_store(self.tcx.sess); + let Some(meta) = attribute.meta() else { return }; + // SAFETY: Lint attributes are always a metalist inside a + // metalist (even with just one lint). + let Some(meta_item_list) = meta.meta_item_list() else { return }; + + for meta_list in meta_item_list { + // Convert Path to String + let Some(meta_item) = meta_list.meta_item() else { return }; + let ident: &str = &meta_item + .path + .segments + .iter() + .map(|segment| segment.ident.as_str()) + .collect::>() + .join("::"); + let Ok(lints) = store.find_lints( + // SAFETY: Lint attributes can only have literals + ident, + ) else { + return; + }; + for lint in lints { + self.dont_need_to_run.swap_remove(&lint); } - // We handle #![allow]s differently, as these remove checking rather than adding. - } // Some(Level::Allow) if 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 - // .insert(meta_list.name_or_empty().as_str().to_string()); - // } else { - // self.lints_allowed - // .insert(meta_item.path.segments[1].ident.name.as_str().to_string()); - // } - // } - // } - // } - _ => { - return; } } } diff --git a/compiler/rustc_lint/src/lib.rs b/compiler/rustc_lint/src/lib.rs index 11dd17bcd4a..1607c8abfb5 100644 --- a/compiler/rustc_lint/src/lib.rs +++ b/compiler/rustc_lint/src/lib.rs @@ -279,7 +279,7 @@ fn register_builtins(store: &mut LintStore) { store.register_lints(&BuiltinCombinedEarlyLintPass::get_lints()); store.register_lints(&BuiltinCombinedModuleLateLintPass::get_lints()); store.register_lints(&foreign_modules::get_lints()); - store.register_lints(&HardwiredLints::default().get_lints()); + store.register_lints(&HardwiredLints::lint_vec()); add_lint_group!( "nonstandard_style", @@ -601,25 +601,25 @@ fn register_builtins(store: &mut LintStore) { } fn register_internals(store: &mut LintStore) { - store.register_lints(&LintPassImpl::default().get_lints()); + store.register_lints(&LintPassImpl::lint_vec()); store.register_early_pass(|| Box::new(LintPassImpl)); - store.register_lints(&DefaultHashTypes::default().get_lints()); + store.register_lints(&DefaultHashTypes::lint_vec()); store.register_late_mod_pass(|_| Box::new(DefaultHashTypes)); - store.register_lints(&QueryStability::default().get_lints()); + store.register_lints(&QueryStability::lint_vec()); store.register_late_mod_pass(|_| Box::new(QueryStability)); - store.register_lints(&ExistingDocKeyword::default().get_lints()); + store.register_lints(&ExistingDocKeyword::lint_vec()); store.register_late_mod_pass(|_| Box::new(ExistingDocKeyword)); - store.register_lints(&TyTyKind::default().get_lints()); + store.register_lints(&TyTyKind::lint_vec()); store.register_late_mod_pass(|_| Box::new(TyTyKind)); - store.register_lints(&TypeIr::default().get_lints()); + store.register_lints(&TypeIr::lint_vec()); store.register_late_mod_pass(|_| Box::new(TypeIr)); - store.register_lints(&Diagnostics::default().get_lints()); + store.register_lints(&Diagnostics::lint_vec()); store.register_late_mod_pass(|_| Box::new(Diagnostics)); - store.register_lints(&BadOptAccess::default().get_lints()); + store.register_lints(&BadOptAccess::lint_vec()); store.register_late_mod_pass(|_| Box::new(BadOptAccess)); - store.register_lints(&PassByValue::default().get_lints()); + store.register_lints(&PassByValue::lint_vec()); store.register_late_mod_pass(|_| Box::new(PassByValue)); - store.register_lints(&SpanUseEqCtxt::default().get_lints()); + store.register_lints(&SpanUseEqCtxt::lint_vec()); 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 6dbcdefe08d..18244b14e79 100644 --- a/compiler/rustc_lint/src/passes.rs +++ b/compiler/rustc_lint/src/passes.rs @@ -70,18 +70,7 @@ macro_rules! declare_late_lint_pass { // for all the `check_*` methods. late_lint_methods!(declare_late_lint_pass, []); -impl LateLintPass<'_> for HardwiredLints { - fn check_fn( - &mut self, - _: &LateContext<'_>, - _: rustc_hir::intravisit::FnKind<'_>, - _: &'_ rustc_hir::FnDecl<'_>, - _: &'_ rustc_hir::Body<'_>, - _: rustc_span::Span, - _: rustc_span::def_id::LocalDefId, - ) { - } -} +impl LateLintPass<'_> for HardwiredLints {} #[macro_export] macro_rules! expand_combined_late_lint_pass_method { @@ -121,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::default().get_lints());)* + $(lints.extend_from_slice(&$pass::lint_vec());)* lints } } @@ -236,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::default().get_lints());)* + $(lints.extend_from_slice(&$pass::lint_vec());)* lints } } diff --git a/compiler/rustc_lint_defs/src/builtin.rs b/compiler/rustc_lint_defs/src/builtin.rs index df86e9de22e..3d8da0536bd 100644 --- a/compiler/rustc_lint_defs/src/builtin.rs +++ b/compiler/rustc_lint_defs/src/builtin.rs @@ -12,6 +12,8 @@ use rustc_span::edition::Edition; use crate::{FutureIncompatibilityReason, declare_lint, declare_lint_pass}; declare_lint_pass! { + /// Does nothing as a lint pass, but registers some `Lint`s + /// that are used by other parts of the compiler. HardwiredLints => [ // tidy-alphabetical-start ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE, @@ -377,7 +379,7 @@ declare_lint! { pub ARITHMETIC_OVERFLOW, Deny, "arithmetic operation overflows", - [loadbearing: true] + @eval_always = true } declare_lint! { @@ -402,7 +404,7 @@ declare_lint! { pub UNCONDITIONAL_PANIC, Deny, "operation will cause a panic at runtime", - [loadbearing: true] + @eval_always = true } declare_lint! { @@ -634,7 +636,7 @@ declare_lint! { pub UNKNOWN_LINTS, Warn, "unrecognized lint attribute", - [loadbearing: true] + @eval_always = true } declare_lint! { diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 691f37f0977..da0accb0e09 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -315,7 +315,7 @@ pub struct Lint { /// `true` if this lint should not be filtered out under any circustamces /// (e.g. the unknown_attributes lint) - pub loadbearing: bool, + pub eval_always: bool, } /// Extra information for a future incompatibility lint. @@ -460,7 +460,7 @@ impl Lint { future_incompatible: None, feature_gate: None, crate_level_only: false, - loadbearing: false, + eval_always: false, } } @@ -868,7 +868,8 @@ macro_rules! declare_lint { $(#[$attr])* $vis $NAME, $Level, $desc, ); ); - ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, $([loadbearing: $loadbearing: literal])? + ($(#[$attr:meta])* $vis: vis $NAME: ident, $Level: ident, $desc: expr, + $(@eval_always = $eval_always:literal)? $(@feature_gate = $gate:ident;)? $(@future_incompatible = FutureIncompatibleInfo { reason: $reason:expr, @@ -890,7 +891,7 @@ macro_rules! declare_lint { ..$crate::FutureIncompatibleInfo::default_fields_for_macro() }),)? $(edition_lint_opts: Some(($crate::Edition::$lint_edition, $crate::$edition_level)),)? - $(loadbearing: $loadbearing,)? + $(eval_always: $eval_always,)? ..$crate::Lint::default_fields_for_macro() }; ); @@ -900,8 +901,8 @@ macro_rules! declare_lint { macro_rules! declare_tool_lint { ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level: ident, $desc: expr + $(, @eval_always = $eval_always:literal)? $(, @feature_gate = $gate:ident;)? - $(, [loadbearing: $loadbearing: literal])? ) => ( $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false $(, @feature_gate = $gate;)?} ); @@ -909,15 +910,15 @@ macro_rules! declare_tool_lint { $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, report_in_external_macro: $rep:expr $(, @feature_gate = $gate:ident;)? - $(, [loadbearing: $loadbearing: literal])? + $(, eval_always: $eval_always: literal)? ) => ( $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep $(, @feature_gate = $gate;)?} ); ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, $external:expr + $(, @eval_always: $eval_always: literal)? $(, @feature_gate = $gate:ident;)? - $(, [loadbearing: $loadbearing: literal])? ) => ( $(#[$attr])* $vis static $NAME: &$crate::Lint = &$crate::Lint { @@ -930,7 +931,7 @@ macro_rules! declare_tool_lint { is_externally_loaded: true, $(feature_gate: Some(rustc_span::symbol::sym::$gate),)? crate_level_only: false, - $(loadbearing: $loadbearing,)? + $(eval_always: $eval_always,)? ..$crate::Lint::default_fields_for_macro() }; ); @@ -951,6 +952,10 @@ macro_rules! impl_lint_pass { fn name(&self) -> &'static str { stringify!($ty) } fn get_lints(&self) -> $crate::LintVec { vec![$($lint),*] } } + impl $ty { + #[allow(unused)] + pub fn lint_vec() -> $crate::LintVec { vec![$($lint),*] } + } }; } @@ -959,7 +964,7 @@ macro_rules! impl_lint_pass { #[macro_export] macro_rules! declare_lint_pass { ($(#[$m:meta])* $name:ident => [$($lint:expr),* $(,)?]) => { - $(#[$m])* #[derive(Copy, Clone, Default)] pub struct $name; + $(#[$m])* #[derive(Copy, Clone)] pub struct $name; $crate::impl_lint_pass!($name => [$($lint),*]); }; } diff --git a/compiler/rustc_middle/src/lint.rs b/compiler/rustc_middle/src/lint.rs index eaec19700c8..b5862565e8e 100644 --- a/compiler/rustc_middle/src/lint.rs +++ b/compiler/rustc_middle/src/lint.rs @@ -117,7 +117,7 @@ impl ShallowLintLevelMap { /// This lint level is not usable for diagnostics, it needs to be corrected by /// `reveal_actual_level` beforehand. #[instrument(level = "trace", skip(self, tcx), ret)] - pub fn probe_for_lint_level( + fn probe_for_lint_level( &self, tcx: TyCtxt<'_>, id: LintId, diff --git a/compiler/rustc_session/src/session.rs b/compiler/rustc_session/src/session.rs index c8f46226a3e..27879d817b2 100644 --- a/compiler/rustc_session/src/session.rs +++ b/compiler/rustc_session/src/session.rs @@ -187,7 +187,6 @@ pub struct Session { /// errors. pub ctfe_backtrace: Lock, - // pub force_ctfe: bool, /// This tracks where `-Zunleash-the-miri-inside-of-you` was used to get around a /// const check, optionally with the relevant feature gate. We use this to /// warn about unleashing, but with a single diagnostic instead of dozens that diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 679fc45df76..2afb9e549d9 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -2,7 +2,7 @@ use std::sync::LazyLock as Lazy; use rustc_data_structures::fx::FxHashMap; use rustc_lint::LintStore; -use rustc_lint_defs::{Lint, LintId, LintPass, declare_tool_lint}; +use rustc_lint_defs::{Lint, LintId, declare_tool_lint}; use rustc_session::{Session, lint}; /// This function is used to setup the lint initialization. By default, in rustdoc, everything @@ -31,10 +31,9 @@ where allowed_lints.extend(lint_opts.iter().map(|(lint, _)| lint).cloned()); let lints = || { - lint::builtin::HardwiredLints::default() - .get_lints() + lint::builtin::HardwiredLints::lint_vec() .into_iter() - .chain(rustc_lint::SoftLints::default().get_lints()) + .chain(rustc_lint::SoftLints::lint_vec()) }; let lint_opts = lints() diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index b027c289a7f..91cb8b78ba8 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -25,7 +25,7 @@ pub static COGNITIVE_COMPLEXITY: &Lint = &Lint { future_incompatible: None, is_externally_loaded: true, crate_level_only: false, - loadbearing: true, + eval_always: true, ..Lint::default_fields_for_macro() }; pub(crate) static COGNITIVE_COMPLEXITY_INFO: &'static LintInfo = &LintInfo { @@ -44,7 +44,7 @@ Sometimes it's hard to find a way to reduce the complexity. ### Example You'll see it when you get the warning.", version: Some("1.35.0"), - location: "#L0", + location: "clippy_lints/src/cognitive_complexity.rs#L47", }; pub struct CognitiveComplexity { diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs index 7b9f71810a9..ddb4eb82165 100644 --- a/src/tools/clippy/clippy_lints/src/ctfe.rs +++ b/src/tools/clippy/clippy_lints/src/ctfe.rs @@ -1,41 +1,28 @@ use rustc_hir::def_id::LocalDefId; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, FnDecl}; -use rustc_lint::{LateContext, LateLintPass}; +use rustc_lint::Level::Deny; +use rustc_lint::{LateContext, LateLintPass, Lint}; use rustc_session::declare_lint_pass; use rustc_span::Span; -declare_clippy_lint! { - /// ### What it does - /// Checks for comparisons where one side of the relation is - /// either the minimum or maximum value for its type and warns if it involves a - /// case that is always true or always false. Only integer and boolean types are - /// checked. - /// - /// ### Why is this bad? - /// An expression like `min <= x` may misleadingly imply - /// that it is possible for `x` to be less than the minimum. Expressions like - /// `max < x` are probably mistakes. - /// - /// ### Known problems - /// For `usize` the size of the current compile target will - /// be assumed (e.g., 64 bits on 64 bit systems). This means code that uses such - /// a comparison to detect target pointer width will trigger this lint. One can - /// use `mem::sizeof` and compare its value or conditional compilation - /// attributes - /// like `#[cfg(target_pointer_width = "64")] ..` instead. - /// - /// ### Example - /// ```no_run - /// let vec: Vec = Vec::new(); - /// if vec.len() <= 0 {} - /// if 100 > i32::MAX {} - /// ``` - #[clippy::version = "1.82.0"] - pub CLIPPY_CTFE, - correctness, - "a comparison with a maximum or minimum value that is always true or false" -} +/// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes). +/// See rust-lang/rust#125116 for more info. +#[clippy::version = "1.82.0"] +pub static CLIPPY_CTFE: &Lint = &Lint { + name: &"clippy::CLIPPY_CTFE", + default_level: Deny, + desc: "Ensure CTFE is being made", + edition_lint_opts: None, + report_in_external_macro: true, + future_incompatible: None, + is_externally_loaded: true, + crate_level_only: false, + eval_always: true, + ..Lint::default_fields_for_macro() +}; + +// No static CLIPPY_CTFE_INFO because we want this lint to be invisible declare_lint_pass! { ClippyCtfe => [CLIPPY_CTFE] } diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index a5d2f6a4122..88a227f5b6d 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -65,7 +65,7 @@ extern crate clippy_utils; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; -pub mod ctfe; // VERY important lint (rust#125116) +pub mod ctfe; // Very important lint (rust#125116) pub mod declared_lints; pub mod deprecated_lints; diff --git a/src/tools/clippy/clippy_lints/src/utils/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs index f4e166327af..31f9d84f5e4 100644 --- a/src/tools/clippy/clippy_lints/src/utils/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -798,4 +798,3 @@ fn path_to_string(path: &QPath<'_>) -> Result { inner(&mut s, path)?; Ok(s) } - -- cgit 1.4.1-3-g733a5 From 8a40884e1c606ac92326ed5ea444eb78d06da975 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Fri, 11 Oct 2024 00:31:17 +0200 Subject: Unify syntax (all to @eval_always) --- compiler/rustc_lint/src/internal.rs | 4 ++-- compiler/rustc_lint_defs/src/lib.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/src/internal.rs b/compiler/rustc_lint/src/internal.rs index 11e9e933dd9..7dec6dbdc05 100644 --- a/compiler/rustc_lint/src/internal.rs +++ b/compiler/rustc_lint/src/internal.rs @@ -430,7 +430,7 @@ declare_tool_lint! { Deny, "prevent creation of diagnostics which cannot be translated", report_in_external_macro: true, - eval_always: true + @eval_always = true } declare_tool_lint! { @@ -444,7 +444,7 @@ declare_tool_lint! { Deny, "prevent diagnostic creation outside of `Diagnostic`/`Subdiagnostic`/`LintDiagnostic` impls", report_in_external_macro: true, - eval_always: true + @eval_always = true } declare_lint_pass!(Diagnostics => [UNTRANSLATABLE_DIAGNOSTIC, DIAGNOSTIC_OUTSIDE_OF_IMPL]); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index da0accb0e09..320668a5dbf 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -910,14 +910,14 @@ macro_rules! declare_tool_lint { $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, report_in_external_macro: $rep:expr $(, @feature_gate = $gate:ident;)? - $(, eval_always: $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? ) => ( $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep $(, @feature_gate = $gate;)?} ); ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, $external:expr - $(, @eval_always: $eval_always: literal)? + $(, @eval_always = $eval_always: literal)? $(, @feature_gate = $gate:ident;)? ) => ( $(#[$attr])* -- cgit 1.4.1-3-g733a5 From ddad55f6c214357f48134d00050c6dbfcf1d9215 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Tue, 15 Oct 2024 22:15:07 +0200 Subject: Apply review comments + use `shallow_lint_levels_on` --- compiler/rustc_lint/src/late.rs | 2 +- compiler/rustc_lint/src/levels.rs | 20 +++++++++++++++----- compiler/rustc_lint_defs/src/lib.rs | 11 ----------- src/tools/clippy/clippy_lints/src/ctfe.rs | 2 +- src/tools/clippy/clippy_lints/src/lib.rs | 2 +- 5 files changed, 18 insertions(+), 19 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/src/late.rs b/compiler/rustc_lint/src/late.rs index ea7a44dd123..9b1877599ba 100644 --- a/compiler/rustc_lint/src/late.rs +++ b/compiler/rustc_lint/src/late.rs @@ -432,7 +432,7 @@ fn late_lint_crate<'tcx>(tcx: TyCtxt<'tcx>) { let mut filtered_passes: Vec>> = passes .into_iter() .filter(|pass| { - let lints = LintPass::get_lints(pass); + let lints = (**pass).get_lints(); !lints.iter().all(|lint| lints_that_dont_need_to_run.contains(&LintId::of(lint))) }) .collect(); diff --git a/compiler/rustc_lint/src/levels.rs b/compiler/rustc_lint/src/levels.rs index 64e1cddf248..2c12899fad0 100644 --- a/compiler/rustc_lint/src/levels.rs +++ b/compiler/rustc_lint/src/levels.rs @@ -2,8 +2,8 @@ use rustc_ast_pretty::pprust; use rustc_data_structures::fx::{FxIndexMap, FxIndexSet}; use rustc_errors::{Diag, LintDiagnostic, MultiSpan}; use rustc_feature::{Features, GateIssue}; -use rustc_hir::HirId; use rustc_hir::intravisit::{self, Visitor}; +use rustc_hir::{CRATE_HIR_ID, HirId}; use rustc_index::IndexVec; use rustc_middle::bug; use rustc_middle::hir::nested_filter; @@ -118,12 +118,22 @@ impl LintLevelSets { fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { let store = unerased_lint_store(&tcx.sess); + let map = tcx.shallow_lint_levels_on(rustc_hir::CRATE_OWNER_ID); + let dont_need_to_run: FxIndexSet = store .get_lints() .into_iter() .filter_map(|lint| { - if !lint.eval_always && lint.default_level(tcx.sess.edition()) == Level::Allow { - Some(LintId::of(lint)) + if !lint.eval_always { + let lint_level = map.lint_level_id_at_node(tcx, LintId::of(lint), CRATE_HIR_ID); + if matches!(lint_level, (Level::Allow, ..)) + || (matches!(lint_level, (.., LintLevelSource::Default))) + && lint.default_level(tcx.sess.edition()) == Level::Allow + { + Some(LintId::of(lint)) + } else { + None + } } else { None } @@ -372,7 +382,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { ) { let store = unerased_lint_store(self.tcx.sess); let Some(meta) = attribute.meta() else { return }; - // SAFETY: Lint attributes are always a metalist inside a + // Lint attributes are always a metalist inside a // metalist (even with just one lint). let Some(meta_item_list) = meta.meta_item_list() else { return }; @@ -387,7 +397,7 @@ impl<'tcx> Visitor<'tcx> for LintLevelMaximum<'tcx> { .collect::>() .join("::"); let Ok(lints) = store.find_lints( - // SAFETY: Lint attributes can only have literals + // Lint attributes can only have literals ident, ) else { return; diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 320668a5dbf..418caa699e2 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -968,14 +968,3 @@ macro_rules! declare_lint_pass { $crate::impl_lint_pass!($name => [$($lint),*]); }; } - -#[allow(rustc::lint_pass_impl_without_macro)] -impl LintPass for Box

{ - fn name(&self) -> &'static str { - (**self).name() - } - - fn get_lints(&self) -> LintVec { - (**self).get_lints() - } -} diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs index ddb4eb82165..93d0ad789be 100644 --- a/src/tools/clippy/clippy_lints/src/ctfe.rs +++ b/src/tools/clippy/clippy_lints/src/ctfe.rs @@ -7,7 +7,7 @@ use rustc_session::declare_lint_pass; use rustc_span::Span; /// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes). -/// See rust-lang/rust#125116 for more info. +/// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran. #[clippy::version = "1.82.0"] pub static CLIPPY_CTFE: &Lint = &Lint { name: &"clippy::CLIPPY_CTFE", diff --git a/src/tools/clippy/clippy_lints/src/lib.rs b/src/tools/clippy/clippy_lints/src/lib.rs index 88a227f5b6d..14110539709 100644 --- a/src/tools/clippy/clippy_lints/src/lib.rs +++ b/src/tools/clippy/clippy_lints/src/lib.rs @@ -65,7 +65,7 @@ extern crate clippy_utils; #[cfg_attr(feature = "internal", allow(clippy::missing_clippy_version_attribute))] mod utils; -pub mod ctfe; // Very important lint (rust#125116) +pub mod ctfe; // Very important lint, do not remove (rust#125116) pub mod declared_lints; pub mod deprecated_lints; -- cgit 1.4.1-3-g733a5 From 1dcfa271443b4d3a608e656e203a987a5cb07e60 Mon Sep 17 00:00:00 2001 From: blyxyas Date: Sat, 19 Oct 2024 17:47:16 +0200 Subject: Move COGNITIVE_COMPLEXITY to use macro again --- compiler/rustc_lint/src/shadowed_into_iter.rs | 2 +- compiler/rustc_lint_defs/src/lib.rs | 6 +-- .../clippy_lints/src/cognitive_complexity.rs | 55 +++++++++------------- src/tools/clippy/clippy_lints/src/ctfe.rs | 1 - .../clippy/clippy_lints/src/declare_clippy_lint.rs | 30 ++++++++---- 5 files changed, 45 insertions(+), 49 deletions(-) (limited to 'compiler') diff --git a/compiler/rustc_lint/src/shadowed_into_iter.rs b/compiler/rustc_lint/src/shadowed_into_iter.rs index 33a4ae60663..a73904cd776 100644 --- a/compiler/rustc_lint/src/shadowed_into_iter.rs +++ b/compiler/rustc_lint/src/shadowed_into_iter.rs @@ -64,7 +64,7 @@ declare_lint! { }; } -#[derive(Copy, Clone, Default)] +#[derive(Copy, Clone)] pub(crate) struct ShadowedIntoIter; impl_lint_pass!(ShadowedIntoIter => [ARRAY_INTO_ITER, BOXED_SLICE_INTO_ITER]); diff --git a/compiler/rustc_lint_defs/src/lib.rs b/compiler/rustc_lint_defs/src/lib.rs index 418caa699e2..601784f9732 100644 --- a/compiler/rustc_lint_defs/src/lib.rs +++ b/compiler/rustc_lint_defs/src/lib.rs @@ -904,15 +904,15 @@ macro_rules! declare_tool_lint { $(, @eval_always = $eval_always:literal)? $(, @feature_gate = $gate:ident;)? ) => ( - $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false $(, @feature_gate = $gate;)?} + $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, false $(, @eval_always = $eval_always)? $(, @feature_gate = $gate;)?} ); ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, report_in_external_macro: $rep:expr - $(, @feature_gate = $gate:ident;)? $(, @eval_always = $eval_always: literal)? + $(, @feature_gate = $gate:ident;)? ) => ( - $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep $(, @feature_gate = $gate;)?} + $crate::declare_tool_lint!{$(#[$attr])* $vis $tool::$NAME, $Level, $desc, $rep $(, @eval_always = $eval_always)? $(, @feature_gate = $gate;)?} ); ( $(#[$attr:meta])* $vis:vis $tool:ident ::$NAME:ident, $Level:ident, $desc:expr, diff --git a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs index 91cb8b78ba8..477435236a5 100644 --- a/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs +++ b/src/tools/clippy/clippy_lints/src/cognitive_complexity.rs @@ -8,44 +8,31 @@ use core::ops::ControlFlow; use rustc_ast::ast::Attribute; use rustc_hir::intravisit::FnKind; use rustc_hir::{Body, Expr, ExprKind, FnDecl}; -use rustc_lint::Level::Allow; -use rustc_lint::{LateContext, LateLintPass, Lint, LintContext}; +use rustc_lint::{LateContext, LateLintPass, LintContext}; use rustc_session::impl_lint_pass; use rustc_span::def_id::LocalDefId; use rustc_span::{Span, sym}; -use crate::LintInfo; - -pub static COGNITIVE_COMPLEXITY: &Lint = &Lint { - name: &"clippy::COGNITIVE_COMPLEXITY", - default_level: Allow, - desc: "functions that should be split up into multiple functions", - edition_lint_opts: None, - report_in_external_macro: true, - future_incompatible: None, - is_externally_loaded: true, - crate_level_only: false, - eval_always: true, - ..Lint::default_fields_for_macro() -}; -pub(crate) static COGNITIVE_COMPLEXITY_INFO: &'static LintInfo = &LintInfo { - lint: &COGNITIVE_COMPLEXITY, - category: crate::LintCategory::Nursery, - explanation: r"### What it does -Checks for methods with high cognitive complexity. - -### Why is this bad? -Methods of high cognitive complexity tend to be hard to both read and maintain. -Also LLVM will tend to optimize small methods better. - -### Known problems -Sometimes it's hard to find a way to reduce the complexity. - -### Example -You'll see it when you get the warning.", - version: Some("1.35.0"), - location: "clippy_lints/src/cognitive_complexity.rs#L47", -}; +declare_clippy_lint! { + /// ### What it does + /// Checks for methods with high cognitive complexity. + /// + /// ### Why is this bad? + /// Methods of high cognitive complexity tend to be hard to + /// both read and maintain. Also LLVM will tend to optimize small methods better. + /// + /// ### Known problems + /// Sometimes it's hard to find a way to reduce the + /// complexity. + /// + /// ### Example + /// You'll see it when you get the warning. + #[clippy::version = "1.35.0"] + pub COGNITIVE_COMPLEXITY, + nursery, + "functions that should be split up into multiple functions" + @eval_always = true +} pub struct CognitiveComplexity { limit: LimitStack, diff --git a/src/tools/clippy/clippy_lints/src/ctfe.rs b/src/tools/clippy/clippy_lints/src/ctfe.rs index 93d0ad789be..2fe37a64db6 100644 --- a/src/tools/clippy/clippy_lints/src/ctfe.rs +++ b/src/tools/clippy/clippy_lints/src/ctfe.rs @@ -8,7 +8,6 @@ use rustc_span::Span; /// Ensures that Constant-time Function Evaluation is being done (specifically, MIR lint passes). /// As Clippy deactivates codegen, this lint ensures that CTFE (used in hard errors) is still ran. -#[clippy::version = "1.82.0"] pub static CLIPPY_CTFE: &Lint = &Lint { name: &"clippy::CLIPPY_CTFE", default_level: Deny, diff --git a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs index b1e39c70baa..a785a9d377c 100644 --- a/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs +++ b/src/tools/clippy/clippy_lints/src/declare_clippy_lint.rs @@ -9,6 +9,7 @@ macro_rules! declare_clippy_lint { $desc:literal, $version_expr:expr, $version_lit:literal + $(, $eval_always: literal)? ) => { rustc_session::declare_tool_lint! { $(#[doc = $lit])* @@ -17,6 +18,7 @@ macro_rules! declare_clippy_lint { $category, $desc, report_in_external_macro:true + $(, @eval_always = $eval_always)? } pub(crate) static ${concat($lint_name, _INFO)}: &'static crate::LintInfo = &crate::LintInfo { @@ -33,11 +35,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, restriction, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Allow, crate::LintCategory::Restriction, $desc, - Some($version), $version + Some($version), $version $(, $eval_always)? } }; ( @@ -46,12 +49,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, style, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Warn, crate::LintCategory::Style, $desc, - Some($version), $version - + Some($version), $version $(, $eval_always)? } }; ( @@ -60,11 +63,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, correctness, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Deny, crate::LintCategory::Correctness, $desc, - Some($version), $version + Some($version), $version $(, $eval_always)? } }; @@ -74,11 +78,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, perf, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Warn, crate::LintCategory::Perf, $desc, - Some($version), $version + Some($version), $version $(, $eval_always)? } }; ( @@ -87,11 +92,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, complexity, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Warn, crate::LintCategory::Complexity, $desc, - Some($version), $version + Some($version), $version $(, $eval_always)? } }; ( @@ -100,11 +106,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, suspicious, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Warn, crate::LintCategory::Suspicious, $desc, - Some($version), $version + Some($version), $version $(, $eval_always)? } }; ( @@ -113,11 +120,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, nursery, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Allow, crate::LintCategory::Nursery, $desc, - Some($version), $version + Some($version), $version $(, $eval_always)? } }; ( @@ -126,11 +134,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, pedantic, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Allow, crate::LintCategory::Pedantic, $desc, - Some($version), $version + Some($version), $version $(, $eval_always)? } }; ( @@ -139,11 +148,12 @@ macro_rules! declare_clippy_lint { pub $lint_name:ident, cargo, $desc:literal + $(@eval_always = $eval_always: literal)? ) => { declare_clippy_lint! {@ $(#[doc = $lit])* pub $lint_name, Allow, crate::LintCategory::Cargo, $desc, - Some($version), $version + Some($version), $version $(, $eval_always)? } }; -- cgit 1.4.1-3-g733a5