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); +fn lints_that_dont_need_to_run(tcx: TyCtxt<'_>, (): ()) -> FxIndexSet { + let store = unerased_lint_store(&tcx.sess); + + 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(); + + let mut visitor = LintLevelMaximum { tcx, dont_need_to_run }; 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_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 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()); - } - } - } - - Lrc::new((visitor.lints_that_actually_run, visitor.lints_allowed)) + 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/internal_lints/author.rs b/src/tools/clippy/clippy_lints/src/utils/author.rs similarity index 99% rename from src/tools/clippy/clippy_lints/src/utils/internal_lints/author.rs rename to src/tools/clippy/clippy_lints/src/utils/author.rs index 0ed606a836e..f4e166327af 100644 --- a/src/tools/clippy/clippy_lints/src/utils/internal_lints/author.rs +++ b/src/tools/clippy/clippy_lints/src/utils/author.rs @@ -12,7 +12,7 @@ use rustc_span::symbol::{Ident, Symbol}; use std::cell::Cell; use std::fmt::{Display, Formatter, Write as _}; -declare_clippy_lint!{ +declare_lint_pass!( /// ### What it does /// Generates clippy code that detects the offending pattern /// @@ -44,13 +44,8 @@ declare_clippy_lint!{ /// // report your lint here /// } /// ``` - #[clippy::version = "1.0.0"] - pub AUTHOR, - internal, - "The author lint, see documentation at " -}; - -declare_lint_pass! { Author => [AUTHOR] } + Author => [] +); /// Writes a line of output with indentation added macro_rules! out { @@ -803,3 +798,4 @@ fn path_to_string(path: &QPath<'_>) -> Result { 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")]