diff --git a/src/cargo/util/context/key.rs b/src/cargo/util/context/key.rs index 66d12c83c..048b595f2 100644 --- a/src/cargo/util/context/key.rs +++ b/src/cargo/util/context/key.rs @@ -112,7 +112,7 @@ impl fmt::Display for ConfigKey { } } -fn escape_key_part<'a>(part: &'a str) -> Cow<'a, str> { +pub(super) fn escape_key_part<'a>(part: &'a str) -> Cow<'a, str> { let ok = part.chars().all(|c| { matches!(c, 'a'..='z' | 'A'..='Z' | '0'..='9' | '-' | '_') diff --git a/src/cargo/util/context/mod.rs b/src/cargo/util/context/mod.rs index 89e7da808..006201898 100644 --- a/src/cargo/util/context/mod.rs +++ b/src/cargo/util/context/mod.rs @@ -2155,6 +2155,12 @@ impl From for ConfigError { } } +#[derive(Debug)] +enum KeyOrIdx { + Key(String), + Idx(usize), +} + #[derive(Eq, PartialEq, Clone)] pub enum ConfigValue { Integer(i64, Definition), @@ -2197,26 +2203,56 @@ impl ConfigValue { } fn from_toml(def: Definition, toml: toml::Value) -> CargoResult { + let mut error_path = Vec::new(); + Self::from_toml_inner(def, toml, &mut error_path).with_context(|| { + let mut it = error_path.iter().rev().peekable(); + let mut key_path = String::with_capacity(error_path.len() * 3); + while let Some(k) = it.next() { + match k { + KeyOrIdx::Key(s) => key_path.push_str(&key::escape_key_part(&s)), + KeyOrIdx::Idx(i) => key_path.push_str(&format!("[{i}]")), + } + if matches!(it.peek(), Some(KeyOrIdx::Key(_))) { + key_path.push('.'); + } + } + format!("failed to parse config at `{key_path}`") + }) + } + + fn from_toml_inner( + def: Definition, + toml: toml::Value, + path: &mut Vec, + ) -> CargoResult { match toml { toml::Value::String(val) => Ok(CV::String(val, def)), toml::Value::Boolean(b) => Ok(CV::Boolean(b, def)), toml::Value::Integer(i) => Ok(CV::Integer(i, def)), toml::Value::Array(val) => Ok(CV::List( val.into_iter() - .map(|toml| match toml { + .enumerate() + .map(|(i, toml)| match toml { toml::Value::String(val) => Ok((val, def.clone())), - v => bail!("expected string but found {} in list", v.type_str()), + v => { + path.push(KeyOrIdx::Idx(i)); + bail!("expected string but found {} at index {i}", v.type_str()) + } }) .collect::>()?, def, )), toml::Value::Table(val) => Ok(CV::Table( val.into_iter() - .map(|(key, value)| { - let value = CV::from_toml(def.clone(), value) - .with_context(|| format!("failed to parse key `{}`", key))?; - Ok((key, value)) - }) + .map( + |(key, value)| match CV::from_toml_inner(def.clone(), value, path) { + Ok(value) => Ok((key, value)), + Err(e) => { + path.push(KeyOrIdx::Key(key)); + Err(e) + } + }, + ) .collect::>()?, def, )), diff --git a/tests/testsuite/bad_config.rs b/tests/testsuite/bad_config.rs index 6ecf107cd..d8eec977a 100644 --- a/tests/testsuite/bad_config.rs +++ b/tests/testsuite/bad_config.rs @@ -47,10 +47,7 @@ Caused by: failed to load TOML configuration from `[ROOT]/foo/.cargo/config.toml` Caused by: - failed to parse key `http` - -Caused by: - failed to parse key `proxy` + failed to parse config at `http.proxy` Caused by: found TOML configuration value of unknown type `float` diff --git a/tests/testsuite/cargo_alias_config.rs b/tests/testsuite/cargo_alias_config.rs index 5fa73f6ad..044a5a28a 100644 --- a/tests/testsuite/cargo_alias_config.rs +++ b/tests/testsuite/cargo_alias_config.rs @@ -87,13 +87,10 @@ Caused by: failed to load TOML configuration from `[ROOT]/foo/.cargo/config.toml` Caused by: - failed to parse key `alias` + failed to parse config at `alias.b-cargo-test[0]` Caused by: - failed to parse key `b-cargo-test` - -Caused by: - expected string but found integer in list + expected string but found integer at index 0 "#]]) .run(); diff --git a/tests/testsuite/config.rs b/tests/testsuite/config.rs index 81506bb5e..ff8bad643 100644 --- a/tests/testsuite/config.rs +++ b/tests/testsuite/config.rs @@ -1363,10 +1363,10 @@ Caused by: failed to load TOML configuration from `[ROOT]/.cargo/config.toml` Caused by: - failed to parse key `foo` + failed to parse config at `foo[0]` Caused by: - expected string but found integer in list + expected string but found integer at index 0 "#]], ); } diff --git a/tests/testsuite/config_cli.rs b/tests/testsuite/config_cli.rs index e7e36f63b..e95d8aed5 100644 --- a/tests/testsuite/config_cli.rs +++ b/tests/testsuite/config_cli.rs @@ -527,7 +527,7 @@ fn bad_cv_convert() { failed to convert --config argument `a=2019-12-01` Caused by: - failed to parse key `a` + failed to parse config at `a` Caused by: found TOML configuration value of unknown type `datetime`