Skip to content

Commit b92d944

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

8 files changed

Lines changed: 251 additions & 8 deletions

File tree

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;
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
[compiler/rustc_errors/src/lib.rs:1298:48] self.msrv = Some(
2+
RustcVersion {
3+
major: 1,
4+
minor: 89,
5+
patch: 0,
6+
},
7+
)
8+
[compiler/rustc_errors/src/lib.rs:1298:48] diagnostic.rust_version() = Some(
9+
RustcVersion {
10+
major: 1,
11+
minor: 88,
12+
patch: 0,
13+
},
14+
)
15+
error: use of empty `cfg(any())`
16+
--> $DIR/empty-cfg-predicate.rs:11:7
17+
|
18+
LL | #[cfg(any())]
19+
| ^^^^^
20+
|
21+
= note: this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
22+
note: the lint level is defined here
23+
--> $DIR/empty-cfg-predicate.rs:8:9
24+
|
25+
LL | #![deny(empty_cfg_predicate)]
26+
| ^^^^^^^^^^^^^^^^^^^
27+
help: consider using a boolean literal
28+
|
29+
LL - #[cfg(any())]
30+
LL + #[cfg(false)]
31+
|
32+
33+
[compiler/rustc_errors/src/lib.rs:1298:48] self.msrv = Some(
34+
RustcVersion {
35+
major: 1,
36+
minor: 89,
37+
patch: 0,
38+
},
39+
)
40+
[compiler/rustc_errors/src/lib.rs:1298:48] diagnostic.rust_version() = Some(
41+
RustcVersion {
42+
major: 1,
43+
minor: 88,
44+
patch: 0,
45+
},
46+
)
47+
error: use of empty `cfg(all())`
48+
--> $DIR/empty-cfg-predicate.rs:15:7
49+
|
50+
LL | #[cfg(all())]
51+
| ^^^^^
52+
|
53+
= note: this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
54+
help: consider using a boolean literal
55+
|
56+
LL - #[cfg(all())]
57+
LL + #[cfg(true)]
58+
|
59+
60+
[compiler/rustc_errors/src/lib.rs:1298:48] self.msrv = Some(
61+
RustcVersion {
62+
major: 1,
63+
minor: 89,
64+
patch: 0,
65+
},
66+
)
67+
[compiler/rustc_errors/src/lib.rs:1298:48] diagnostic.rust_version() = None
68+
error: aborting due to 2 previous errors
69+
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
[compiler/rustc_errors/src/lib.rs:1298:48] self.msrv = Some(
2+
RustcVersion {
3+
major: 1,
4+
minor: 87,
5+
patch: 0,
6+
},
7+
)
8+
[compiler/rustc_errors/src/lib.rs:1298:48] diagnostic.rust_version() = Some(
9+
RustcVersion {
10+
major: 1,
11+
minor: 88,
12+
patch: 0,
13+
},
14+
)
15+
[compiler/rustc_errors/src/lib.rs:1298:48] self.msrv = Some(
16+
RustcVersion {
17+
major: 1,
18+
minor: 87,
19+
patch: 0,
20+
},
21+
)
22+
[compiler/rustc_errors/src/lib.rs:1298:48] diagnostic.rust_version() = Some(
23+
RustcVersion {
24+
major: 1,
25+
minor: 88,
26+
patch: 0,
27+
},
28+
)
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
[compiler/rustc_errors/src/lib.rs:1298:48] self.msrv = None
2+
[compiler/rustc_errors/src/lib.rs:1298:48] diagnostic.rust_version() = Some(
3+
RustcVersion {
4+
major: 1,
5+
minor: 88,
6+
patch: 0,
7+
},
8+
)
9+
error: use of empty `cfg(any())`
10+
--> $DIR/empty-cfg-predicate.rs:11:7
11+
|
12+
LL | #[cfg(any())]
13+
| ^^^^^
14+
|
15+
= note: this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
16+
note: the lint level is defined here
17+
--> $DIR/empty-cfg-predicate.rs:8:9
18+
|
19+
LL | #![deny(empty_cfg_predicate)]
20+
| ^^^^^^^^^^^^^^^^^^^
21+
help: consider using a boolean literal
22+
|
23+
LL - #[cfg(any())]
24+
LL + #[cfg(false)]
25+
|
26+
27+
[compiler/rustc_errors/src/lib.rs:1298:48] self.msrv = None
28+
[compiler/rustc_errors/src/lib.rs:1298:48] diagnostic.rust_version() = Some(
29+
RustcVersion {
30+
major: 1,
31+
minor: 88,
32+
patch: 0,
33+
},
34+
)
35+
error: use of empty `cfg(all())`
36+
--> $DIR/empty-cfg-predicate.rs:15:7
37+
|
38+
LL | #[cfg(all())]
39+
| ^^^^^
40+
|
41+
= note: this used to be a common pattern before `cfg(true)` and `cfg(false)` were added to the language in Rust 1.88
42+
help: consider using a boolean literal
43+
|
44+
LL - #[cfg(all())]
45+
LL + #[cfg(true)]
46+
|
47+
48+
[compiler/rustc_errors/src/lib.rs:1298:48] self.msrv = None
49+
[compiler/rustc_errors/src/lib.rs:1298:48] diagnostic.rust_version() = None
50+
error: aborting due to 2 previous errors
51+
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;

0 commit comments

Comments
 (0)