Skip to content

Commit c560529

Browse files
committed
Announce automatic checking of cfgs at compile-time
1 parent 4587ed7 commit c560529

File tree

1 file changed

+115
-0
lines changed

1 file changed

+115
-0
lines changed

posts/2024-04-29-check-cfg.md

Lines changed: 115 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,115 @@
1+
---
2+
layout: post
3+
title: "Automatic checking of cfgs at compile-time"
4+
author: Urgau
5+
team: The Cargo Team <https://www.rust-lang.org/governance/teams/dev-tools#cargo>
6+
---
7+
8+
# Automatic checking of cfgs at compile-time
9+
10+
The Cargo and Compiler team are delighted to announce that starting with Rust 1.80 (or nightly-2024-04-XX) every _reachable_ `#[cfg]`s will be automatically checked for expected config names and values.
11+
12+
This can help with verifying that the crate is correctly handling conditional compilation for different target platforms or features. It ensures that the cfg settings are consistent between what is intended and what is used, helping to catch potential bugs or errors early in the development process.
13+
14+
Addressing a common pitfall for new and advanced users.
15+
16+
This is another step to our commitment to provide user-focused tooling and we are eager and excited to finally see it fixed, more than two years since the original [RFC 3013](https://github.com/rust-lang/rfcs/pull/3013)[^1].
17+
18+
[^1]: The stabilized implementation and RFC 3013 diverge significantly, in particular there is only one form for `--check-cfg`: `cfg()` (instead of `values()` and `names()` being incomplete and subtlety incompatible with each other).
19+
20+
## A look at the feature
21+
22+
Every time a Cargo feature is declared that feature is transformed into a config that is passed to `rustc` (the Rust compiler) so it can verify with its [well known cfgs](TODO) if any of the `#[cfg]`, `#![cfg_attr]` and `cfg!` have unexpected configs and report a warning with the `unexpected_cfgs` lint.
23+
24+
*`Cargo.toml`*:
25+
26+
```toml
27+
[package]
28+
name = "foo"
29+
30+
[features]
31+
lasers = []
32+
zapping = []
33+
```
34+
35+
*`src/lib.rs`:*
36+
```rust
37+
#[cfg(feature = "lasers")] // This condition is expected
38+
// as "lasers" is an expected value of `feature`
39+
fn shoot_lasers() {}
40+
41+
#[cfg(feature = "monkeys")] // This condition is UNEXPECTED
42+
// as "monkeys" is NOT an expected value of `feature`
43+
fn write_shakespeare() {}
44+
45+
#[cfg(windosw)] // This condition is UNEXPECTED
46+
// it's supposed to be `windows`
47+
fn win() {}
48+
```
49+
50+
*`cargo check`*:
51+
![cargo-check](https://github.com/rust-lang/rust/assets/3616612/c6ecdb34-b92c-42b8-9f80-7066b76541ff)
52+
53+
## Custom cfgs and build scripts
54+
55+
> In Cargo point-of-view: a custom cfg is one that is neither defined by `rustc` nor by a Cargo feature. Think of `tokio_unstable`, `has_foo`, ... but not `feature = "lasers"`, `unix`, ...
56+
57+
Some crates use custom cfgs that they either expected from the environment (`RUSTFLAGS`or other means) or is enabled by some logic in the crate `build.rs`. For those crates Cargo provides a new instruction: [`cargo::rustc-check-cfg`](TODO)[^2] (or `cargo:rustc-check-cfg` for older Cargo version).
58+
59+
[^2]: `cargo::rustc-check-cfg` take action since Rust 1.80, between Rust 1.77 and Rust 1.79 *(included)* it silently did nothing and before that (so Rust 1.76 and below) it warned when used without the unstable Cargo `-Zcheck-cfg`.
60+
61+
The syntax to use is described in the [rustc book](https://doc.rust-lang.org/rustc/index.html) section [checking configuration](TODO), but in a nut shell the basic syntax of `--check-cfg` is:
62+
63+
> `cfg(name, values("value1", "value2", ..., "valueN"))`
64+
65+
Note that every custom cfgs must always be expected, regardless if the cfg is active or not!
66+
67+
### `build.rs` example
68+
69+
`build.rs`:
70+
```rust
71+
fn main() {
72+
println!("cargo::rustc-check-cfg=cfg(has_foo)"); // <-- new with Cargo 1.80
73+
if has_foo() {
74+
println!("cargo::rustc-cfg=has_foo");
75+
}
76+
}
77+
```
78+
79+
> Note: Each `cargo::rustc-cfg` must always have a accompanying _unconditional_ `cargo::rustc-check-cfg` directive otherwise you risk having warnings like this:`unexpected cfg condition name: has_foo`.
80+
81+
### Equivalence table
82+
83+
| `cargo::rustc-cfg` | `cargo::rustc-check-cfg` |
84+
|-------------------------|------------------------------------------------|
85+
| `foo` | `cfg(foo)` or `cfg(foo, values(none()))` |
86+
| `foo=""` | `cfg(foo, values(""))` |
87+
| `foo="bar"` | `cfg(foo, values("bar"))` |
88+
| `foo="1"` and `foo="2"` | `cfg(foo, values("1", "2"))` |
89+
| `foo="1"` and `bar="2"` | `cfg(foo, values("1"))` and `cfg(bar, values("2"))` |
90+
| `foo` and `foo="bar"` | `cfg(foo, values(none(), "bar"))` |
91+
92+
More details can be found on the [`rustc` book](TODO).
93+
94+
## Frequently asked questions
95+
96+
### Can it be disabled?
97+
98+
No, it's **always on** for Cargo and _cannot_ be disabled.
99+
100+
### How does it interact with the `RUSTFLAGS` env ?
101+
102+
You should be able to use the `RUSTFLAGS` environment variable like it was before.
103+
*Currently `--cfg` arguments are not checked, only usage in code are.*
104+
105+
This means that doing `RUSTFLAGS="--cfg tokio_unstable" cargo check` will not report any warnings, unless `tokio_unstable` is used within your local crates, in which case crate author will need to make sure that that custom cfg is expected with `cargo::rustc-check-cfg` in the `build.rs` of that crate.
106+
107+
### How to expect custom cfgs without a `build.rs` ?
108+
109+
There is not **currently no way** to expect a custom cfg other than with `cargo::rustc-check-cfg` in a `build.rs`.
110+
111+
Crate author that don't want to use a `build.rs` are encouraged to use Cargo features instead.
112+
113+
### Does it affect dependencies ?
114+
115+
No, the lint only affects local packages; only those will report the lint.

0 commit comments

Comments
 (0)