@@ -362,6 +362,32 @@ declare_clippy_lint! {
362362 "ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
363363}
364364
365+ declare_clippy_lint ! {
366+ /// ### What it does
367+ /// Checks for `#[cfg(features = "...")]` and suggests to replace it with
368+ /// `#[cfg(feature = "...")]`.
369+ ///
370+ /// ### Why is this bad?
371+ /// Misspelling `feature` as `features` can be sometimes hard to spot. It
372+ /// may cause conditional compilation not work quitely.
373+ ///
374+ /// ### Example
375+ /// ```rust
376+ /// #[cfg(features = "some-feature")]
377+ /// fn conditional() { }
378+ /// ```
379+ ///
380+ /// Use instead:
381+ /// ```rust
382+ /// #[cfg(feature = "some-feature")]
383+ /// fn conditional() { }
384+ /// ```
385+ #[ clippy:: version = "1.69.0" ]
386+ pub MAYBE_MISUSED_CFG ,
387+ suspicious,
388+ "prevent from misusing the wrong attr name"
389+ }
390+
365391declare_lint_pass ! ( Attributes => [
366392 ALLOW_ATTRIBUTES_WITHOUT_REASON ,
367393 INLINE_ALWAYS ,
@@ -676,6 +702,7 @@ impl_lint_pass!(EarlyAttributes => [
676702 EMPTY_LINE_AFTER_OUTER_ATTR ,
677703 EMPTY_LINE_AFTER_DOC_COMMENTS ,
678704 NON_MINIMAL_CFG ,
705+ MAYBE_MISUSED_CFG ,
679706] ) ;
680707
681708impl EarlyLintPass for EarlyAttributes {
@@ -687,6 +714,7 @@ impl EarlyLintPass for EarlyAttributes {
687714 check_deprecated_cfg_attr ( cx, attr, & self . msrv ) ;
688715 check_mismatched_target_os ( cx, attr) ;
689716 check_minimal_cfg_condition ( cx, attr) ;
717+ check_misused_cfg ( cx, attr) ;
690718 }
691719
692720 extract_msrv_attr ! ( EarlyContext ) ;
@@ -810,6 +838,27 @@ fn check_nested_cfg(cx: &EarlyContext<'_>, items: &[NestedMetaItem]) {
810838 }
811839}
812840
841+ fn check_nested_misused_cfg ( cx : & EarlyContext < ' _ > , items : & [ NestedMetaItem ] ) {
842+ for item in items. iter ( ) {
843+ if let NestedMetaItem :: MetaItem ( meta) = item {
844+ if meta. has_name ( sym ! ( features) ) && let Some ( val) = meta. value_str ( ) {
845+ span_lint_and_sugg (
846+ cx,
847+ MAYBE_MISUSED_CFG ,
848+ meta. span ,
849+ "feature may misspelled as features" ,
850+ "use" ,
851+ format ! ( "feature = \" {val}\" " ) ,
852+ Applicability :: MaybeIncorrect ,
853+ ) ;
854+ }
855+ if let MetaItemKind :: List ( list) = & meta. kind {
856+ check_nested_misused_cfg ( cx, list) ;
857+ }
858+ }
859+ }
860+ }
861+
813862fn check_minimal_cfg_condition ( cx : & EarlyContext < ' _ > , attr : & Attribute ) {
814863 if attr. has_name ( sym:: cfg) &&
815864 let Some ( items) = attr. meta_item_list ( )
@@ -818,6 +867,14 @@ fn check_minimal_cfg_condition(cx: &EarlyContext<'_>, attr: &Attribute) {
818867 }
819868}
820869
870+ fn check_misused_cfg ( cx : & EarlyContext < ' _ > , attr : & Attribute ) {
871+ if attr. has_name ( sym:: cfg) &&
872+ let Some ( items) = attr. meta_item_list ( )
873+ {
874+ check_nested_misused_cfg ( cx, & items) ;
875+ }
876+ }
877+
821878fn check_mismatched_target_os ( cx : & EarlyContext < ' _ > , attr : & Attribute ) {
822879 fn find_os ( name : & str ) -> Option < & ' static str > {
823880 UNIX_SYSTEMS
0 commit comments