@@ -338,6 +338,30 @@ declare_clippy_lint! {
338338 "ensures that all `allow` and `expect` attributes have a reason"
339339}
340340
341+ declare_clippy_lint ! {
342+ /// ### What it does
343+ /// Checks for `any` and `all` combinators in `cfg` with only one condition.
344+ ///
345+ /// ### Why is this bad?
346+ /// If there is only one condition, no need to wrap it into `any` or `all` combinators.
347+ ///
348+ /// ### Example
349+ /// ```rust
350+ /// #[cfg(any(unix))]
351+ /// pub struct Bar;
352+ /// ```
353+ ///
354+ /// Use instead:
355+ /// ```rust
356+ /// #[cfg(unix)]
357+ /// pub struct Bar;
358+ /// ```
359+ #[ clippy:: version = "1.71.0" ]
360+ pub NON_MINIMAL_CFG ,
361+ style,
362+ "ensure that all `cfg(any())` and `cfg(all())` have more than one condition"
363+ }
364+
341365declare_lint_pass ! ( Attributes => [
342366 ALLOW_ATTRIBUTES_WITHOUT_REASON ,
343367 INLINE_ALWAYS ,
@@ -651,6 +675,7 @@ impl_lint_pass!(EarlyAttributes => [
651675 MISMATCHED_TARGET_OS ,
652676 EMPTY_LINE_AFTER_OUTER_ATTR ,
653677 EMPTY_LINE_AFTER_DOC_COMMENTS ,
678+ NON_MINIMAL_CFG ,
654679] ) ;
655680
656681impl EarlyLintPass for EarlyAttributes {
@@ -661,6 +686,7 @@ impl EarlyLintPass for EarlyAttributes {
661686 fn check_attribute ( & mut self , cx : & EarlyContext < ' _ > , attr : & Attribute ) {
662687 check_deprecated_cfg_attr ( cx, attr, & self . msrv ) ;
663688 check_mismatched_target_os ( cx, attr) ;
689+ check_minimal_cfg_condition ( cx, attr) ;
664690 }
665691
666692 extract_msrv_attr ! ( EarlyContext ) ;
@@ -750,6 +776,48 @@ fn check_deprecated_cfg_attr(cx: &EarlyContext<'_>, attr: &Attribute, msrv: &Msr
750776 }
751777}
752778
779+ fn check_nested_cfg ( cx : & EarlyContext < ' _ > , items : & [ NestedMetaItem ] ) {
780+ for item in items. iter ( ) {
781+ if let NestedMetaItem :: MetaItem ( meta) = item {
782+ if !meta. has_name ( sym:: any) && !meta. has_name ( sym:: all) {
783+ continue ;
784+ }
785+ if let MetaItemKind :: List ( list) = & meta. kind {
786+ check_nested_cfg ( cx, list) ;
787+ if list. len ( ) == 1 {
788+ span_lint_and_then (
789+ cx,
790+ NON_MINIMAL_CFG ,
791+ meta. span ,
792+ "unneeded sub `cfg` when there is only one condition" ,
793+ |diag| {
794+ if let Some ( snippet) = snippet_opt ( cx, list[ 0 ] . span ( ) ) {
795+ diag. span_suggestion ( meta. span , "try" , snippet, Applicability :: MaybeIncorrect ) ;
796+ }
797+ } ,
798+ ) ;
799+ } else if list. is_empty ( ) && meta. has_name ( sym:: all) {
800+ span_lint_and_then (
801+ cx,
802+ NON_MINIMAL_CFG ,
803+ meta. span ,
804+ "unneeded sub `cfg` when there is no condition" ,
805+ |_| { } ,
806+ ) ;
807+ }
808+ }
809+ }
810+ }
811+ }
812+
813+ fn check_minimal_cfg_condition ( cx : & EarlyContext < ' _ > , attr : & Attribute ) {
814+ if attr. has_name ( sym:: cfg) &&
815+ let Some ( items) = attr. meta_item_list ( )
816+ {
817+ check_nested_cfg ( cx, & items) ;
818+ }
819+ }
820+
753821fn check_mismatched_target_os ( cx : & EarlyContext < ' _ > , attr : & Attribute ) {
754822 fn find_os ( name : & str ) -> Option < & ' static str > {
755823 UNIX_SYSTEMS
0 commit comments