Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 38 additions & 8 deletions compiler/rustc_attr_parsing/src/attributes/cfg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use rustc_errors::{Applicability, Diagnostic, PResult, msg};
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
use rustc_hir::attrs::CfgEntry;
use rustc_hir::{AttrPath, RustcVersion, Target};
use rustc_lint_defs::builtin::EMPTY_CFG_PREDICATE;
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
use rustc_parse::{exp, parse_in};
use rustc_session::Session;
Expand All @@ -19,6 +20,7 @@ use thin_vec::ThinVec;

use crate::attributes::AttributeSafety;
use crate::context::{AcceptContext, ShouldEmit};
use crate::diagnostics::EmptyCfgPredictate;
use crate::parser::{
AllowExprMetavar, ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser,
};
Expand Down Expand Up @@ -92,14 +94,42 @@ pub fn parse_cfg_entry(
};
CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span)
}
Some(sym::any) => CfgEntry::Any(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
),
Some(sym::all) => CfgEntry::All(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
),
Some(sym::any) => {
if list.is_empty() && !list.span.from_expansion() {
Comment thread
JonathanBrouwer marked this conversation as resolved.
let span = meta.span();
cx.emit_lint(
EMPTY_CFG_PREDICATE,
EmptyCfgPredictate {
predicate_span: span,
predicate: sym::any,
lit: false,
},
span,
);
}
CfgEntry::Any(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
)
}
Some(sym::all) => {
if list.is_empty() && !list.span.from_expansion() {
let span = meta.span();
cx.emit_lint(
EMPTY_CFG_PREDICATE,
EmptyCfgPredictate {
predicate_span: span,
predicate: sym::all,
lit: true,
},
span,
);
}
CfgEntry::All(
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
list.span,
)
}
Some(sym::target) => parse_cfg_entry_target(cx, list, meta.span())?,
Some(sym::version) => parse_cfg_entry_version(cx, list, meta.span())?,
_ => {
Expand Down
17 changes: 17 additions & 0 deletions compiler/rustc_attr_parsing/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,23 @@ pub(crate) mod unexpected_cfg_value {
}
}

#[derive(Diagnostic)]
#[diag("use of empty `cfg({$predicate}())`")]
#[note(
"this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88"
)]
pub(crate) struct EmptyCfgPredictate {
#[suggestion(
"consider using a boolean literal",
code = "{lit}",
applicability = "machine-applicable",
style = "verbose"
)]
pub predicate_span: Span,
pub predicate: Symbol,
pub lit: bool,
}

#[derive(Diagnostic)]
pub(crate) enum InvalidOnClause {
#[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)]
Expand Down
29 changes: 29 additions & 0 deletions compiler/rustc_lint_defs/src/builtin.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ pub mod hardwired {
DUPLICATE_MACRO_ATTRIBUTES,
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
ELIDED_LIFETIMES_IN_PATHS,
EMPTY_CFG_PREDICATE,
EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
EXPORTED_PRIVATE_DEPENDENCIES,
FFI_UNWIND_CALLS,
Expand Down Expand Up @@ -5578,3 +5579,31 @@ declare_lint! {
"usage of `unsafe` code and other potentially unsound constructs",
@eval_always = true
}

declare_lint! {
Comment thread
clubby789 marked this conversation as resolved.
/// The `empty_cfg_predicate` lint detects the use of empty `cfg` predicate lists.
///
/// ### Example
///
/// ```rust,compile_fail
/// #![deny(empty_cfg_predicate)]
/// #[cfg(any())]
/// fn foo() {}
///
/// #[cfg(all())]
/// fn bar() {}
/// ```
///
/// {{produces}}
///
/// ### Explanation
///
/// The meaning of `cfg(any())` and `cfg(all())` is not immediately obvious;
/// `cfg(false)` and `cfg(true)` respectively may be used instead.
/// This used to be a common pattern before `cfg(true)` and `cfg(false)`
/// were added to the language in Rust 1.88
pub EMPTY_CFG_PREDICATE,

@traviscross traviscross Jun 24, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
pub EMPTY_CFG_PREDICATE,
pub EMPTY_CFG_PREDICATES,

Per our RFC 0344 guidance, this should be in the plural.

Also, the naming seems a bit loose. This is targeting two specific cfg predicates, not empty cfg predicates in general (and, of course, we might later add more cfg predicates that we might not want to lint when empty). Have any more targeted ideas for a name?

View changes since the review

@clubby789 clubby789 Jun 24, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe EMPTY_CFG_COMBINATORS? Or EMPTY_CFG_LISTS (but that's a little less precise)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or MANUAL_CFG_BOOL?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's probably my preferred suggestion so far (or MANUAL_CFG_BOOLS?), but open to others

Warn,
"detects use of empty `cfg(any())` and `cfg(all())`",
@msrv = "1.88.0";
}
2 changes: 1 addition & 1 deletion library/std/src/os/raw/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ macro_rules! alias_core_ffi {
($($t:ident)*) => {$(
#[stable(feature = "raw_os", since = "1.1.0")]
#[doc = include_str!(concat!("../../../../core/src/ffi/", stringify!($t), ".md"))]
#[doc(cfg(all()))]
#[doc(cfg(true))]
pub type $t = core::ffi::$t;
)*}
}
Expand Down
4 changes: 2 additions & 2 deletions library/std/src/os/windows/raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,10 @@ use crate::os::raw::c_void;
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type HANDLE = *mut c_void;
#[cfg(target_pointer_width = "32")]
#[doc(cfg(all()))]
#[doc(cfg(true))]
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type SOCKET = u32;
#[cfg(target_pointer_width = "64")]
#[doc(cfg(all()))]
#[doc(cfg(true))]
Comment thread
JonathanBrouwer marked this conversation as resolved.
#[stable(feature = "raw_ext", since = "1.1.0")]
pub type SOCKET = u64;
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod field_attributes {
// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800
mod cfgs_between_fields {
#[allow(clippy::non_minimal_cfg)]
#[allow(empty_cfg_predicate)]
fn cfg_all() {
struct S {
a: i32,
Expand All @@ -62,6 +63,7 @@ mod cfgs_between_fields {
};
}

#[allow(empty_cfg_predicate)]
fn cfg_any() {
struct S {
a: i32,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ mod field_attributes {
// https://github.com/rust-lang/rust-clippy/pull/13737#discussion_r1874539800
mod cfgs_between_fields {
#[allow(clippy::non_minimal_cfg)]
#[allow(empty_cfg_predicate)]
fn cfg_all() {
struct S {
a: i32,
Expand All @@ -62,6 +63,7 @@ mod cfgs_between_fields {
};
}

#[allow(empty_cfg_predicate)]
fn cfg_any() {
struct S {
a: i32,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ LL ~ expn_depth: if condition { 1 } else { 0 },
|

error: struct constructor field order is inconsistent with struct definition field order
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:56:13
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:57:13
|
LL | / d: 0,
LL | |
Expand All @@ -61,7 +61,7 @@ LL ~ d: 0,
|

error: struct constructor field order is inconsistent with struct definition field order
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:74:13
--> tests/ui-toml/toml_inconsistent_struct_constructor/conf_inconsistent_struct_constructor.rs:76:13
|
LL | / d: 0,
LL | |
Expand Down
4 changes: 2 additions & 2 deletions src/tools/clippy/tests/ui/double_must_use_unfixable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
#![expect(clippy::result_unit_err)]
#![feature(never_type)]

#[cfg_attr(all(), must_use, deprecated)]
#[cfg_attr(true, must_use, deprecated)]
pub fn issue_12320() -> Result<(), ()> {
//~^ double_must_use
unimplemented!();
}

#[cfg_attr(all(), deprecated, must_use)]
#[cfg_attr(true, deprecated, must_use)]
pub fn issue_12320_2() -> Result<(), ()> {
//~^ double_must_use
unimplemented!();
Expand Down
12 changes: 6 additions & 6 deletions src/tools/clippy/tests/ui/double_must_use_unfixable.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ LL | pub fn issue_12320() -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove `must_use`
--> tests/ui/double_must_use_unfixable.rs:5:19
--> tests/ui/double_must_use_unfixable.rs:5:18
|
LL | #[cfg_attr(all(), must_use, deprecated)]
| ^^^^^^^^
LL | #[cfg_attr(true, must_use, deprecated)]
| ^^^^^^^^
= note: alternatively, you may add an explicit reason to the `must_use` attribute
= note: `-D clippy::double-must-use` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::double_must_use)]`
Expand All @@ -20,10 +20,10 @@ LL | pub fn issue_12320_2() -> Result<(), ()> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
help: remove `must_use`
--> tests/ui/double_must_use_unfixable.rs:11:31
--> tests/ui/double_must_use_unfixable.rs:11:30
|
LL | #[cfg_attr(all(), deprecated, must_use)]
| ^^^^^^^^
LL | #[cfg_attr(true, deprecated, must_use)]
| ^^^^^^^^
= note: alternatively, you may add an explicit reason to the `must_use` attribute

error: aborting due to 2 previous errors
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ pub struct EmptyInBlockComment;
pub struct BlockComment;

/// Ignore the empty line inside a cfg_attr'd out attribute
#[cfg_attr(any(), multiline(
#[cfg_attr(false, multiline(
foo = 1

bar = 2
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ pub struct EmptyInBlockComment;
pub struct BlockComment;

/// Ignore the empty line inside a cfg_attr'd out attribute
#[cfg_attr(any(), multiline(
#[cfg_attr(false, multiline(
foo = 1

bar = 2
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/empty_line_after/doc_comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ pub struct EmptyInBlockComment;
pub struct BlockComment;

/// Ignore the empty line inside a cfg_attr'd out attribute
#[cfg_attr(any(), multiline(
#[cfg_attr(false, multiline(
foo = 1
bar = 2
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/let_and_return.edition2021.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ fn_in_macro!({

fn issue9150() -> usize {
let x = 1;
#[cfg(any())]
#[cfg(false)]
panic!("can't see me");
x
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/let_and_return.edition2024.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ fn_in_macro!({

fn issue9150() -> usize {
let x = 1;
#[cfg(any())]
#[cfg(false)]
panic!("can't see me");
x
}
Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/let_and_return.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ fn_in_macro!({

fn issue9150() -> usize {
let x = 1;
#[cfg(any())]
#[cfg(false)]
panic!("can't see me");
x
}
Expand Down
4 changes: 2 additions & 2 deletions src/tools/clippy/tests/ui/must_use_unit_unfixable.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#[cfg_attr(all(), must_use, deprecated)]
#[cfg_attr(true, must_use, deprecated)]
fn issue_12320() {}
//~^ must_use_unit

#[cfg_attr(all(), deprecated, doc = "foo", must_use)]
#[cfg_attr(true, deprecated, doc = "foo", must_use)]
fn issue_12320_2() {}
//~^ must_use_unit

Expand Down
12 changes: 6 additions & 6 deletions src/tools/clippy/tests/ui/must_use_unit_unfixable.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ LL | fn issue_12320() {}
| ^^^^^^^^^^^^^^^^
|
help: remove `must_use`
--> tests/ui/must_use_unit_unfixable.rs:1:19
--> tests/ui/must_use_unit_unfixable.rs:1:18
|
LL | #[cfg_attr(all(), must_use, deprecated)]
| ^^^^^^^^
LL | #[cfg_attr(true, must_use, deprecated)]
| ^^^^^^^^
= note: `-D clippy::must-use-unit` implied by `-D warnings`
= help: to override `-D warnings` add `#[allow(clippy::must_use_unit)]`

Expand All @@ -19,10 +19,10 @@ LL | fn issue_12320_2() {}
| ^^^^^^^^^^^^^^^^^^
|
help: remove `must_use`
--> tests/ui/must_use_unit_unfixable.rs:5:44
--> tests/ui/must_use_unit_unfixable.rs:5:43
|
LL | #[cfg_attr(all(), deprecated, doc = "foo", must_use)]
| ^^^^^^^^
LL | #[cfg_attr(true, deprecated, doc = "foo", must_use)]
| ^^^^^^^^

error: aborting due to 2 previous errors

2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/needless_ifs.fixed
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ fn main() {
}

if true {
#[cfg(any())]
#[cfg(false)]
foo;
}

Expand Down
2 changes: 1 addition & 1 deletion src/tools/clippy/tests/ui/needless_ifs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ fn main() {
}

if true {
#[cfg(any())]
#[cfg(false)]
foo;
}

Expand Down
1 change: 1 addition & 0 deletions src/tools/clippy/tests/ui/non_minimal_cfg.fixed
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(unused)]
#![allow(empty_cfg_predicate)]

#[cfg(windows)]
//~^ non_minimal_cfg
Expand Down
1 change: 1 addition & 0 deletions src/tools/clippy/tests/ui/non_minimal_cfg.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#![allow(unused)]
#![allow(empty_cfg_predicate)]

#[cfg(all(windows))]
//~^ non_minimal_cfg
Expand Down
8 changes: 4 additions & 4 deletions src/tools/clippy/tests/ui/non_minimal_cfg.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error: unneeded sub `cfg` when there is only one condition
--> tests/ui/non_minimal_cfg.rs:3:7
--> tests/ui/non_minimal_cfg.rs:4:7
|
LL | #[cfg(all(windows))]
| ^^^^^^^^^^^^ help: try: `windows`
Expand All @@ -8,19 +8,19 @@ LL | #[cfg(all(windows))]
= help: to override `-D warnings` add `#[allow(clippy::non_minimal_cfg)]`

error: unneeded sub `cfg` when there is only one condition
--> tests/ui/non_minimal_cfg.rs:7:7
--> tests/ui/non_minimal_cfg.rs:8:7
|
LL | #[cfg(any(windows))]
| ^^^^^^^^^^^^ help: try: `windows`

error: unneeded sub `cfg` when there is only one condition
--> tests/ui/non_minimal_cfg.rs:11:11
--> tests/ui/non_minimal_cfg.rs:12:11
|
LL | #[cfg(all(any(unix), all(not(windows))))]
| ^^^^^^^^^ help: try: `unix`

error: unneeded sub `cfg` when there is only one condition
--> tests/ui/non_minimal_cfg.rs:11:22
--> tests/ui/non_minimal_cfg.rs:12:22
|
LL | #[cfg(all(any(unix), all(not(windows))))]
| ^^^^^^^^^^^^^^^^^ help: try: `not(windows)`
Expand Down
Loading
Loading