11//! checks for attributes
22
33mod allow_attributes_without_reason;
4+ mod allow_attributes;
45mod blanket_clippy_restriction_lints;
56mod deprecated_cfg_attr;
67mod deprecated_semver;
@@ -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
@@ -272,23 +273,17 @@ declare_clippy_lint! {
272273 /// ### What it does
273274 /// Checks for attributes that allow lints without a reason.
274275 ///
275- /// (This requires the `lint_reasons` feature)
276- ///
277276 /// ### Why restrict this?
278277 /// Justifying each `allow` helps readers understand the reasoning,
279278 /// and may allow removing `allow` attributes if their purpose is obsolete.
280279 ///
281280 /// ### Example
282281 /// ```no_run
283- /// #![feature(lint_reasons)]
284- ///
285282 /// #![allow(clippy::some_lint)]
286283 /// ```
287284 ///
288285 /// Use instead:
289286 /// ```no_run
290- /// #![feature(lint_reasons)]
291- ///
292287 /// #![allow(clippy::some_lint, reason = "False positive rust-lang/rust-clippy#1002020")]
293288 /// ```
294289 #[ clippy:: version = "1.61.0" ]
@@ -297,6 +292,41 @@ declare_clippy_lint! {
297292 "ensures that all `allow` and `expect` attributes have a reason"
298293}
299294
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+
300330declare_clippy_lint ! {
301331 /// ### What it does
302332 /// Checks for `#[should_panic]` attributes without specifying the expected panic message.
@@ -469,7 +499,12 @@ declare_clippy_lint! {
469499 "duplicated attribute"
470500}
471501
472- declare_lint_pass ! ( Attributes => [
502+ #[ derive( Clone ) ]
503+ pub struct Attributes {
504+ msrv : Msrv ,
505+ }
506+
507+ impl_lint_pass ! ( Attributes => [
473508 ALLOW_ATTRIBUTES_WITHOUT_REASON ,
474509 INLINE_ALWAYS ,
475510 DEPRECATED_SEMVER ,
@@ -480,6 +515,13 @@ declare_lint_pass!(Attributes => [
480515 DUPLICATED_ATTRIBUTES ,
481516] ) ;
482517
518+ impl Attributes {
519+ #[ must_use]
520+ pub fn new ( msrv : Msrv ) -> Self {
521+ Self { msrv }
522+ }
523+ }
524+
483525impl < ' tcx > LateLintPass < ' tcx > for Attributes {
484526 fn check_crate ( & mut self , cx : & LateContext < ' tcx > ) {
485527 blanket_clippy_restriction_lints:: check_command_line ( cx) ;
@@ -492,8 +534,15 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
492534 if is_lint_level ( ident. name , attr. id ) {
493535 blanket_clippy_restriction_lints:: check ( cx, ident. name , items) ;
494536 }
537+ if matches ! ( ident. name, sym:: allow) {
538+ if self . msrv . meets ( msrvs:: LINT_REASONS_STABILIZATION ) {
539+ allow_attributes:: check ( cx, attr) ;
540+ }
541+ }
495542 if matches ! ( ident. name, sym:: allow | sym:: expect) {
496- allow_attributes_without_reason:: check ( cx, ident. name , items, attr) ;
543+ if self . msrv . meets ( msrvs:: LINT_REASONS_STABILIZATION ) {
544+ allow_attributes_without_reason:: check ( cx, ident. name , items, attr) ;
545+ }
497546 }
498547 if items. is_empty ( ) || !attr. has_name ( sym:: deprecated) {
499548 return ;
@@ -537,6 +586,8 @@ impl<'tcx> LateLintPass<'tcx> for Attributes {
537586 inline_always:: check ( cx, item. span , item. ident . name , cx. tcx . hir ( ) . attrs ( item. hir_id ( ) ) ) ;
538587 }
539588 }
589+
590+ extract_msrv_attr ! ( LateContext ) ;
540591}
541592
542593pub struct EarlyAttributes {
0 commit comments