11//! checks for attributes
22
3+ mod allow_attributes;
34mod allow_attributes_without_reason;
45mod blanket_clippy_restriction_lints;
56mod deprecated_cfg_attr;
@@ -14,11 +15,11 @@ mod unnecessary_clippy_cfg;
1415mod useless_attribute;
1516mod utils;
1617
17- use clippy_config:: msrvs:: Msrv ;
18+ use clippy_config:: msrvs:: { self , Msrv } ;
1819use rustc_ast:: { Attribute , MetaItemKind , NestedMetaItem } ;
1920use rustc_hir:: { ImplItem , Item , ItemKind , TraitItem } ;
2021use rustc_lint:: { EarlyContext , EarlyLintPass , LateContext , LateLintPass } ;
21- use rustc_session:: { declare_lint_pass , impl_lint_pass} ;
22+ use rustc_session:: impl_lint_pass;
2223use rustc_span:: sym;
2324use utils:: { is_lint_level, is_relevant_impl, is_relevant_item, is_relevant_trait} ;
2425
@@ -270,26 +271,19 @@ declare_clippy_lint! {
270271
271272declare_clippy_lint ! {
272273 /// ### What it does
273- /// Checks for attributes that allow lints without specifying the reason
274- /// they should be allowed. (This requires the `lint_reasons` feature.)
274+ /// Checks for attributes that allow lints without a reason.
275275 ///
276276 /// ### Why restrict this?
277- /// There should always be a specific reason to allow a lint. This reason
278- /// should be documented using the `reason` parameter, so that readers can
279- /// understand why the `allow` is required, or remove it if it's no
280- /// longer needed.
277+ /// Justifying each `allow` helps readers understand the reasoning,
278+ /// and may allow removing `allow` attributes if their purpose is obsolete.
281279 ///
282280 /// ### Example
283281 /// ```no_run
284- /// #![feature(lint_reasons)]
285- ///
286282 /// #![allow(clippy::some_lint)]
287283 /// ```
288284 ///
289285 /// Use instead:
290286 /// ```no_run
291- /// #![feature(lint_reasons)]
292- ///
293287 /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
294288 /// ```
295289 #[ clippy:: version = "1.61.0" ]
@@ -298,6 +292,41 @@ declare_clippy_lint! {
298292 "ensures that all `allow` and `expect` attributes have a reason"
299293}
300294
295+ declare_clippy_lint ! {
296+ /// ### What it does
297+ /// Checks for usage of the `#[allow]` attribute and suggests replacing it with
298+ /// the `#[expect]` (See [RFC 2383](https://rust-lang.github.io/rfcs/2383-lint-reasons.html))
299+ ///
300+ /// This lint only warns outer attributes (`#[allow]`), as inner attributes
301+ /// (`#![allow]`) are usually used to enable or disable lints on a global scale.
302+ ///
303+ /// ### Why is this bad?
304+ /// `#[expect]` attributes suppress the lint emission, but emit a warning, if
305+ /// the expectation is unfulfilled. This can be useful to be notified when the
306+ /// lint is no longer triggered.
307+ ///
308+ /// ### Example
309+ /// ```rust,ignore
310+ /// #[allow(unused_mut)]
311+ /// fn foo() -> usize {
312+ /// let mut a = Vec::new();
313+ /// a.len()
314+ /// }
315+ /// ```
316+ /// Use instead:
317+ /// ```rust,ignore
318+ /// #[expect(unused_mut)]
319+ /// fn foo() -> usize {
320+ /// let mut a = Vec::new();
321+ /// a.len()
322+ /// }
323+ /// ```
324+ #[ clippy:: version = "1.70.0" ]
325+ pub ALLOW_ATTRIBUTES ,
326+ restriction,
327+ "`#[allow]` will not trigger if a warning isn't found. `#[expect]` triggers if there are no warnings."
328+ }
329+
301330declare_clippy_lint ! {
302331 /// ### What it does
303332 /// Checks for `#[should_panic]` attributes without specifying the expected panic message.
@@ -470,7 +499,13 @@ declare_clippy_lint! {
470499 "duplicated attribute"
471500}
472501
473- declare_lint_pass ! ( Attributes => [
502+ #[ derive( Clone ) ]
503+ pub struct Attributes {
504+ msrv : Msrv ,
505+ }
506+
507+ impl_lint_pass ! ( Attributes => [
508+ ALLOW_ATTRIBUTES ,
474509 ALLOW_ATTRIBUTES_WITHOUT_REASON ,
475510 INLINE_ALWAYS ,
476511 DEPRECATED_SEMVER ,
@@ -481,6 +516,13 @@ declare_lint_pass!(Attributes => [
481516 DUPLICATED_ATTRIBUTES ,
482517] ) ;
483518
519+ impl Attributes {
520+ #[ must_use]
521+ pub fn new ( msrv : Msrv ) -> Self {
522+ Self { msrv }
523+ }
524+ }
525+
484526impl < ' tcx > LateLintPass < ' tcx > for Attributes {
485527 fn check_crate ( & mut self , cx : & LateContext < ' tcx > ) {
486528 blanket_clippy_restriction_lints:: check_command_line ( cx) ;
@@ -493,7 +535,11 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
493535 if is_lint_level ( ident. name , attr. id ) {
494536 blanket_clippy_restriction_lints:: check ( cx, ident. name , items) ;
495537 }
496- if matches ! ( ident. name, sym:: allow | sym:: expect) {
538+ if matches ! ( ident. name, sym:: allow) && self . msrv . meets ( msrvs:: LINT_REASONS_STABILIZATION ) {
539+ allow_attributes:: check ( cx, attr) ;
540+ }
541+ if matches ! ( ident. name, sym:: allow | sym:: expect) && self . msrv . meets ( msrvs:: LINT_REASONS_STABILIZATION )
542+ {
497543 allow_attributes_without_reason:: check ( cx, ident. name , items, attr) ;
498544 }
499545 if items. is_empty ( ) || !attr. has_name ( sym:: deprecated) {
@@ -538,6 +584,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
538584 inline_always:: check ( cx, item. span , item. ident . name , cx. tcx . hir ( ) . attrs ( item. hir_id ( ) ) ) ;
539585 }
540586 }
587+
588+ extract_msrv_attr ! ( LateContext ) ;
541589}
542590
543591pub struct EarlyAttributes {
0 commit comments