From 1eebd207250afbefaeeff1e889bc6962474a15fa Mon Sep 17 00:00:00 2001 From: Leo Kettmeir Date: Mon, 23 Sep 2024 16:01:05 -0700 Subject: [PATCH] fix: error out if a valid flag is passed before a subcommand (#25830) Closes #25808 --- cli/args/flags.rs | 72 ++++++++++++++++++++++++++++++++++++++++------- 1 file changed, 62 insertions(+), 10 deletions(-) diff --git a/cli/args/flags.rs b/cli/args/flags.rs index 8490fdab65b96a..d325ce7bcb0e57 100644 --- a/cli/args/flags.rs +++ b/cli/args/flags.rs @@ -1233,6 +1233,51 @@ pub fn flags_from_vec(args: Vec) -> clap::error::Result { } if let Some((subcommand, mut m)) = matches.remove_subcommand() { + let pre_subcommand_arg = app + .get_arguments() + .filter(|arg| !arg.is_global_set()) + .find(|arg| { + matches + .value_source(arg.get_id().as_str()) + .is_some_and(|value| value == clap::parser::ValueSource::CommandLine) + }) + .map(|arg| { + format!( + "--{}", + arg.get_long().unwrap_or_else(|| arg.get_id().as_str()) + ) + }); + + if let Some(arg) = pre_subcommand_arg { + let usage = app.find_subcommand_mut(&subcommand).unwrap().render_usage(); + + let mut err = + clap::error::Error::new(ErrorKind::UnknownArgument).with_cmd(&app); + err.insert( + clap::error::ContextKind::InvalidArg, + clap::error::ContextValue::String(arg.clone()), + ); + + let valid = app.get_styles().get_valid(); + + let styled_suggestion = clap::builder::StyledStr::from(format!( + "'{}{subcommand} {arg}{}' exists", + valid.render(), + valid.render_reset() + )); + + err.insert( + clap::error::ContextKind::Suggested, + clap::error::ContextValue::StyledStrs(vec![styled_suggestion]), + ); + err.insert( + clap::error::ContextKind::Usage, + clap::error::ContextValue::StyledStr(usage), + ); + + return Err(err); + } + match subcommand.as_str() { "add" => add_parse(&mut flags, &mut m), "remove" => remove_parse(&mut flags, &mut m), @@ -4695,16 +4740,10 @@ fn run_parse( "[SCRIPT_ARG] may only be omitted with --v8-flags=--help, else to use the repl with arguments, please use the `deno repl` subcommand", )); } else { - return Err( - app - .get_subcommands_mut() - .find(|subcommand| subcommand.get_name() == "run") - .unwrap() - .error( - clap::error::ErrorKind::MissingRequiredArgument, - "[SCRIPT_ARG] may only be omitted with --v8-flags=--help", - ), - ); + return Err(app.find_subcommand_mut("run").unwrap().error( + clap::error::ErrorKind::MissingRequiredArgument, + "[SCRIPT_ARG] may only be omitted with --v8-flags=--help", + )); } Ok(()) @@ -10799,4 +10838,17 @@ mod tests { } ) } + + #[test] + fn flag_before_subcommand() { + let r = flags_from_vec(svec!["deno", "--allow-net", "repl"]); + assert_eq!( + r.unwrap_err().to_string(), + "error: unexpected argument '--allow-net' found + + tip: 'repl --allow-net' exists + +Usage: deno repl [OPTIONS] [-- [ARGS]...]\n" + ) + } }