Skip to content

Commit c689f55

Browse files
committed
Auto merge of #10193 - sstangl:help-alias-10138, r=alexcrichton
Display alias target on 'cargo help <alias>` ``` Previously, `cargo help <alias>` resolved the alias and displayed the help for the targeted subcommand. For example, if `br` were aliased to `build --release`, `cargo help br` would display the manpage for cargo-build. With this patch, it will print "'br' is aliased to 'build --release'". ``` Addresses issue #10138. This is my first patch to Cargo. I attempted to follow the style of the surrounding code. Please let me know if any changes are required: happy to make them. In particular, I wasn't sure if any tests exist for this path.
2 parents c87b986 + 4c66d18 commit c689f55

File tree

3 files changed

+71
-23
lines changed

3 files changed

+71
-23
lines changed

crates/cargo-test-support/src/lib.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -803,6 +803,15 @@ impl Execs {
803803
}
804804
}
805805

806+
#[track_caller]
807+
pub fn run_expect_error(&mut self) {
808+
self.ran = true;
809+
let p = (&self.process_builder).clone().unwrap();
810+
if self.match_process(&p).is_ok() {
811+
panic!("test was expected to fail, but succeeded running {}", p);
812+
}
813+
}
814+
806815
/// Runs the process, checks the expected output, and returns the first
807816
/// JSON object on stdout.
808817
#[track_caller]

src/bin/cargo/commands/help.rs

Lines changed: 32 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::aliased_command;
22
use cargo::util::errors::CargoResult;
3-
use cargo::Config;
3+
use cargo::{drop_println, Config};
44
use cargo_util::paths::resolve_executable;
55
use flate2::read::GzDecoder;
66
use std::ffi::OsString;
@@ -15,14 +15,14 @@ const COMPRESSED_MAN: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/man.tgz"
1515
/// This runs before clap processing, because it needs to intercept the `help`
1616
/// command if a man page is available.
1717
///
18-
/// Returns `true` if a man page was displayed. In this case, Cargo should
19-
/// exit.
18+
/// Returns `true` if help information was successfully displayed to the user.
19+
/// In this case, Cargo should exit.
2020
pub fn handle_embedded_help(config: &Config) -> bool {
2121
match try_help(config) {
2222
Ok(true) => true,
2323
Ok(false) => false,
2424
Err(e) => {
25-
log::warn!("man failed: {:?}", e);
25+
log::warn!("help failed: {:?}", e);
2626
false
2727
}
2828
}
@@ -46,11 +46,28 @@ fn try_help(config: &Config) -> CargoResult<bool> {
4646
Some(s) => s,
4747
None => return Ok(false),
4848
};
49-
// Check if this is a built-in command (or alias);
49+
5050
let subcommand = match check_alias(config, subcommand) {
51+
// If this alias is more than a simple subcommand pass-through, show the alias.
52+
Some(argv) if argv.len() > 1 => {
53+
let alias = argv.join(" ");
54+
drop_println!(config, "`{}` is aliased to `{}`", subcommand, alias);
55+
return Ok(true);
56+
}
57+
// Otherwise, resolve the alias into its subcommand.
58+
Some(argv) => {
59+
// An alias with an empty argv can be created via `"empty-alias" = ""`.
60+
let first = argv.get(0).map(String::as_str).unwrap_or(subcommand);
61+
first.to_string()
62+
}
63+
None => subcommand.to_string(),
64+
};
65+
66+
let subcommand = match check_builtin(&subcommand) {
5167
Some(s) => s,
5268
None => return Ok(false),
5369
};
70+
5471
if resolve_executable(Path::new("man")).is_ok() {
5572
let man = match extract_man(&subcommand, "1") {
5673
Some(man) => man,
@@ -73,24 +90,18 @@ fn try_help(config: &Config) -> CargoResult<bool> {
7390
Ok(true)
7491
}
7592

76-
/// Checks if the given subcommand is a built-in command (possibly via an alias).
93+
/// Checks if the given subcommand is an alias.
94+
///
95+
/// Returns None if it is not an alias.
96+
fn check_alias(config: &Config, subcommand: &str) -> Option<Vec<String>> {
97+
aliased_command(config, subcommand).ok().flatten()
98+
}
99+
100+
/// Checks if the given subcommand is a built-in command (not via an alias).
77101
///
78102
/// Returns None if it is not a built-in command.
79-
fn check_alias(config: &Config, subcommand: &str) -> Option<String> {
80-
if super::builtin_exec(subcommand).is_some() {
81-
return Some(subcommand.to_string());
82-
}
83-
match aliased_command(config, subcommand) {
84-
Ok(Some(alias)) => {
85-
let alias = alias.into_iter().next()?;
86-
if super::builtin_exec(&alias).is_some() {
87-
Some(alias)
88-
} else {
89-
None
90-
}
91-
}
92-
_ => None,
93-
}
103+
fn check_builtin(subcommand: &str) -> Option<&str> {
104+
super::builtin_exec(subcommand).map(|_| subcommand)
94105
}
95106

96107
/// Extracts the given man page from the compressed archive.

tests/testsuite/help.rs

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,20 @@ fn help_with_man_and_path(
110110
assert_eq!(stdout, contents);
111111
}
112112

113+
fn help_with_stdout_and_path(subcommand: &str, path: &Path) -> String {
114+
let output = process(&cargo_exe())
115+
.arg("help")
116+
.arg(subcommand)
117+
.env("PATH", path)
118+
.exec_with_output()
119+
.unwrap();
120+
assert!(output.status.success());
121+
let stderr = from_utf8(&output.stderr).unwrap();
122+
assert_eq!(stderr, "");
123+
let stdout = from_utf8(&output.stdout).unwrap();
124+
stdout.to_string()
125+
}
126+
113127
#[cargo_test]
114128
fn help_man() {
115129
// Checks that `help command` displays the man page using the given command.
@@ -132,11 +146,25 @@ fn help_alias() {
132146
config,
133147
r#"
134148
[alias]
135-
my-alias = ["build", "--release"]
149+
empty-alias = ""
150+
simple-alias = "build"
151+
complex-alias = ["build", "--release"]
136152
"#,
137153
)
138154
.unwrap();
139-
help_with_man_and_path("", "my-alias", "build", Path::new(""));
155+
156+
// The `empty-alias` returns an error.
157+
cargo_process("help empty-alias")
158+
.env("PATH", Path::new(""))
159+
.with_stderr_contains("[..]The subcommand 'empty-alias' wasn't recognized[..]")
160+
.run_expect_error();
161+
162+
// Because `simple-alias` aliases a subcommand with no arguments, help shows the manpage.
163+
help_with_man_and_path("", "simple-alias", "build", Path::new(""));
164+
165+
// Help for `complex-alias` displays the full alias command.
166+
let out = help_with_stdout_and_path("complex-alias", Path::new(""));
167+
assert_eq!(out, "`complex-alias` is aliased to `build --release`\n");
140168
}
141169

142170
#[cargo_test]

0 commit comments

Comments
 (0)