Auto merge of #10799 - Urgau:check-cfg-fix-config-deserialization, r=ehuss

Fix deserialization of check-cfg in config.toml

When improving the check-cfg implementation in https://github.com/rust-lang/cargo/pull/10566 I changed the internal representation of `check_cfg` from multiple `bool` options to one `Option<(bool, bool, bool, bool)>` but I didn't realize until https://github.com/rust-lang/rust/issues/82450#issuecomment-1169836928 that the internal representation is actually somewhat public as it's used in the `[unstable]` in `.cargo/config.toml`.

And because TOML cannot represent tuples there is no way to set it from the `[unstable]` section. This PR fix this oversight by using a custom deserializer method similar to what was already done for `build-std`.
This commit is contained in:
bors 2022-06-29 22:13:09 +00:00
commit 42311063f0
2 changed files with 116 additions and 29 deletions

View file

@ -641,6 +641,7 @@ unstable_cli_options!(
build_std_features: Option<Vec<String>> = ("Configure features enabled for the standard library itself when building the standard library"),
config_include: bool = ("Enable the `include` key in config files"),
credential_process: bool = ("Add a config setting to fetch registry authentication tokens by calling an external process"),
#[serde(deserialize_with = "deserialize_check_cfg")]
check_cfg: Option<(/*features:*/ bool, /*well_known_names:*/ bool, /*well_known_values:*/ bool, /*output:*/ bool)> = ("Specify scope of compile-time checking of `cfg` names/values"),
doctest_in_workspace: bool = ("Compile doctests with paths relative to the workspace root"),
doctest_xcompile: bool = ("Compile and run doctests for non-host target using runner config"),
@ -733,6 +734,47 @@ where
))
}
fn deserialize_check_cfg<'de, D>(
deserializer: D,
) -> Result<Option<(bool, bool, bool, bool)>, D::Error>
where
D: serde::Deserializer<'de>,
{
use serde::de::Error;
let crates = match <Option<Vec<String>>>::deserialize(deserializer)? {
Some(list) => list,
None => return Ok(None),
};
parse_check_cfg(crates.into_iter()).map_err(D::Error::custom)
}
fn parse_check_cfg(
it: impl Iterator<Item = impl AsRef<str>>,
) -> CargoResult<Option<(bool, bool, bool, bool)>> {
let mut features = false;
let mut well_known_names = false;
let mut well_known_values = false;
let mut output = false;
for e in it {
match e.as_ref() {
"features" => features = true,
"names" => well_known_names = true,
"values" => well_known_values = true,
"output" => output = true,
_ => bail!("unstable check-cfg only takes `features`, `names`, `values` or `output` as valid inputs"),
}
}
Ok(Some((
features,
well_known_names,
well_known_values,
output,
)))
}
impl CliUnstable {
pub fn parse(
&mut self,
@ -783,34 +825,6 @@ impl CliUnstable {
}
}
fn parse_check_cfg(value: Option<&str>) -> CargoResult<Option<(bool, bool, bool, bool)>> {
if let Some(value) = value {
let mut features = false;
let mut well_known_names = false;
let mut well_known_values = false;
let mut output = false;
for e in value.split(',') {
match e {
"features" => features = true,
"names" => well_known_names = true,
"values" => well_known_values = true,
"output" => output = true,
_ => bail!("flag -Zcheck-cfg only takes `features`, `names`, `values` or `output` as valid inputs"),
}
}
Ok(Some((
features,
well_known_names,
well_known_values,
output,
)))
} else {
Ok(None)
}
}
// Asserts that there is no argument to the flag.
fn parse_empty(key: &str, value: Option<&str>) -> CargoResult<bool> {
if let Some(v) = value {
@ -868,7 +882,9 @@ impl CliUnstable {
"minimal-versions" => self.minimal_versions = parse_empty(k, v)?,
"advanced-env" => self.advanced_env = parse_empty(k, v)?,
"config-include" => self.config_include = parse_empty(k, v)?,
"check-cfg" => self.check_cfg = parse_check_cfg(v)?,
"check-cfg" => {
self.check_cfg = v.map_or(Ok(None), |v| parse_check_cfg(v.split(',')))?
}
"dual-proc-macros" => self.dual_proc_macros = parse_empty(k, v)?,
// can also be set in .cargo/config or with and ENV
"mtime-on-use" => self.mtime_on_use = parse_empty(k, v)?,

View file

@ -629,3 +629,74 @@ fn build_script_test() {
.masquerade_as_nightly_cargo()
.run();
}
#[cargo_test]
fn config_valid() {
if !is_nightly() {
// --check-cfg is a nightly only rustc command line
return;
}
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"
[features]
f_a = []
f_b = []
"#,
)
.file("src/main.rs", "fn main() {}")
.file(
".cargo/config.toml",
r#"
[unstable]
check-cfg = ["features", "names", "values"]
"#,
)
.build();
p.cargo("build -v -Zcheck-cfg=features,names,values")
.masquerade_as_nightly_cargo()
.with_stderr_contains(x!("rustc" => "names"))
.with_stderr_contains(x!("rustc" => "values"))
.with_stderr_contains(x!("rustc" => "values" of "feature" with "f_a" "f_b"))
.run();
}
#[cargo_test]
fn config_invalid() {
if !is_nightly() {
// --check-cfg is a nightly only rustc command line
return;
}
let p = project()
.file(
"Cargo.toml",
r#"
[project]
name = "foo"
version = "0.1.0"
"#,
)
.file("src/main.rs", "fn main() {}")
.file(
".cargo/config.toml",
r#"
[unstable]
check-cfg = ["va"]
"#,
)
.build();
p.cargo("build")
.masquerade_as_nightly_cargo()
.with_stderr_contains("error: unstable check-cfg only takes `features`, `names`, `values` or `output` as valid inputs")
.with_status(101)
.run();
}