Skip to content

Commit dcb2888

Browse files
committed
Auto merge of #10408 - Urgau:check-cfg-features, r=alexcrichton
Add -Z check-cfg-features to enable compile-time checking of features This pull-request implements the "[Cargo support](https://rust-lang.github.io/rfcs/3013-conditional-compilation-checking.html#cargo-support)" section of [RFC 3013: Checking conditional compilation at compile time](https://rust-lang.github.io/rfcs/3013-conditional-compilation-checking.html#checking-conditional-compilation-at-compile-time). The support is added in the form of an new unstable flags: `-Z check-cfg-features` that pass all possible features of a package to `rustc` unstable `--check-cfg` command line as `--check-cfg=values(feature, ...)`. This enables compile time checking of `feature` values in `#[cfg]`, `cfg!` and `#[cfg_attr]`. This new flag currently only affects `rustc` but `rustdoc` support will be added as soon as [it's support](rust-lang/rust#94154) is merged. Note than the intent is that this command line options become the default when stabilizing as suggested in the RFC: > [..] it seems uncontroversial for Cargo to enable checking for feature = "..." values immediately [..]
2 parents e46a9ec + 4ac4f3d commit dcb2888

File tree

6 files changed

+277
-3
lines changed

6 files changed

+277
-3
lines changed

src/cargo/core/compiler/mod.rs

Lines changed: 23 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,28 @@ fn add_allow_features(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder) {
791791
}
792792
}
793793

794+
/// Add all features as cfg
795+
fn add_features(cx: &Context<'_, '_>, cmd: &mut ProcessBuilder, unit: &Unit) {
796+
for feat in &unit.features {
797+
cmd.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
798+
}
799+
800+
if cx.bcx.config.cli_unstable().check_cfg_features {
801+
// This generate something like this:
802+
// - values(feature)
803+
// - values(feature, "foo", "bar")
804+
let mut arg = String::from("values(feature");
805+
for (&feat, _) in unit.pkg.summary().features() {
806+
arg.push_str(", \"");
807+
arg.push_str(&feat);
808+
arg.push_str("\"");
809+
}
810+
arg.push(')');
811+
812+
cmd.arg("-Zunstable-options").arg("--check-cfg").arg(&arg);
813+
}
814+
}
815+
794816
/// Add error-format flags to the command.
795817
///
796818
/// Cargo always uses JSON output. This has several benefits, such as being
@@ -987,9 +1009,7 @@ fn build_base_args(
9871009
cmd.arg("--cfg").arg("test");
9881010
}
9891011

990-
for feat in &unit.features {
991-
cmd.arg("--cfg").arg(&format!("feature=\"{}\"", feat));
992-
}
1012+
add_features(cx, cmd, unit);
9931013

9941014
let meta = cx.files().metadata(unit);
9951015
cmd.arg("-C").arg(&format!("metadata={}", meta));

src/cargo/core/features.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -638,6 +638,7 @@ unstable_cli_options!(
638638
build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"),
639639
config_include: bool = ("Enable the `include` key in config files"),
640640
credential_process: bool = ("Add a config setting to fetch registry authentication tokens by calling an external process"),
641+
check_cfg_features: bool = ("Enable compile-time checking of features in `cfg`"),
641642
doctest_in_workspace: bool = ("Compile doctests with paths relative to the workspace root"),
642643
doctest_xcompile: bool = ("Compile and run doctests for non-host target using runner config"),
643644
dual_proc_macros: bool = ("Build proc-macros for both the host and the target"),
@@ -835,6 +836,7 @@ impl CliUnstable {
835836
"minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
836837
"advanced-env" => self.advanced_env = parse_empty(k, v)?,
837838
"config-include" => self.config_include = parse_empty(k, v)?,
839+
"check-cfg-features" => self.check_cfg_features = parse_empty(k, v)?,
838840
"dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
839841
// can also be set in .cargo/config or with and ENV
840842
"mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,

src/doc/src/reference/unstable.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1127,6 +1127,20 @@ For instance:
11271127
cargo doc -Z unstable-options -Z rustdoc-scrape-examples=examples
11281128
```
11291129

1130+
### check-cfg-features
1131+
1132+
* RFC: [#3013](https://github.com/rust-lang/rfcs/pull/3013)
1133+
1134+
The `-Z check-cfg-features` argument tells Cargo to pass all possible features of a package to
1135+
`rustc` unstable `--check-cfg` command line as `--check-cfg=values(feature, ...)`. This enables
1136+
compile time checking of feature values in `#[cfg]`, `cfg!` and `#[cfg_attr]`. Note than this
1137+
command line options will probably become the default when stabilizing.
1138+
For instance:
1139+
1140+
```
1141+
cargo check -Z unstable-options -Z check-cfg-features
1142+
```
1143+
11301144
## Stabilized and removed features
11311145

11321146
### Compile progress

tests/testsuite/build.rs

Lines changed: 164 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5948,3 +5948,167 @@ fn primary_package_env_var() {
59485948

59495949
foo.cargo("test").run();
59505950
}
5951+
5952+
#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
5953+
#[cargo_test]
5954+
fn check_cfg_features() {
5955+
if !is_nightly() {
5956+
// --check-cfg is a nightly only rustc command line
5957+
return;
5958+
}
5959+
5960+
let p = project()
5961+
.file(
5962+
"Cargo.toml",
5963+
r#"
5964+
[project]
5965+
name = "foo"
5966+
version = "0.1.0"
5967+
5968+
[features]
5969+
f_a = []
5970+
f_b = []
5971+
"#,
5972+
)
5973+
.file("src/main.rs", "fn main() {}")
5974+
.build();
5975+
5976+
p.cargo("build -v -Z check-cfg-features")
5977+
.masquerade_as_nightly_cargo()
5978+
.with_stderr(
5979+
"\
5980+
[COMPILING] foo v0.1.0 [..]
5981+
[RUNNING] `rustc [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
5982+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
5983+
",
5984+
)
5985+
.run();
5986+
}
5987+
5988+
#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
5989+
#[cargo_test]
5990+
fn check_cfg_features_with_deps() {
5991+
if !is_nightly() {
5992+
// --check-cfg is a nightly only rustc command line
5993+
return;
5994+
}
5995+
5996+
let p = project()
5997+
.file(
5998+
"Cargo.toml",
5999+
r#"
6000+
[project]
6001+
name = "foo"
6002+
version = "0.1.0"
6003+
6004+
[dependencies]
6005+
bar = { path = "bar/" }
6006+
6007+
[features]
6008+
f_a = []
6009+
f_b = []
6010+
"#,
6011+
)
6012+
.file("src/main.rs", "fn main() {}")
6013+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
6014+
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
6015+
.build();
6016+
6017+
p.cargo("build -v -Z check-cfg-features")
6018+
.masquerade_as_nightly_cargo()
6019+
.with_stderr(
6020+
"\
6021+
[COMPILING] bar v0.1.0 [..]
6022+
[RUNNING] `rustc [..] --check-cfg 'values(feature)' [..]
6023+
[COMPILING] foo v0.1.0 [..]
6024+
[RUNNING] `rustc --crate-name foo [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
6025+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
6026+
",
6027+
)
6028+
.run();
6029+
}
6030+
6031+
#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
6032+
#[cargo_test]
6033+
fn check_cfg_features_with_opt_deps() {
6034+
if !is_nightly() {
6035+
// --check-cfg is a nightly only rustc command line
6036+
return;
6037+
}
6038+
6039+
let p = project()
6040+
.file(
6041+
"Cargo.toml",
6042+
r#"
6043+
[project]
6044+
name = "foo"
6045+
version = "0.1.0"
6046+
6047+
[dependencies]
6048+
bar = { path = "bar/", optional = true }
6049+
6050+
[features]
6051+
default = ["bar"]
6052+
f_a = []
6053+
f_b = []
6054+
"#,
6055+
)
6056+
.file("src/main.rs", "fn main() {}")
6057+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
6058+
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
6059+
.build();
6060+
6061+
p.cargo("build -v -Z check-cfg-features")
6062+
.masquerade_as_nightly_cargo()
6063+
.with_stderr(
6064+
"\
6065+
[COMPILING] bar v0.1.0 [..]
6066+
[RUNNING] `rustc [..] --check-cfg 'values(feature)' [..]
6067+
[COMPILING] foo v0.1.0 [..]
6068+
[RUNNING] `rustc --crate-name foo [..] --check-cfg 'values(feature, \"bar\", \"default\", \"f_a\", \"f_b\")' [..]
6069+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
6070+
",
6071+
)
6072+
.run();
6073+
}
6074+
6075+
#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
6076+
#[cargo_test]
6077+
fn check_cfg_features_with_namespaced_features() {
6078+
if !is_nightly() {
6079+
// --check-cfg is a nightly only rustc command line
6080+
return;
6081+
}
6082+
6083+
let p = project()
6084+
.file(
6085+
"Cargo.toml",
6086+
r#"
6087+
[project]
6088+
name = "foo"
6089+
version = "0.1.0"
6090+
6091+
[dependencies]
6092+
bar = { path = "bar/", optional = true }
6093+
6094+
[features]
6095+
f_a = ["dep:bar"]
6096+
f_b = []
6097+
"#,
6098+
)
6099+
.file("src/main.rs", "fn main() {}")
6100+
.file("bar/Cargo.toml", &basic_manifest("bar", "0.1.0"))
6101+
.file("bar/src/lib.rs", "#[allow(dead_code)] fn bar() {}")
6102+
.build();
6103+
6104+
p.cargo("build -v -Z check-cfg-features")
6105+
.masquerade_as_nightly_cargo()
6106+
.with_stderr(
6107+
"\
6108+
[COMPILING] foo v0.1.0 [..]
6109+
[RUNNING] `rustc --crate-name foo [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
6110+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
6111+
",
6112+
)
6113+
.run();
6114+
}

tests/testsuite/check.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
use std::fmt::{self, Write};
44

55
use cargo_test_support::install::exe;
6+
use cargo_test_support::is_nightly;
67
use cargo_test_support::paths::CargoPathExt;
78
use cargo_test_support::registry::Package;
89
use cargo_test_support::tools;
@@ -997,3 +998,39 @@ fn rustc_workspace_wrapper_excludes_published_deps() {
997998
.with_stdout_does_not_contain("WRAPPER CALLED: rustc --crate-name baz [..]")
998999
.run();
9991000
}
1001+
1002+
#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
1003+
#[cargo_test]
1004+
fn check_cfg_features() {
1005+
if !is_nightly() {
1006+
// --check-cfg is a nightly only rustc command line
1007+
return;
1008+
}
1009+
1010+
let p = project()
1011+
.file(
1012+
"Cargo.toml",
1013+
r#"
1014+
[project]
1015+
name = "foo"
1016+
version = "0.1.0"
1017+
1018+
[features]
1019+
f_a = []
1020+
f_b = []
1021+
"#,
1022+
)
1023+
.file("src/main.rs", "fn main() {}")
1024+
.build();
1025+
1026+
p.cargo("check -v -Z check-cfg-features")
1027+
.masquerade_as_nightly_cargo()
1028+
.with_stderr(
1029+
"\
1030+
[CHECKING] foo v0.1.0 [..]
1031+
[RUNNING] `rustc [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
1032+
[FINISHED] dev [unoptimized + debuginfo] target(s) in [..]
1033+
",
1034+
)
1035+
.run();
1036+
}

tests/testsuite/test.rs

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4499,3 +4499,40 @@ fn test_workspaces_cwd() {
44994499
.with_stdout_contains("test test_integration_deep_cwd ... ok")
45004500
.run();
45014501
}
4502+
4503+
#[cfg_attr(windows, ignore)] // weird normalization issue with windows and cargo-test-support
4504+
#[cargo_test]
4505+
fn check_cfg_features() {
4506+
if !is_nightly() {
4507+
// --check-cfg is a nightly only rustc command line
4508+
return;
4509+
}
4510+
4511+
let p = project()
4512+
.file(
4513+
"Cargo.toml",
4514+
r#"
4515+
[project]
4516+
name = "foo"
4517+
version = "0.1.0"
4518+
4519+
[features]
4520+
f_a = []
4521+
f_b = []
4522+
"#,
4523+
)
4524+
.file("src/main.rs", "fn main() {}")
4525+
.build();
4526+
4527+
p.cargo("test -v -Z check-cfg-features")
4528+
.masquerade_as_nightly_cargo()
4529+
.with_stderr(
4530+
"\
4531+
[COMPILING] foo v0.1.0 [..]
4532+
[RUNNING] `rustc [..] --check-cfg 'values(feature, \"f_a\", \"f_b\")' [..]
4533+
[FINISHED] test [unoptimized + debuginfo] target(s) in [..]
4534+
[RUNNING] [..]
4535+
",
4536+
)
4537+
.run();
4538+
}

0 commit comments

Comments
 (0)