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() {

@JonathanBrouwer JonathanBrouwer Jun 20, 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.

Could you add a test that checks that this lint when the attribute is produced by a macro expansion?

View changes since the review

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 @@ -780,6 +780,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! {

@JonathanBrouwer JonathanBrouwer Jun 20, 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.

In rust-lang/rfcs#3695, this lint is mentioned as a "future possibility".
I'm not super familiar with the process, but I think this means that the change has not actually been FCP'ed yet by the lang team. Is this correct? If so, please lang-nominate this

View changes since the review

/// 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,
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