Skip to content

Commit 2d0d591

Browse files
committed
Lint against empty cfg(any()/all())
1 parent 883de8e commit 2d0d591

14 files changed

Lines changed: 182 additions & 21 deletions

compiler/rustc_attr_parsing/src/attributes/cfg.rs

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use rustc_errors::{Applicability, Diagnostic, PResult, msg};
77
use rustc_feature::{Features, GatedCfg, find_gated_cfg};
88
use rustc_hir::attrs::CfgEntry;
99
use rustc_hir::{AttrPath, RustcVersion, Target};
10+
use rustc_lint_defs::builtin::EMPTY_CFG_PREDICATE;
1011
use rustc_parse::parser::{ForceCollect, Parser, Recovery};
1112
use rustc_parse::{exp, parse_in};
1213
use rustc_session::Session;
@@ -19,6 +20,7 @@ use thin_vec::ThinVec;
1920

2021
use crate::attributes::AttributeSafety;
2122
use crate::context::{AcceptContext, ShouldEmit};
23+
use crate::diagnostics::EmptyCfgPredictate;
2224
use crate::parser::{
2325
AllowExprMetavar, ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser,
2426
};
@@ -92,14 +94,42 @@ pub fn parse_cfg_entry(
9294
};
9395
CfgEntry::Not(Box::new(parse_cfg_entry(cx, single)?), list.span)
9496
}
95-
Some(sym::any) => CfgEntry::Any(
96-
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
97-
list.span,
98-
),
99-
Some(sym::all) => CfgEntry::All(
100-
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
101-
list.span,
102-
),
97+
Some(sym::any) => {
98+
if list.is_empty() && !list.span.from_expansion() {
99+
let span = meta.span();
100+
cx.emit_lint(
101+
EMPTY_CFG_PREDICATE,
102+
EmptyCfgPredictate {
103+
predicate_span: span,
104+
predicate: sym::any,
105+
lit: false,
106+
},
107+
span,
108+
);
109+
}
110+
CfgEntry::Any(
111+
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
112+
list.span,
113+
)
114+
}
115+
Some(sym::all) => {
116+
if list.is_empty() && !list.span.from_expansion() {
117+
let span = meta.span();
118+
cx.emit_lint(
119+
EMPTY_CFG_PREDICATE,
120+
EmptyCfgPredictate {
121+
predicate_span: span,
122+
predicate: sym::all,
123+
lit: true,
124+
},
125+
span,
126+
);
127+
}
128+
CfgEntry::All(
129+
list.mixed().flat_map(|sub_item| parse_cfg_entry(cx, sub_item)).collect(),
130+
list.span,
131+
)
132+
}
103133
Some(sym::target) => parse_cfg_entry_target(cx, list, meta.span())?,
104134
Some(sym::version) => parse_cfg_entry_version(cx, list, meta.span())?,
105135
_ => {

compiler/rustc_attr_parsing/src/diagnostics.rs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -780,6 +780,23 @@ pub(crate) mod unexpected_cfg_value {
780780
}
781781
}
782782

783+
#[derive(Diagnostic)]
784+
#[diag("use of empty `cfg({$predicate}())`")]
785+
#[note(
786+
"this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88"
787+
)]
788+
pub(crate) struct EmptyCfgPredictate {
789+
#[suggestion(
790+
"consider using a boolean literal",
791+
code = "{lit}",
792+
applicability = "machine-applicable",
793+
style = "verbose"
794+
)]
795+
pub predicate_span: Span,
796+
pub predicate: Symbol,
797+
pub lit: bool,
798+
}
799+
783800
#[derive(Diagnostic)]
784801
pub(crate) enum InvalidOnClause {
785802
#[diag("empty `on`-clause in `#[rustc_on_unimplemented]`", code = E0232)]

compiler/rustc_lint_defs/src/builtin.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ declare_lint_pass! {
4545
DUPLICATE_MACRO_ATTRIBUTES,
4646
ELIDED_LIFETIMES_IN_ASSOCIATED_CONSTANT,
4747
ELIDED_LIFETIMES_IN_PATHS,
48+
EMPTY_CFG_PREDICATE,
4849
EXPLICIT_BUILTIN_CFGS_IN_FLAGS,
4950
EXPORTED_PRIVATE_DEPENDENCIES,
5051
FFI_UNWIND_CALLS,
@@ -5554,3 +5555,31 @@ declare_lint! {
55545555
"usage of `unsafe` code and other potentially unsound constructs",
55555556
@eval_always = true
55565557
}
5558+
5559+
declare_lint! {
5560+
/// The `empty_cfg_predicate` lint detects the use of empty `cfg` predicate lists.
5561+
///
5562+
/// ### Example
5563+
///
5564+
/// ```rust,compile_fail
5565+
/// #![deny(empty_cfg_predicate)]
5566+
/// #[cfg(any())]
5567+
/// fn foo() {}
5568+
///
5569+
/// #[cfg(all())]
5570+
/// fn bar() {}
5571+
/// ```
5572+
///
5573+
/// {{produces}}
5574+
///
5575+
/// ### Explanation
5576+
///
5577+
/// The meaning of `cfg(any())` and `cfg(all())` is not immediately obvious;
5578+
/// `cfg(false)` and `cfg(true)` respectively may be used instead.
5579+
/// This used to be a common pattern before `cfg(true)` and `cfg(false)`
5580+
/// were added to the language in Rust 1.88
5581+
pub EMPTY_CFG_PREDICATE,
5582+
Warn,
5583+
"detects use of empty `cfg(any())` and `cfg(all())`",
5584+
@msrv = "1.88.0";
5585+
}

tests/ui/conditional-compilation/cfg-empty-any-all.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
//! Test the behaviour of `cfg(any())` and `cfg(all())`
2+
#![allow(empty_cfg_predicate)]
23

34
#[cfg(any())] // Equivalent to cfg(false)
45
struct Disabled;

tests/ui/conditional-compilation/cfg-empty-any-all.stderr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
error[E0425]: cannot find value `Disabled` in this scope
2-
--> $DIR/cfg-empty-any-all.rs:10:13
2+
--> $DIR/cfg-empty-any-all.rs:11:13
33
|
44
LL | let _ = Disabled;
55
| ^^^^^^^^ not found in this scope
66
|
77
note: found an item that was configured out
8-
--> $DIR/cfg-empty-any-all.rs:4:8
8+
--> $DIR/cfg-empty-any-all.rs:5:8
99
|
1010
LL | #[cfg(any())] // Equivalent to cfg(false)
1111
| -- the item is gated here
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error: use of empty `cfg(any())`
2+
--> $DIR/empty-cfg-predicate.rs:11:7
3+
|
4+
LL | #[cfg(any())]
5+
| ^^^^^
6+
|
7+
= note: this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
8+
note: the lint level is defined here
9+
--> $DIR/empty-cfg-predicate.rs:8:9
10+
|
11+
LL | #![deny(empty_cfg_predicate)]
12+
| ^^^^^^^^^^^^^^^^^^^
13+
help: consider using a boolean literal
14+
|
15+
LL - #[cfg(any())]
16+
LL + #[cfg(false)]
17+
|
18+
19+
error: use of empty `cfg(all())`
20+
--> $DIR/empty-cfg-predicate.rs:15:7
21+
|
22+
LL | #[cfg(all())]
23+
| ^^^^^
24+
|
25+
= note: this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
26+
help: consider using a boolean literal
27+
|
28+
LL - #[cfg(all())]
29+
LL + #[cfg(true)]
30+
|
31+
32+
error: aborting due to 2 previous errors
33+
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
error: use of empty `cfg(any())`
2+
--> $DIR/empty-cfg-predicate.rs:11:7
3+
|
4+
LL | #[cfg(any())]
5+
| ^^^^^
6+
|
7+
= note: this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
8+
note: the lint level is defined here
9+
--> $DIR/empty-cfg-predicate.rs:8:9
10+
|
11+
LL | #![deny(empty_cfg_predicate)]
12+
| ^^^^^^^^^^^^^^^^^^^
13+
help: consider using a boolean literal
14+
|
15+
LL - #[cfg(any())]
16+
LL + #[cfg(false)]
17+
|
18+
19+
error: use of empty `cfg(all())`
20+
--> $DIR/empty-cfg-predicate.rs:15:7
21+
|
22+
LL | #[cfg(all())]
23+
| ^^^^^
24+
|
25+
= note: this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
26+
help: consider using a boolean literal
27+
|
28+
LL - #[cfg(all())]
29+
LL + #[cfg(true)]
30+
|
31+
32+
error: aborting due to 2 previous errors
33+
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
//@ revisions: novers lowvers highvers
2+
//@[lowvers] compile-flags: -Zlint-rust-version=1.87.0
3+
//@[highvers] compile-flags: -Zlint-rust-version=1.89.0
4+
//@[lowvers] check-pass
5+
6+
//! Check that we suggest `cfg(any())` -> `false` and `cfg(all())` -> true
7+
//! Additionally tests the behaviour of empty cfg predicates.
8+
#![deny(empty_cfg_predicate)]
9+
#![crate_type = "lib"]
10+
11+
#[cfg(any())]
12+
//[novers]~^ ERROR: use of empty `cfg(any())`
13+
//[highvers]~^^ ERROR: use of empty `cfg(any())`
14+
pub struct A;
15+
#[cfg(all())]
16+
//[novers]~^ ERROR: use of empty `cfg(all())`
17+
//[highvers]~^^ ERROR: use of empty `cfg(all())`
18+
pub struct B;

tests/ui/lint/unused-features/unused-language-features.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
//~^ ERROR feature `asm_unwind` is declared but not used
1313

1414
// Enabled via cfg_attr, unused
15-
#![cfg_attr(all(), feature(negative_impls))]
15+
#![cfg_attr(true, feature(negative_impls))]
1616
//~^ ERROR feature `negative_impls` is declared but not used
1717

1818
// Not enabled via cfg_attr, so should not warn even if unused
19-
#![cfg_attr(any(), feature(never_type))]
19+
#![cfg_attr(false, feature(never_type))]
2020

2121
macro_rules! use_asm_unwind {
2222
() => {

tests/ui/lint/unused-features/unused-language-features.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,10 @@ LL | #![feature(asm_unwind)]
2929
| ^^^^^^^^^^
3030

3131
error: feature `negative_impls` is declared but not used
32-
--> $DIR/unused-language-features.rs:15:28
32+
--> $DIR/unused-language-features.rs:15:27
3333
|
34-
LL | #![cfg_attr(all(), feature(negative_impls))]
35-
| ^^^^^^^^^^^^^^
34+
LL | #![cfg_attr(true, feature(negative_impls))]
35+
| ^^^^^^^^^^^^^^
3636

3737
error: aborting due to 5 previous errors
3838

0 commit comments

Comments
 (0)