Upgrade to Clap 3

- One parser change found by `cargo_config::includes` is that clap 2
  would ignore any values after a `=` for flags.
  `cargo config --show-origin` is a flag but the test passed `--show-origin=yes` which
  happens to give the desired result for that test but is the same as
  `--show-origin=no` or `--show-origin=alien-invasion`.
- The parser now panics when accessing an undefined attribute but clap
  takes advantage of that for sharing code across commands that have
  different subsets of arguments defined.  I've extended clap so we can
  "look before you leap" and put the checks at the argument calls to
  start off with so its very clear what is tenuously shared.  This
  allows us to go in either direction in the future, either addressing
  how we are sharing between commands or by moving this down into the
  extension methods and pretending this clap feature doesn't exist
- On that topic, a test found clap-rs/clap#3263.  For now, there is a
  hack in clap.  Depending on how we fix that in clap for clap 4.0, we
  might need to re-address things in cargo.
- `value_of_os` now requires setting `allow_invalid_utf8`, otherwise it
  asserts.  To help catch this, I updated the argument definitions
  associated with lookups reported by:
  - `rg 'values?_os' src/`
  - `rg 'values?_of_os' src/`
- clap now reports `2` for usage errors, so we had to bypass clap's
  `exit` call to keep the same exit code.

BREAKING CHANGE: API now uses clap3
This commit is contained in:
Ed Page 2022-01-04 11:04:50 -06:00
parent 2478331215
commit f17ecafc24
47 changed files with 200 additions and 148 deletions

View File

@ -61,7 +61,7 @@ toml = "0.5.7"
unicode-xid = "0.2.0"
url = "2.2.2"
walkdir = "2.2"
clap = "2.34.0"
clap = "3.0.5"
unicode-width = "0.1.5"
openssl = { version = '0.10.11', optional = true }
im-rc = "15.0.0"

View File

@ -35,8 +35,8 @@ pub fn main(config: &mut Config) -> CliResult {
Err(e) => {
if e.kind == clap::ErrorKind::UnrecognizedSubcommand {
// An unrecognized subcommand might be an external subcommand.
let cmd = &e.info.as_ref().unwrap()[0].to_owned();
return super::execute_external_subcommand(config, cmd, &[cmd, "--help"])
let cmd = e.info[0].clone();
return super::execute_external_subcommand(config, &cmd, &[&cmd, "--help"])
.map_err(|_| e.into());
} else {
return Err(e.into());
@ -152,7 +152,7 @@ Run with 'cargo -Z [FLAG] [SUBCOMMAND]'",
}
let (cmd, subcommand_args) = match expanded_args.subcommand() {
(cmd, Some(args)) => (cmd, args),
Some((cmd, args)) => (cmd, args),
_ => {
// No subcommand provided.
cli().print_help()?;
@ -236,10 +236,10 @@ fn add_ssl(version_string: &mut String) {
fn expand_aliases(
config: &mut Config,
args: ArgMatches<'static>,
args: ArgMatches,
mut already_expanded: Vec<String>,
) -> Result<(ArgMatches<'static>, GlobalArgs), CliError> {
if let (cmd, Some(args)) = args.subcommand() {
) -> Result<(ArgMatches, GlobalArgs), CliError> {
if let Some((cmd, args)) = args.subcommand() {
match (
commands::builtin_exec(cmd),
super::aliased_command(config, cmd)?,
@ -292,7 +292,7 @@ For more information, see issue #10049 <https://github.com/rust-lang/cargo/issue
.setting(AppSettings::NoBinaryName)
.get_matches_from_safe(alias)?;
let (new_cmd, _) = new_args.subcommand();
let new_cmd = new_args.subcommand_name().expect("subcommand is required");
already_expanded.push(cmd.to_string());
if already_expanded.contains(&new_cmd.to_string()) {
// Crash if the aliases are corecursive / unresolvable
@ -316,16 +316,20 @@ For more information, see issue #10049 <https://github.com/rust-lang/cargo/issue
fn config_configure(
config: &mut Config,
args: &ArgMatches<'_>,
subcommand_args: &ArgMatches<'_>,
args: &ArgMatches,
subcommand_args: &ArgMatches,
global_args: GlobalArgs,
) -> CliResult {
let arg_target_dir = &subcommand_args.value_of_path("target-dir", config);
let arg_target_dir = &subcommand_args
._is_valid_arg("target-dir")
.then(|| subcommand_args.value_of_path("target-dir", config))
.flatten();
let verbose = global_args.verbose + args.occurrences_of("verbose") as u32;
// quiet is unusual because it is redefined in some subcommands in order
// to provide custom help text.
let quiet =
args.is_present("quiet") || subcommand_args.is_present("quiet") || global_args.quiet;
let quiet = args.is_present("quiet")
|| subcommand_args.is_valid_and_present("quiet")
|| global_args.quiet;
let global_color = global_args.color; // Extract so it can take reference.
let color = args.value_of("color").or_else(|| global_color.as_deref());
let frozen = args.is_present("frozen") || global_args.frozen;
@ -353,11 +357,7 @@ fn config_configure(
Ok(())
}
fn execute_subcommand(
config: &mut Config,
cmd: &str,
subcommand_args: &ArgMatches<'_>,
) -> CliResult {
fn execute_subcommand(config: &mut Config, cmd: &str, subcommand_args: &ArgMatches) -> CliResult {
if let Some(exec) = commands::builtin_exec(cmd) {
return exec(config, subcommand_args);
}
@ -380,7 +380,7 @@ struct GlobalArgs {
}
impl GlobalArgs {
fn new(args: &ArgMatches<'_>) -> GlobalArgs {
fn new(args: &ArgMatches) -> GlobalArgs {
GlobalArgs {
verbose: args.occurrences_of("verbose") as u32,
quiet: args.is_present("quiet"),
@ -411,9 +411,12 @@ fn cli() -> App {
.settings(&[
AppSettings::UnifiedHelpMessage,
AppSettings::DeriveDisplayOrder,
AppSettings::VersionlessSubcommands,
AppSettings::AllowExternalSubcommands,
AppSettings::NoAutoVersion,
])
// Doesn't mix well with our list of common cargo commands. See clap-rs/clap#3108 for
// opening clap up to allow us to style our help template
.global_setting(AppSettings::DisableColoredHelp)
.usage(usage)
.template(
"\
@ -423,7 +426,7 @@ USAGE:
{usage}
OPTIONS:
{unified}
{options}
Some common cargo commands are (see all commands with --list):
build, b Compile the current package
@ -443,7 +446,7 @@ Some common cargo commands are (see all commands with --list):
See 'cargo help <command>' for more information on a specific command.\n",
)
.arg(opt("version", "Print version info and exit").short("V"))
.arg(opt("version", "Print version info and exit").short('V'))
.arg(opt("list", "List installed commands"))
.arg(opt("explain", "Run `rustc --explain CODE`").value_name("CODE"))
.arg(
@ -451,8 +454,8 @@ See 'cargo help <command>' for more information on a specific command.\n",
"verbose",
"Use verbose output (-vv very verbose/build.rs output)",
)
.short("v")
.multiple(true)
.short('v')
.multiple_occurrences(true)
.global(true),
)
.arg_quiet()
@ -475,7 +478,7 @@ See 'cargo help <command>' for more information on a specific command.\n",
.arg(
Arg::with_name("unstable-features")
.help("Unstable (nightly-only) flags to Cargo, see 'cargo -Z help' for details")
.short("Z")
.short('Z')
.value_name("FLAG")
.multiple(true)
.number_of_values(1)
@ -483,3 +486,8 @@ See 'cargo help <command>' for more information on a specific command.\n",
)
.subcommands(commands::builtin())
}
#[test]
fn verify_cli() {
cli().debug_assert();
}

View File

@ -50,7 +50,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help bench` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let mut compile_opts = args.compile_options(
config,

View File

@ -47,7 +47,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help build` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let mut compile_opts = args.compile_options(
config,

View File

@ -39,7 +39,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help check` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
// This is a legacy behavior that causes `cargo check` to pass `--test`.
let test = matches!(args.value_of("profile"), Some("test"));

View File

@ -17,7 +17,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help clean` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
if args.is_present_with_zero_values("package") {

View File

@ -26,12 +26,12 @@ pub fn cli() -> App {
)
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
config
.cli_unstable()
.fail_if_stable_command(config, "config", 9301)?;
match args.subcommand() {
("get", Some(args)) => {
Some(("get", args)) => {
let opts = cargo_config::GetOptions {
key: args.value_of("key"),
format: args.value_of("format").unwrap().parse()?,
@ -40,8 +40,11 @@ pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
};
cargo_config::get(config, &opts)?;
}
(cmd, _) => {
panic!("unexpected command `{}`", cmd)
Some((cmd, _)) => {
unreachable!("unexpected command {}", cmd)
}
None => {
unreachable!("unexpected command")
}
}
Ok(())

View File

@ -39,7 +39,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help doc` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let mode = CompileMode::Doc {
deps: !args.is_present("no-deps"),

View File

@ -12,7 +12,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help fetch` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let opts = FetchOptions {

View File

@ -65,7 +65,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help fix` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
// This is a legacy behavior that causes `cargo fix` to pass `--test`.
let test = matches!(args.value_of("profile"), Some("test"));

View File

@ -10,7 +10,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help generate-lockfile` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
ops::generate_lockfile(&ws)?;
Ok(())

View File

@ -9,6 +9,6 @@ pub fn cli() -> App {
.help(REMOVED)
}
pub fn exec(_config: &mut Config, _args: &ArgMatches<'_>) -> CliResult {
pub fn exec(_config: &mut Config, _args: &ArgMatches) -> CliResult {
Err(anyhow::format_err!(REMOVED).into())
}

View File

@ -12,7 +12,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help init` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let opts = args.new_options(config)?;
let project_kind = ops::init(&opts, config)?;
config

View File

@ -45,7 +45,7 @@ pub fn cli() -> App {
"list all installed packages and their versions",
))
.arg_jobs()
.arg(opt("force", "Force overwriting existing crates or binaries").short("f"))
.arg(opt("force", "Force overwriting existing crates or binaries").short('f'))
.arg(opt("no-track", "Do not save tracking information"))
.arg_features()
.arg_profile("Install artifacts with the specified profile")
@ -75,7 +75,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help install` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
if let Some(path) = args.value_of_path("path", config) {
config.reload_rooted_at(path)?;
} else {

View File

@ -24,7 +24,7 @@ pub struct ProjectLocation<'a> {
root: &'a str,
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let root_manifest;
let workspace;
let root = match WhatToFind::parse(args) {
@ -64,7 +64,7 @@ enum WhatToFind {
}
impl WhatToFind {
fn parse(args: &ArgMatches<'_>) -> Self {
fn parse(args: &ArgMatches) -> Self {
if args.is_present("workspace") {
WhatToFind::Workspace
} else {
@ -79,7 +79,7 @@ enum MessageFormat {
}
impl MessageFormat {
fn parse(args: &ArgMatches<'_>) -> CargoResult<Self> {
fn parse(args: &ArgMatches) -> CargoResult<Self> {
let fmt = match args.value_of("message-format") {
Some(fmt) => fmt,
None => return Ok(MessageFormat::Json),

View File

@ -14,7 +14,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help login` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
ops::registry_login(
config,
args.value_of("token").map(String::from),

View File

@ -9,7 +9,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help logout` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
if !config.cli_unstable().credential_process {
config
.cli_unstable()

View File

@ -29,7 +29,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help metadata` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let version = match args.value_of("format-version") {

View File

@ -40,7 +40,7 @@ pub fn builtin() -> Vec<App> {
]
}
pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches<'_>) -> CliResult> {
pub fn builtin_exec(cmd: &str) -> Option<fn(&mut Config, &ArgMatches) -> CliResult> {
let f = match cmd {
"bench" => bench::exec,
"build" => build::exec,

View File

@ -12,7 +12,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help new` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let opts = args.new_options(config)?;
ops::new(&opts, config)?;

View File

@ -13,7 +13,7 @@ pub fn cli() -> App {
"LOGIN",
"Name of a user or team to invite as an owner",
)
.short("a"),
.short('a'),
)
.arg(
multi_opt(
@ -21,16 +21,16 @@ pub fn cli() -> App {
"LOGIN",
"Name of a user or team to remove as an owner",
)
.short("r"),
.short('r'),
)
.arg(opt("list", "List owners of a crate").short("l"))
.arg(opt("list", "List owners of a crate").short('l'))
.arg(opt("index", "Registry index to modify owners for").value_name("INDEX"))
.arg(opt("token", "API token to use when authenticating").value_name("TOKEN"))
.arg(opt("registry", "Registry to use").value_name("REGISTRY"))
.after_help("Run `cargo help owner` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
config.load_credentials()?;
let registry = args.registry(config)?;

View File

@ -11,7 +11,7 @@ pub fn cli() -> App {
"list",
"Print files included in a package without making one",
)
.short("l"),
.short('l'),
)
.arg(opt(
"no-verify",
@ -38,7 +38,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help package` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let specs = args.packages_from_flags()?;

View File

@ -13,7 +13,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help pkgid` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
if args.is_present_with_zero_values("package") {
print_available_packages(&ws)?

View File

@ -27,7 +27,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help publish` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
config.load_credentials()?;
let registry = args.registry(config)?;

View File

@ -13,7 +13,7 @@ Deprecated, use `cargo metadata --no-deps` instead.\
.arg_manifest_path()
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
config
.shell()

View File

@ -22,14 +22,19 @@ pub fn cli() -> App {
)
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
match args.subcommand() {
("future-incompatibilities", Some(args)) => report_future_incompatibilies(config, args),
(cmd, _) => panic!("unexpected command `{}`", cmd),
Some(("future-incompatibilities", args)) => report_future_incompatibilies(config, args),
Some((cmd, _)) => {
unreachable!("unexpected command {}", cmd)
}
None => {
unreachable!("unexpected command")
}
}
}
fn report_future_incompatibilies(config: &Config, args: &ArgMatches<'_>) -> CliResult {
fn report_future_incompatibilies(config: &Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let reports = OnDiskReports::load(&ws)?;
let id = args

View File

@ -11,7 +11,11 @@ pub fn cli() -> App {
.setting(AppSettings::TrailingVarArg)
.about("Run a binary or example of the local package")
.arg_quiet()
.arg(Arg::with_name("args").multiple(true))
.arg(
Arg::with_name("args")
.allow_invalid_utf8(true)
.multiple(true),
)
.arg_targets_bin_example(
"Name of the bin target to run",
"Name of the example target to run",
@ -30,7 +34,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help run` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let mut compile_opts = args.compile_options(

View File

@ -50,7 +50,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help rustc` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
// This is a legacy behavior that changes the behavior based on the profile.
// If we want to support this more formally, I think adding a --mode flag

View File

@ -38,7 +38,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help rustdoc` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let mut compile_opts = args.compile_options_for_single_package(
config,

View File

@ -21,7 +21,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help search` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let registry = args.registry(config)?;
let index = args.index()?;
let limit = args.value_of_u32("limit")?;

View File

@ -23,7 +23,7 @@ pub fn cli() -> App {
"quiet",
"Display one character per test instead of one line",
)
.short("q"),
.short('q'),
)
.arg_targets_all(
"Test only this package's library unit tests",
@ -62,7 +62,7 @@ pub fn cli() -> App {
)
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
let mut compile_opts = args.compile_options(

View File

@ -20,7 +20,7 @@ pub fn cli() -> App {
"Exclude specific workspace members",
)
// Deprecated, use --no-dedupe instead.
.arg(Arg::with_name("all").long("all").short("a").hidden(true))
.arg(Arg::with_name("all").long("all").short('a').hidden(true))
// Deprecated, use --target=all instead.
.arg(
Arg::with_name("all-targets")
@ -46,7 +46,7 @@ pub fn cli() -> App {
(features, normal, build, dev, all, \
no-normal, no-build, no-dev, no-proc-macro)",
)
.short("e"),
.short('e'),
)
.arg(
optional_multi_opt(
@ -54,7 +54,7 @@ pub fn cli() -> App {
"SPEC",
"Invert the tree direction and focus on the given package",
)
.short("i"),
.short('i'),
)
.arg(multi_opt(
"prune",
@ -88,7 +88,7 @@ pub fn cli() -> App {
"duplicates",
"Show only dependencies which come in multiple versions (implies -i)",
)
.short("d")
.short('d')
.alias("duplicate"),
)
.arg(
@ -100,20 +100,20 @@ pub fn cli() -> App {
.arg(
opt("format", "Format string used for printing dependencies")
.value_name("FORMAT")
.short("f")
.short('f')
.default_value("{p}"),
)
.arg(
// Backwards compatibility with old cargo-tree.
Arg::with_name("version")
.long("version")
.short("V")
.short('V')
.hidden(true),
)
.after_help("Run `cargo help tree` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
if args.is_present("version") {
let verbose = args.occurrences_of("verbose") > 0;
let version = cli::get_version_string(verbose);
@ -226,10 +226,7 @@ subtree of the package given to -p.\n\
/// Parses `--edges` option.
///
/// Returns a tuple of `EdgeKind` map and `no_proc_marco` flag.
fn parse_edge_kinds(
config: &Config,
args: &ArgMatches<'_>,
) -> CargoResult<(HashSet<EdgeKind>, bool)> {
fn parse_edge_kinds(config: &Config, args: &ArgMatches) -> CargoResult<(HashSet<EdgeKind>, bool)> {
let (kinds, no_proc_macro) = {
let mut no_proc_macro = false;
let mut kinds = args.values_of("edges").map_or_else(

View File

@ -13,7 +13,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help uninstall` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let root = args.value_of("root");
if args.is_present_with_zero_values("package") {

View File

@ -7,7 +7,7 @@ pub fn cli() -> App {
subcommand("update")
.about("Update dependencies as recorded in the local lock file")
.arg_quiet()
.arg(opt("workspace", "Only update the workspace packages").short("w"))
.arg(opt("workspace", "Only update the workspace packages").short('w'))
.arg_package_spec_simple("Package to update")
.arg(opt(
"aggressive",
@ -25,7 +25,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help update` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let ws = args.workspace(config)?;
if args.is_present_with_zero_values("package") {

View File

@ -7,7 +7,11 @@ pub fn cli() -> App {
.about("Vendor all dependencies for a project locally")
.arg_quiet()
.arg_manifest_path()
.arg(Arg::with_name("path").help("Where to vendor crates (`vendor` by default)"))
.arg(
Arg::with_name("path")
.allow_invalid_utf8(true)
.help("Where to vendor crates (`vendor` by default)"),
)
.arg(
Arg::with_name("no-delete")
.long("no-delete")
@ -15,17 +19,18 @@ pub fn cli() -> App {
)
.arg(
Arg::with_name("tomls")
.short("s")
.short('s')
.long("sync")
.help("Additional `Cargo.toml` to sync and vendor")
.value_name("TOML")
.allow_invalid_utf8(true)
.multiple(true),
)
.arg(
Arg::with_name("respect-source-config")
.long("respect-source-config")
.help("Respect `[source]` config in `.cargo/config`")
.multiple(true),
.multiple_occurrences(true),
)
.arg(
Arg::with_name("versioned-dirs")
@ -59,7 +64,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help vendor` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
// We're doing the vendoring operation ourselves, so we don't actually want
// to respect any of the `source` configuration in Cargo itself. That's
// intended for other consumers of Cargo, but we want to go straight to the

View File

@ -11,7 +11,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help verify-project` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
if let Err(e) = args.workspace(config) {
let mut h = HashMap::new();
h.insert("invalid".to_string(), e.to_string());

View File

@ -8,7 +8,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help version` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
let verbose = args.occurrences_of("verbose") > 0;
let version = cli::get_version_string(verbose);
cargo::drop_print!(config, "{}", version);

View File

@ -22,7 +22,7 @@ pub fn cli() -> App {
.after_help("Run `cargo help yank` for more detailed information.\n")
}
pub fn exec(config: &mut Config, args: &ArgMatches<'_>) -> CliResult {
pub fn exec(config: &mut Config, args: &ArgMatches) -> CliResult {
config.load_credentials()?;
let registry = args.registry(config)?;

View File

@ -116,7 +116,7 @@ fn list_commands(config: &Config) -> BTreeMap<String, CommandInfo> {
commands.insert(
cmd.get_name().to_string(),
CommandInfo::BuiltIn {
about: cmd.p.meta.about.map(|s| s.to_string()),
about: cmd.get_about().map(|s| s.to_string()),
},
);
}

View File

@ -30,9 +30,12 @@ mod version;
pub fn exit_with_error(err: CliError, shell: &mut Shell) -> ! {
debug!("exit_with_error; err={:?}", err);
if let Some(ref err) = err.error {
if let Some(clap_err) = err.downcast_ref::<clap::Error>() {
clap_err.exit()
let exit_code = if clap_err.use_stderr() { 1 } else { 0 };
let _ = clap_err.print();
std::process::exit(exit_code)
}
}

View File

@ -1,3 +1,5 @@
#![allow(deprecated)]
use crate::core::compiler::{BuildConfig, MessageFormat};
use crate::core::resolver::CliFeatures;
use crate::core::{Edition, Workspace};
@ -23,10 +25,10 @@ pub use crate::core::compiler::CompileMode;
pub use crate::{CliError, CliResult, Config};
pub use clap::{AppSettings, Arg, ArgMatches};
pub type App = clap::App<'static, 'static>;
pub type App = clap::App<'static>;
pub trait AppExt: Sized {
fn _arg(self, arg: Arg<'static, 'static>) -> Self;
fn _arg(self, arg: Arg<'static>) -> Self;
/// Do not use this method, it is only for backwards compatibility.
/// Use `arg_package_spec_no_all` instead.
@ -55,13 +57,13 @@ pub trait AppExt: Sized {
}
fn arg_package_spec_simple(self, package: &'static str) -> Self {
self._arg(optional_multi_opt("package", "SPEC", package).short("p"))
self._arg(optional_multi_opt("package", "SPEC", package).short('p'))
}
fn arg_package(self, package: &'static str) -> Self {
self._arg(
optional_opt("package", package)
.short("p")
.short('p')
.value_name("SPEC"),
)
}
@ -69,7 +71,7 @@ pub trait AppExt: Sized {
fn arg_jobs(self) -> Self {
self._arg(
opt("jobs", "Number of parallel jobs, defaults to # of CPUs")
.short("j")
.short('j')
.value_name("N"),
)
}
@ -142,7 +144,7 @@ pub trait AppExt: Sized {
}
fn arg_release(self, release: &'static str) -> Self {
self._arg(opt("release", release).short("r"))
self._arg(opt("release", release).short('r'))
}
fn arg_profile(self, profile: &'static str) -> Self {
@ -238,21 +240,21 @@ pub trait AppExt: Sized {
}
fn arg_quiet(self) -> Self {
self._arg(opt("quiet", "Do not print cargo log messages").short("q"))
self._arg(opt("quiet", "Do not print cargo log messages").short('q'))
}
}
impl AppExt for App {
fn _arg(self, arg: Arg<'static, 'static>) -> Self {
fn _arg(self, arg: Arg<'static>) -> Self {
self.arg(arg)
}
}
pub fn opt(name: &'static str, help: &'static str) -> Arg<'static, 'static> {
pub fn opt(name: &'static str, help: &'static str) -> Arg<'static> {
Arg::with_name(name).long(name).help(help)
}
pub fn optional_opt(name: &'static str, help: &'static str) -> Arg<'static, 'static> {
pub fn optional_opt(name: &'static str, help: &'static str) -> Arg<'static> {
opt(name, help).min_values(0)
}
@ -260,7 +262,7 @@ pub fn optional_multi_opt(
name: &'static str,
value_name: &'static str,
help: &'static str,
) -> Arg<'static, 'static> {
) -> Arg<'static> {
opt(name, help)
.value_name(value_name)
.multiple(true)
@ -268,11 +270,7 @@ pub fn optional_multi_opt(
.number_of_values(1)
}
pub fn multi_opt(
name: &'static str,
value_name: &'static str,
help: &'static str,
) -> Arg<'static, 'static> {
pub fn multi_opt(name: &'static str, value_name: &'static str, help: &'static str) -> Arg<'static> {
// Note that all `.multiple(true)` arguments in Cargo should specify
// `.number_of_values(1)` as well, so that `--foo val1 val2` is
// *not* parsed as `foo` with values ["val1", "val2"].
@ -308,7 +306,10 @@ pub trait ArgMatchesExt {
let arg = match self._value_of(name) {
None => None,
Some(arg) => Some(arg.parse::<u32>().map_err(|_| {
clap::Error::value_validation_auto(format!("could not parse `{}` as a number", arg))
clap::Error::raw(
clap::ErrorKind::ValueValidation,
format!("Invalid value: could not parse `{}` as a number", arg),
)
})?),
};
Ok(arg)
@ -320,7 +321,11 @@ pub trait ArgMatchesExt {
}
fn root_manifest(&self, config: &Config) -> CargoResult<PathBuf> {
if let Some(path) = self.value_of_path("manifest-path", config) {
if let Some(path) = self
._is_valid_arg("manifest-path")
.then(|| self.value_of_path("manifest-path", config))
.flatten()
{
// In general, we try to avoid normalizing paths in Cargo,
// but in this particular case we need it to fix #3586.
let path = paths::normalize_path(&path);
@ -395,8 +400,8 @@ pub trait ArgMatchesExt {
};
let name = match (
self._is_present("release"),
self._is_present("debug"),
self.is_valid_and_present("release"),
self.is_valid_and_present("debug"),
specified_profile,
) {
(false, false, None) => default,
@ -424,9 +429,13 @@ pub trait ArgMatchesExt {
fn packages_from_flags(&self) -> CargoResult<Packages> {
Packages::from_flags(
// TODO Integrate into 'workspace'
self._is_present("workspace") || self._is_present("all"),
self._values_of("exclude"),
self._values_of("package"),
self.is_valid_and_present("workspace") || self.is_valid_and_present("all"),
self._is_valid_arg("exclude")
.then(|| self._values_of("exclude"))
.unwrap_or_default(),
self._is_valid_arg("package")
.then(|| self._values_of("package"))
.unwrap_or_default(),
)
}
@ -503,9 +512,9 @@ pub trait ArgMatchesExt {
let mut build_config = BuildConfig::new(config, self.jobs()?, &self.targets(), mode)?;
build_config.message_format = message_format.unwrap_or(MessageFormat::Human);
build_config.requested_profile = self.get_profile_name(config, "dev", profile_checking)?;
build_config.build_plan = self._is_present("build-plan");
build_config.unit_graph = self._is_present("unit-graph");
build_config.future_incompat_report = self._is_present("future-incompat-report");
build_config.build_plan = self.is_valid_and_present("build-plan");
build_config.unit_graph = self.is_valid_and_present("unit-graph");
build_config.future_incompat_report = self.is_valid_and_present("future-incompat-report");
if build_config.build_plan {
config
.cli_unstable()
@ -522,28 +531,32 @@ pub trait ArgMatchesExt {
cli_features: self.cli_features()?,
spec,
filter: CompileFilter::from_raw_arguments(
self._is_present("lib"),
self.is_valid_and_present("lib"),
self._values_of("bin"),
self._is_present("bins"),
self._values_of("test"),
self._is_present("tests"),
self.is_valid_and_present("bins"),
self._is_valid_arg("test")
.then(|| self._values_of("test"))
.unwrap_or_default(),
self.is_valid_and_present("tests"),
self._values_of("example"),
self._is_present("examples"),
self._values_of("bench"),
self._is_present("benches"),
self._is_present("all-targets"),
self.is_valid_and_present("examples"),
self._is_valid_arg("bench")
.then(|| self._values_of("bench"))
.unwrap_or_default(),
self.is_valid_and_present("benches"),
self.is_valid_and_present("all-targets"),
),
target_rustdoc_args: None,
target_rustc_args: None,
target_rustc_crate_types: None,
local_rustdoc_args: None,
rustdoc_document_private_items: false,
honor_rust_version: !self._is_present("ignore-rust-version"),
honor_rust_version: !self.is_valid_and_present("ignore-rust-version"),
};
if let Some(ws) = workspace {
self.check_optional_opts(ws, &opts)?;
} else if self.is_present_with_zero_values("package") {
} else if self._is_valid_arg("package") && self.is_present_with_zero_values("package") {
// As for cargo 0.50.0, this won't occur but if someone sneaks in
// we can still provide this informative message for them.
anyhow::bail!(
@ -630,7 +643,7 @@ pub trait ArgMatchesExt {
workspace: &Workspace<'_>,
compile_opts: &CompileOptions,
) -> CargoResult<()> {
if self.is_present_with_zero_values("package") {
if self._is_valid_arg("package") && self.is_present_with_zero_values("package") {
print_available_packages(workspace)?
}
@ -642,11 +655,11 @@ pub trait ArgMatchesExt {
print_available_binaries(workspace, compile_opts)?;
}
if self.is_present_with_zero_values("bench") {
if self._is_valid_arg("bench") && self.is_present_with_zero_values("bench") {
print_available_benches(workspace, compile_opts)?;
}
if self.is_present_with_zero_values("test") {
if self._is_valid_arg("test") && self.is_present_with_zero_values("test") {
print_available_tests(workspace, compile_opts)?;
}
@ -657,6 +670,10 @@ pub trait ArgMatchesExt {
self._is_present(name) && self._value_of(name).is_none()
}
fn is_valid_and_present(&self, name: &str) -> bool {
self._is_valid_arg(name) && self._is_present(name)
}
fn _value_of(&self, name: &str) -> Option<&str>;
fn _values_of(&self, name: &str) -> Vec<String>;
@ -666,9 +683,11 @@ pub trait ArgMatchesExt {
fn _values_of_os(&self, name: &str) -> Vec<OsString>;
fn _is_present(&self, name: &str) -> bool;
fn _is_valid_arg(&self, name: &str) -> bool;
}
impl<'a> ArgMatchesExt for ArgMatches<'a> {
impl<'a> ArgMatchesExt for ArgMatches {
fn _value_of(&self, name: &str) -> Option<&str> {
self.value_of(name)
}
@ -694,13 +713,17 @@ impl<'a> ArgMatchesExt for ArgMatches<'a> {
fn _is_present(&self, name: &str) -> bool {
self.is_present(name)
}
fn _is_valid_arg(&self, name: &str) -> bool {
self.is_valid_arg(name)
}
}
pub fn values(args: &ArgMatches<'_>, name: &str) -> Vec<String> {
pub fn values(args: &ArgMatches, name: &str) -> Vec<String> {
args._values_of(name)
}
pub fn values_os(args: &ArgMatches<'_>, name: &str) -> Vec<OsString> {
pub fn values_os(args: &ArgMatches, name: &str) -> Vec<OsString> {
args._values_of_os(name)
}

View File

@ -300,6 +300,12 @@ impl From<clap::Error> for CliError {
}
}
impl From<std::io::Error> for CliError {
fn from(err: std::io::Error) -> CliError {
CliError::new(err.into(), 1)
}
}
// =============================================================================
// Construction helpers

View File

@ -473,22 +473,20 @@ fn includes() {
.with_stderr("")
.run();
cargo_process(
"config get build.rustflags --show-origin=yes -Zunstable-options -Zconfig-include",
)
.cwd(&sub_folder.parent().unwrap())
.masquerade_as_nightly_cargo()
.with_stdout(
"\
cargo_process("config get build.rustflags --show-origin -Zunstable-options -Zconfig-include")
.cwd(&sub_folder.parent().unwrap())
.masquerade_as_nightly_cargo()
.with_stdout(
"\
build.rustflags = [
\"--flag-other\", # [ROOT]/foo/.cargo/other.toml
\"--flag-directory\", # [ROOT]/foo/.cargo/config.toml
\"--flag-global\", # [ROOT]/home/.cargo/config.toml
]
",
)
.with_stderr("")
.run();
)
.with_stderr("")
.run();
cargo_process("config get --merged=no -Zunstable-options -Zconfig-include")
.cwd(&sub_folder.parent().unwrap())

View File

@ -1183,7 +1183,7 @@ fn cargo_metadata_bad_version() {
.with_status(1)
.with_stderr_contains(
"\
error: '2' isn't a valid value for '--format-version <VERSION>'
error: \"2\" isn't a valid value for '--format-version <VERSION>'
<tab>[possible values: 1]
",
)

View File

@ -362,7 +362,7 @@ fn new_default_edition() {
#[cargo_test]
fn new_with_bad_edition() {
cargo_process("new --edition something_else foo")
.with_stderr_contains("error: 'something_else' isn't a valid value[..]")
.with_stderr_contains("error: \"something_else\" isn't a valid value[..]")
.with_status(1)
.run();
}

View File

@ -1251,7 +1251,7 @@ fn run_multiple_packages() {
cargo().arg("-p").arg("d1").arg("-p").arg("d2")
.with_status(1)
.with_stderr_contains("error: The argument '--package <SPEC>' was provided more than once, but cannot be used multiple times").run();
.with_stderr_contains("error: The argument '--package <SPEC>...' was provided more than once, but cannot be used multiple times").run();
cargo()
.arg("-p")

View File

@ -540,7 +540,7 @@ fn fail_with_multiple_packages() {
.with_status(1)
.with_stderr_contains(
"\
error: The argument '--package <SPEC>' was provided more than once, \
error: The argument '--package <SPEC>...' was provided more than once, \
but cannot be used multiple times
",
)