Skip to content

Commit 247ca7f

Browse files
committed
Auto merge of #11087 - weihanglo:issue-11013, r=epage
Report cmd aliasing failure with more contexts ### What does this PR try to resolve? Commands aliasing resolution should report TOML parsing error to users. Fixes #11013. ### How should we test and review this PR? ef7a4ef is the most important commit in this PR. `has_key` now throws errors after this PR, so any use `Config::get<Option<…>>()` is affected if there is a malformed config. Had a skim over all usages of `get::<Option<…>>`, `get_string` and `get_path`, I don't feel any of them should ignore errors.
2 parents 8882d99 + 5caabc0 commit 247ca7f

File tree

7 files changed

+113
-29
lines changed

7 files changed

+113
-29
lines changed

src/bin/cargo/cli.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,6 @@ pub fn main(config: &mut LazyConfig) -> CliResult {
3232
// the [alias] table).
3333
let config = config.get_mut();
3434

35-
// Global args need to be extracted before expanding aliases because the
36-
// clap code for extracting a subcommand discards global options
37-
// (appearing before the subcommand).
3835
let (expanded_args, global_args) = expand_aliases(config, args, vec![])?;
3936

4037
if expanded_args
@@ -224,26 +221,34 @@ fn add_ssl(version_string: &mut String) {
224221
}
225222
}
226223

224+
/// Expands aliases recursively to collect all the command line arguments.
225+
///
226+
/// [`GlobalArgs`] need to be extracted before expanding aliases because the
227+
/// clap code for extracting a subcommand discards global options
228+
/// (appearing before the subcommand).
227229
fn expand_aliases(
228230
config: &mut Config,
229231
args: ArgMatches,
230232
mut already_expanded: Vec<String>,
231233
) -> Result<(ArgMatches, GlobalArgs), CliError> {
232234
if let Some((cmd, args)) = args.subcommand() {
233-
match (
234-
commands::builtin_exec(cmd),
235-
super::aliased_command(config, cmd)?,
236-
) {
237-
(Some(_), Some(_)) => {
235+
let exec = commands::builtin_exec(cmd);
236+
let aliased_cmd = super::aliased_command(config, cmd);
237+
238+
match (exec, aliased_cmd) {
239+
(Some(_), Ok(Some(_))) => {
238240
// User alias conflicts with a built-in subcommand
239241
config.shell().warn(format!(
240242
"user-defined alias `{}` is ignored, because it is shadowed by a built-in command",
241243
cmd,
242244
))?;
243245
}
244-
(Some(_), None) => {
245-
// Command is built-in and is not conflicting with alias, but contains ignored values.
246+
(Some(_), Ok(None) | Err(_)) => {
247+
// Here we ignore errors from aliasing as we already favor built-in command,
248+
// and alias doesn't involve in this context.
249+
246250
if let Some(values) = args.get_many::<OsString>("") {
251+
// Command is built-in and is not conflicting with alias, but contains ignored values.
247252
return Err(anyhow::format_err!(
248253
"\
249254
trailing arguments after built-in command `{}` are unsupported: `{}`
@@ -255,8 +260,8 @@ To pass the arguments to the subcommand, remove `--`",
255260
.into());
256261
}
257262
}
258-
(None, None) => {}
259-
(_, Some(alias)) => {
263+
(None, Ok(None)) => {}
264+
(None, Ok(Some(alias))) => {
260265
// Check if this alias is shadowing an external subcommand
261266
// (binary of the form `cargo-<subcommand>`)
262267
// Currently this is only a warning, but after a transition period this will become
@@ -300,6 +305,7 @@ For more information, see issue #10049 <https://github.com/rust-lang/cargo/issue
300305
let (expanded_args, _) = expand_aliases(config, new_args, already_expanded)?;
301306
return Ok((expanded_args, global_args));
302307
}
308+
(None, Err(e)) => return Err(e.into()),
303309
}
304310
};
305311

src/bin/cargo/main.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,14 @@ fn builtin_aliases_execs(cmd: &str) -> Option<&(&str, &str, &str)> {
5353
BUILTIN_ALIASES.iter().find(|alias| alias.0 == cmd)
5454
}
5555

56+
/// Resolve the aliased command from the [`Config`] with a given command string.
57+
///
58+
/// The search fallback chain is:
59+
///
60+
/// 1. Get the aliased command as a string.
61+
/// 2. If an `Err` occurs (missing key, type mismatch, or any possible error),
62+
/// try to get it as an array again.
63+
/// 3. If still cannot find any, finds one insides [`BUILTIN_ALIASES`].
5664
fn aliased_command(config: &Config, command: &str) -> CargoResult<Option<Vec<String>>> {
5765
let alias_name = format!("alias.{}", command);
5866
let user_alias = match config.get_string(&alias_name) {

src/cargo/util/config/de.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ impl<'de, 'config> de::Deserializer<'de> for Deserializer<'config> {
8080
where
8181
V: de::Visitor<'de>,
8282
{
83-
if self.config.has_key(&self.key, self.env_prefix_ok) {
83+
if self.config.has_key(&self.key, self.env_prefix_ok)? {
8484
visitor.visit_some(self)
8585
} else {
8686
// Treat missing values as `None`.

src/cargo/util/config/mod.rs

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -682,25 +682,25 @@ impl Config {
682682
}
683683
}
684684

685-
fn has_key(&self, key: &ConfigKey, env_prefix_ok: bool) -> bool {
685+
/// Check if the [`Config`] contains a given [`ConfigKey`].
686+
///
687+
/// See `ConfigMapAccess` for a description of `env_prefix_ok`.
688+
fn has_key(&self, key: &ConfigKey, env_prefix_ok: bool) -> CargoResult<bool> {
686689
if self.env.contains_key(key.as_env_key()) {
687-
return true;
690+
return Ok(true);
688691
}
689-
// See ConfigMapAccess for a description of this.
690692
if env_prefix_ok {
691693
let env_prefix = format!("{}_", key.as_env_key());
692694
if self.env.keys().any(|k| k.starts_with(&env_prefix)) {
693-
return true;
695+
return Ok(true);
694696
}
695697
}
696-
if let Ok(o_cv) = self.get_cv(key) {
697-
if o_cv.is_some() {
698-
return true;
699-
}
698+
if self.get_cv(key)?.is_some() {
699+
return Ok(true);
700700
}
701701
self.check_environment_key_case_mismatch(key);
702702

703-
false
703+
Ok(false)
704704
}
705705

706706
fn check_environment_key_case_mismatch(&self, key: &ConfigKey) {

tests/testsuite/bad_config.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ fn bad1() {
1919
.with_status(101)
2020
.with_stderr(
2121
"\
22-
[ERROR] invalid configuration for key `target.nonexistent-target`
23-
expected a table, but found a string for `[..]` in [..]config
22+
[ERROR] expected table for configuration key `target.nonexistent-target`, \
23+
but found string in [..]config
2424
",
2525
)
2626
.run();

tests/testsuite/cargo_alias_config.rs

Lines changed: 75 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,88 @@ fn alias_incorrect_config_type() {
2121

2222
p.cargo("b-cargo-test -v")
2323
.with_status(101)
24-
.with_stderr_contains(
24+
.with_stderr(
2525
"\
2626
[ERROR] invalid configuration for key `alias.b-cargo-test`
2727
expected a list, but found a integer for [..]",
2828
)
2929
.run();
3030
}
3131

32+
#[cargo_test]
33+
fn alias_malformed_config_string() {
34+
let p = project()
35+
.file("Cargo.toml", &basic_bin_manifest("foo"))
36+
.file("src/main.rs", "fn main() {}")
37+
.file(
38+
".cargo/config",
39+
r#"
40+
[alias]
41+
b-cargo-test = `
42+
"#,
43+
)
44+
.build();
45+
46+
p.cargo("b-cargo-test -v")
47+
.with_status(101)
48+
.with_stderr(
49+
"\
50+
[ERROR] could not load Cargo configuration
51+
52+
Caused by:
53+
could not parse TOML configuration in `[..]/config`
54+
55+
Caused by:
56+
[..]
57+
58+
Caused by:
59+
TOML parse error at line [..]
60+
|
61+
3 | b-cargo-test = `
62+
| ^
63+
Unexpected ```
64+
Expected quoted string
65+
",
66+
)
67+
.run();
68+
}
69+
70+
#[cargo_test]
71+
fn alias_malformed_config_list() {
72+
let p = project()
73+
.file("Cargo.toml", &basic_bin_manifest("foo"))
74+
.file("src/main.rs", "fn main() {}")
75+
.file(
76+
".cargo/config",
77+
r#"
78+
[alias]
79+
b-cargo-test = [1, 2]
80+
"#,
81+
)
82+
.build();
83+
84+
p.cargo("b-cargo-test -v")
85+
.with_status(101)
86+
.with_stderr(
87+
"\
88+
[ERROR] could not load Cargo configuration
89+
90+
Caused by:
91+
failed to load TOML configuration from `[..]/config`
92+
93+
Caused by:
94+
[..] `alias`
95+
96+
Caused by:
97+
[..] `b-cargo-test`
98+
99+
Caused by:
100+
expected string but found integer in list
101+
",
102+
)
103+
.run();
104+
}
105+
32106
#[cargo_test]
33107
fn alias_config() {
34108
let p = project()

tests/testsuite/config.rs

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1074,10 +1074,6 @@ Dotted key `ssl-version` attempted to extend non-table type (string)
10741074
10751075
",
10761076
);
1077-
assert!(config
1078-
.get::<Option<SslVersionConfig>>("http.ssl-version")
1079-
.unwrap()
1080-
.is_none());
10811077
}
10821078

10831079
#[cargo_test]

0 commit comments

Comments
 (0)