| mod allow_attributes; |
| mod allow_attributes_without_reason; |
| mod blanket_clippy_restriction_lints; |
| mod deprecated_cfg_attr; |
| mod deprecated_semver; |
| mod duplicated_attributes; |
| mod inline_always; |
| mod mixed_attributes_style; |
| mod non_minimal_cfg; |
| mod repr_attributes; |
| mod should_panic_without_expect; |
| mod unnecessary_clippy_cfg; |
| mod useless_attribute; |
| mod utils; |
| |
| use clippy_config::Conf; |
| use clippy_utils::diagnostics::span_lint_and_help; |
| use clippy_utils::msrvs::{self, Msrv, MsrvStack}; |
| use rustc_ast::{self as ast, AttrArgs, AttrKind, Attribute, MetaItemInner, MetaItemKind}; |
| use rustc_hir::{ImplItem, Item, ItemKind, TraitItem}; |
| use rustc_lint::{EarlyContext, EarlyLintPass, LateContext, LateLintPass}; |
| use rustc_session::impl_lint_pass; |
| use rustc_span::sym; |
| use utils::{is_lint_level, is_relevant_impl, is_relevant_item, is_relevant_trait}; |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for items annotated with `#[inline(always)]`, |
| /// unless the annotated function is empty or simply panics. |
| /// |
| /// ### Why is this bad? |
| /// While there are valid uses of this annotation (and once |
| /// you know when to use it, by all means `allow` this lint), it's a common |
| /// newbie-mistake to pepper one's code with it. |
| /// |
| /// As a rule of thumb, before slapping `#[inline(always)]` on a function, |
| /// measure if that additional function call really affects your runtime profile |
| /// sufficiently to make up for the increase in compile time. |
| /// |
| /// ### Known problems |
| /// False positives, big time. This lint is meant to be |
| /// deactivated by everyone doing serious performance work. This means having |
| /// done the measurement. |
| /// |
| /// ### Example |
| /// ```ignore |
| /// #[inline(always)] |
| /// fn not_quite_hot_code(..) { ... } |
| /// ``` |
| #[clippy::version = "pre 1.29.0"] |
| pub INLINE_ALWAYS, |
| pedantic, |
| "use of `#[inline(always)]`" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for `extern crate` and `use` items annotated with |
| /// lint attributes. |
| /// |
| /// This lint permits lint attributes for lints emitted on the items themself. |
| /// For `use` items these lints are: |
| /// * ambiguous_glob_reexports |
| /// * dead_code |
| /// * deprecated |
| /// * hidden_glob_reexports |
| /// * unreachable_pub |
| /// * unused |
| /// * unused_braces |
| /// * unused_import_braces |
| /// * clippy::disallowed_types |
| /// * clippy::enum_glob_use |
| /// * clippy::macro_use_imports |
| /// * clippy::module_name_repetitions |
| /// * clippy::redundant_pub_crate |
| /// * clippy::single_component_path_imports |
| /// * clippy::unsafe_removed_from_name |
| /// * clippy::wildcard_imports |
| /// |
| /// For `extern crate` items these lints are: |
| /// * `unused_imports` on items with `#[macro_use]` |
| /// |
| /// ### Why is this bad? |
| /// Lint attributes have no effect on crate imports. Most |
| /// likely a `!` was forgotten. |
| /// |
| /// ### Example |
| /// ```ignore |
| /// #[deny(dead_code)] |
| /// extern crate foo; |
| /// #[forbid(dead_code)] |
| /// use foo::bar; |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```rust,ignore |
| /// #[allow(unused_imports)] |
| /// use foo::baz; |
| /// #[allow(unused_imports)] |
| /// #[macro_use] |
| /// extern crate baz; |
| /// ``` |
| #[clippy::version = "pre 1.29.0"] |
| pub USELESS_ATTRIBUTE, |
| correctness, |
| "use of lint attributes on `extern crate` items" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for `#[deprecated]` annotations with a `since` |
| /// field that is not a valid semantic version. Also allows "TBD" to signal |
| /// future deprecation. |
| /// |
| /// ### Why is this bad? |
| /// For checking the version of the deprecation, it must be |
| /// a valid semver. Failing that, the contained information is useless. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #[deprecated(since = "forever")] |
| /// fn something_else() { /* ... */ } |
| /// ``` |
| #[clippy::version = "pre 1.29.0"] |
| pub DEPRECATED_SEMVER, |
| correctness, |
| "use of `#[deprecated(since = \"x\")]` where x is not semver" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for `warn`/`deny`/`forbid` attributes targeting the whole clippy::restriction category. |
| /// |
| /// ### Why is this bad? |
| /// Restriction lints sometimes are in contrast with other lints or even go against idiomatic rust. |
| /// These lints should only be enabled on a lint-by-lint basis and with careful consideration. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #![deny(clippy::restriction)] |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// #![deny(clippy::as_conversions)] |
| /// ``` |
| #[clippy::version = "1.47.0"] |
| pub BLANKET_CLIPPY_RESTRICTION_LINTS, |
| suspicious, |
| "enabling the complete restriction group" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for `#[cfg_attr(rustfmt, rustfmt_skip)]` and suggests to replace it |
| /// with `#[rustfmt::skip]`. |
| /// |
| /// ### Why is this bad? |
| /// Since tool_attributes ([rust-lang/rust#44690](https://github.com/rust-lang/rust/issues/44690)) |
| /// are stable now, they should be used instead of the old `cfg_attr(rustfmt)` attributes. |
| /// |
| /// ### Known problems |
| /// This lint doesn't detect crate level inner attributes, because they get |
| /// processed before the PreExpansionPass lints get executed. See |
| /// [#3123](https://github.com/rust-lang/rust-clippy/pull/3123#issuecomment-422321765) |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #[cfg_attr(rustfmt, rustfmt_skip)] |
| /// fn main() { } |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// #[rustfmt::skip] |
| /// fn main() { } |
| /// ``` |
| #[clippy::version = "1.32.0"] |
| pub DEPRECATED_CFG_ATTR, |
| complexity, |
| "usage of `cfg_attr(rustfmt)` instead of tool attributes" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for attributes that allow lints without a reason. |
| /// |
| /// ### Why restrict this? |
| /// Justifying each `allow` helps readers understand the reasoning, |
| /// and may allow removing `allow` attributes if their purpose is obsolete. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #![allow(clippy::some_lint)] |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")] |
| /// ``` |
| #[clippy::version = "1.61.0"] |
| pub ALLOW_ATTRIBUTES_WITHOUT_REASON, |
| restriction, |
| "ensures that all `allow` and `expect` attributes have a reason" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for usage of the `#[allow]` attribute and suggests replacing it with |
| /// the `#[expect]` attribute (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html)) |
| /// |
| /// This lint only warns outer attributes (`#[allow]`), as inner attributes |
| /// (`#![allow]`) are usually used to enable or disable lints on a global scale. |
| /// |
| /// ### Why is this bad? |
| /// `#[expect]` attributes suppress the lint emission, but emit a warning, if |
| /// the expectation is unfulfilled. This can be useful to be notified when the |
| /// lint is no longer triggered. |
| /// |
| /// ### Example |
| /// ```rust,ignore |
| /// #[allow(unused_mut)] |
| /// fn foo() -> usize { |
| /// let mut a = Vec::new(); |
| /// a.len() |
| /// } |
| /// ``` |
| /// Use instead: |
| /// ```rust,ignore |
| /// #[expect(unused_mut)] |
| /// fn foo() -> usize { |
| /// let mut a = Vec::new(); |
| /// a.len() |
| /// } |
| /// ``` |
| #[clippy::version = "1.70.0"] |
| pub ALLOW_ATTRIBUTES, |
| restriction, |
| "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings." |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for `#[should_panic]` attributes without specifying the expected panic message. |
| /// |
| /// ### Why is this bad? |
| /// The expected panic message should be specified to ensure that the test is actually |
| /// panicking with the expected message, and not another unrelated panic. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// fn random() -> i32 { 0 } |
| /// |
| /// #[should_panic] |
| /// #[test] |
| /// fn my_test() { |
| /// let _ = 1 / random(); |
| /// } |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// fn random() -> i32 { 0 } |
| /// |
| /// #[should_panic = "attempt to divide by zero"] |
| /// #[test] |
| /// fn my_test() { |
| /// let _ = 1 / random(); |
| /// } |
| /// ``` |
| #[clippy::version = "1.74.0"] |
| pub SHOULD_PANIC_WITHOUT_EXPECT, |
| pedantic, |
| "ensures that all `should_panic` attributes specify its expected panic message" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for items with `#[repr(packed)]`-attribute without ABI qualification |
| /// |
| /// ### Why is this bad? |
| /// Without qualification, `repr(packed)` implies `repr(Rust)`. The Rust-ABI is inherently unstable. |
| /// While this is fine as long as the type is accessed correctly within Rust-code, most uses |
| /// of `#[repr(packed)]` involve FFI and/or data structures specified by network-protocols or |
| /// other external specifications. In such situations, the unstable Rust-ABI implied in |
| /// `#[repr(packed)]` may lead to future bugs should the Rust-ABI change. |
| /// |
| /// In case you are relying on a well defined and stable memory layout, qualify the type's |
| /// representation using the `C`-ABI. Otherwise, if the type in question is only ever |
| /// accessed from Rust-code according to Rust's rules, use the `Rust`-ABI explicitly. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #[repr(packed)] |
| /// struct NetworkPacketHeader { |
| /// header_length: u8, |
| /// header_version: u16 |
| /// } |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// #[repr(C, packed)] |
| /// struct NetworkPacketHeader { |
| /// header_length: u8, |
| /// header_version: u16 |
| /// } |
| /// ``` |
| #[clippy::version = "1.85.0"] |
| pub REPR_PACKED_WITHOUT_ABI, |
| suspicious, |
| "ensures that `repr(packed)` always comes with a qualified ABI" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for `any` and `all` combinators in `cfg` with only one condition. |
| /// |
| /// ### Why is this bad? |
| /// If there is only one condition, no need to wrap it into `any` or `all` combinators. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #[cfg(any(unix))] |
| /// pub struct Bar; |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// #[cfg(unix)] |
| /// pub struct Bar; |
| /// ``` |
| #[clippy::version = "1.71.0"] |
| pub NON_MINIMAL_CFG, |
| style, |
| "ensure that all `cfg(any())` and `cfg(all())` have more than one condition" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for `#[cfg_attr(feature = "cargo-clippy", ...)]` and for |
| /// `#[cfg(feature = "cargo-clippy")]` and suggests to replace it with |
| /// `#[cfg_attr(clippy, ...)]` or `#[cfg(clippy)]`. |
| /// |
| /// ### Why is this bad? |
| /// This feature has been deprecated for years and shouldn't be used anymore. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #[cfg(feature = "cargo-clippy")] |
| /// struct Bar; |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// #[cfg(clippy)] |
| /// struct Bar; |
| /// ``` |
| #[clippy::version = "1.78.0"] |
| pub DEPRECATED_CLIPPY_CFG_ATTR, |
| suspicious, |
| "usage of `cfg(feature = \"cargo-clippy\")` instead of `cfg(clippy)`" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for `#[cfg_attr(clippy, allow(clippy::lint))]` |
| /// and suggests to replace it with `#[allow(clippy::lint)]`. |
| /// |
| /// ### Why is this bad? |
| /// There is no reason to put clippy attributes behind a clippy `cfg` as they are not |
| /// run by anything else than clippy. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #![cfg_attr(clippy, allow(clippy::deprecated_cfg_attr))] |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// #![allow(clippy::deprecated_cfg_attr)] |
| /// ``` |
| #[clippy::version = "1.78.0"] |
| pub UNNECESSARY_CLIPPY_CFG, |
| suspicious, |
| "usage of `cfg_attr(clippy, allow(clippy::lint))` instead of `allow(clippy::lint)`" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for items that have the same kind of attributes with mixed styles (inner/outer). |
| /// |
| /// ### Why is this bad? |
| /// Having both style of said attributes makes it more complicated to read code. |
| /// |
| /// ### Known problems |
| /// This lint currently has false-negatives when mixing same attributes |
| /// but they have different path symbols, for example: |
| /// ```ignore |
| /// #[custom_attribute] |
| /// pub fn foo() { |
| /// #![my_crate::custom_attribute] |
| /// } |
| /// ``` |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #[cfg(linux)] |
| /// pub fn foo() { |
| /// #![cfg(windows)] |
| /// } |
| /// ``` |
| /// Use instead: |
| /// ```no_run |
| /// #[cfg(linux)] |
| /// #[cfg(windows)] |
| /// pub fn foo() { |
| /// } |
| /// ``` |
| #[clippy::version = "1.78.0"] |
| pub MIXED_ATTRIBUTES_STYLE, |
| style, |
| "item has both inner and outer attributes" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for attributes that appear two or more times. |
| /// |
| /// ### Why is this bad? |
| /// Repeating an attribute on the same item (or globally on the same crate) |
| /// is unnecessary and doesn't have an effect. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #[allow(dead_code)] |
| /// #[allow(dead_code)] |
| /// fn foo() {} |
| /// ``` |
| /// |
| /// Use instead: |
| /// ```no_run |
| /// #[allow(dead_code)] |
| /// fn foo() {} |
| /// ``` |
| #[clippy::version = "1.79.0"] |
| pub DUPLICATED_ATTRIBUTES, |
| suspicious, |
| "duplicated attribute" |
| } |
| |
| declare_clippy_lint! { |
| /// ### What it does |
| /// Checks for ignored tests without messages. |
| /// |
| /// ### Why is this bad? |
| /// The reason for ignoring the test may not be obvious. |
| /// |
| /// ### Example |
| /// ```no_run |
| /// #[test] |
| /// #[ignore] |
| /// fn test() {} |
| /// ``` |
| /// Use instead: |
| /// ```no_run |
| /// #[test] |
| /// #[ignore = "Some good reason"] |
| /// fn test() {} |
| /// ``` |
| #[clippy::version = "1.88.0"] |
| pub IGNORE_WITHOUT_REASON, |
| pedantic, |
| "ignored tests without messages" |
| } |
| |
| pub struct Attributes { |
| msrv: Msrv, |
| } |
| |
| impl_lint_pass!(Attributes => [ |
| INLINE_ALWAYS, |
| REPR_PACKED_WITHOUT_ABI, |
| ]); |
| |
| impl Attributes { |
| pub fn new(conf: &'static Conf) -> Self { |
| Self { msrv: conf.msrv } |
| } |
| } |
| |
| impl<'tcx> LateLintPass<'tcx> for Attributes { |
| fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'_>) { |
| let attrs = cx.tcx.hir_attrs(item.hir_id()); |
| if let ItemKind::Fn { ident, .. } = item.kind |
| && is_relevant_item(cx, item) |
| { |
| inline_always::check(cx, item.span, ident.name, attrs); |
| } |
| repr_attributes::check(cx, item.span, attrs, self.msrv); |
| } |
| |
| fn check_impl_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx ImplItem<'_>) { |
| if is_relevant_impl(cx, item) { |
| inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id())); |
| } |
| } |
| |
| fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'_>) { |
| if is_relevant_trait(cx, item) { |
| inline_always::check(cx, item.span, item.ident.name, cx.tcx.hir_attrs(item.hir_id())); |
| } |
| } |
| } |
| |
| pub struct EarlyAttributes { |
| msrv: MsrvStack, |
| } |
| |
| impl EarlyAttributes { |
| pub fn new(conf: &'static Conf) -> Self { |
| Self { |
| msrv: MsrvStack::new(conf.msrv), |
| } |
| } |
| } |
| |
| impl_lint_pass!(EarlyAttributes => [ |
| DEPRECATED_CFG_ATTR, |
| NON_MINIMAL_CFG, |
| DEPRECATED_CLIPPY_CFG_ATTR, |
| UNNECESSARY_CLIPPY_CFG, |
| ]); |
| |
| impl EarlyLintPass for EarlyAttributes { |
| fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { |
| deprecated_cfg_attr::check(cx, attr, &self.msrv); |
| deprecated_cfg_attr::check_clippy(cx, attr); |
| non_minimal_cfg::check(cx, attr); |
| } |
| |
| extract_msrv_attr!(); |
| } |
| |
| pub struct PostExpansionEarlyAttributes { |
| msrv: MsrvStack, |
| } |
| |
| impl PostExpansionEarlyAttributes { |
| pub fn new(conf: &'static Conf) -> Self { |
| Self { |
| msrv: MsrvStack::new(conf.msrv), |
| } |
| } |
| } |
| |
| impl_lint_pass!(PostExpansionEarlyAttributes => [ |
| ALLOW_ATTRIBUTES, |
| ALLOW_ATTRIBUTES_WITHOUT_REASON, |
| DEPRECATED_SEMVER, |
| IGNORE_WITHOUT_REASON, |
| USELESS_ATTRIBUTE, |
| BLANKET_CLIPPY_RESTRICTION_LINTS, |
| SHOULD_PANIC_WITHOUT_EXPECT, |
| MIXED_ATTRIBUTES_STYLE, |
| DUPLICATED_ATTRIBUTES, |
| ]); |
| |
| impl EarlyLintPass for PostExpansionEarlyAttributes { |
| fn check_crate(&mut self, cx: &EarlyContext<'_>, krate: &ast::Crate) { |
| blanket_clippy_restriction_lints::check_command_line(cx); |
| duplicated_attributes::check(cx, &krate.attrs); |
| } |
| |
| fn check_attribute(&mut self, cx: &EarlyContext<'_>, attr: &Attribute) { |
| if let Some(items) = &attr.meta_item_list() |
| && let Some(ident) = attr.ident() |
| { |
| if matches!(ident.name, sym::allow) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { |
| allow_attributes::check(cx, attr); |
| } |
| if matches!(ident.name, sym::allow | sym::expect) && self.msrv.meets(msrvs::LINT_REASONS_STABILIZATION) { |
| allow_attributes_without_reason::check(cx, ident.name, items, attr); |
| } |
| if is_lint_level(ident.name, attr.id) { |
| blanket_clippy_restriction_lints::check(cx, ident.name, items); |
| } |
| if items.is_empty() || !attr.has_name(sym::deprecated) { |
| return; |
| } |
| for item in items { |
| if let MetaItemInner::MetaItem(mi) = &item |
| && let MetaItemKind::NameValue(lit) = &mi.kind |
| && mi.has_name(sym::since) |
| { |
| deprecated_semver::check(cx, item.span(), lit); |
| } |
| } |
| } |
| |
| if attr.has_name(sym::should_panic) { |
| should_panic_without_expect::check(cx, attr); |
| } |
| |
| if attr.has_name(sym::ignore) |
| && match &attr.kind { |
| AttrKind::Normal(normal_attr) => !matches!(normal_attr.item.args, AttrArgs::Eq { .. }), |
| AttrKind::DocComment(..) => true, |
| } |
| { |
| span_lint_and_help( |
| cx, |
| IGNORE_WITHOUT_REASON, |
| attr.span, |
| "`#[ignore]` without reason", |
| None, |
| "add a reason with `= \"..\"`", |
| ); |
| } |
| } |
| |
| fn check_item(&mut self, cx: &EarlyContext<'_>, item: &'_ ast::Item) { |
| match item.kind { |
| ast::ItemKind::ExternCrate(..) | ast::ItemKind::Use(..) => useless_attribute::check(cx, item, &item.attrs), |
| _ => {}, |
| } |
| |
| mixed_attributes_style::check(cx, item.span, &item.attrs); |
| duplicated_attributes::check(cx, &item.attrs); |
| } |
| |
| extract_msrv_attr!(); |
| } |