From 76b891db41c3b2171db45477094946a3cb572d9a Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 21 Jul 2023 14:21:45 -0500 Subject: [PATCH 1/2] test(parser): Reproduce suggestion --- tests/builder/error.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/tests/builder/error.rs b/tests/builder/error.rs index 4b0be802e24..e4fe0b06970 100644 --- a/tests/builder/error.rs +++ b/tests/builder/error.rs @@ -187,3 +187,26 @@ For more information, try '--help'. "; assert_error(err, expected_kind, MESSAGE, true); } + +#[test] +#[cfg(feature = "error-context")] +#[cfg(feature = "suggestions")] +fn cant_use_trailing_subcommand() { + let cmd = Command::new("test").subcommand(Command::new("bar")); + + let res = cmd.try_get_matches_from(["test", "baz"]); + assert!(res.is_err()); + let err = res.unwrap_err(); + let expected_kind = ErrorKind::InvalidSubcommand; + static MESSAGE: &str = "\ +error: unrecognized subcommand 'baz' + + tip: a similar subcommand exists: 'bar' + tip: to pass 'baz' as a value, use 'test -- baz' + +Usage: test [COMMAND] + +For more information, try '--help'. +"; + assert_error(err, expected_kind, MESSAGE, true); +} From 6590a855eef20d5d6ca362b2ac92c51df4502fe9 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Fri, 21 Jul 2023 14:27:15 -0500 Subject: [PATCH 2/2] fix(parser): Don't suggest -- as often See #2766 --- clap_builder/src/error/mod.rs | 25 +++++++++++++++---------- clap_builder/src/parser/parser.rs | 8 +++++--- tests/builder/error.rs | 1 - tests/builder/subcommands.rs | 3 --- 4 files changed, 20 insertions(+), 17 deletions(-) diff --git a/clap_builder/src/error/mod.rs b/clap_builder/src/error/mod.rs index 210a1717f1f..772058a5fe9 100644 --- a/clap_builder/src/error/mod.rs +++ b/clap_builder/src/error/mod.rs @@ -446,6 +446,7 @@ impl Error { subcmd: String, did_you_mean: Vec, name: String, + suggested_trailing_arg: bool, usage: Option, ) -> Self { use std::fmt::Write as _; @@ -456,15 +457,19 @@ impl Error { #[cfg(feature = "error-context")] { - let mut styled_suggestion = StyledStr::new(); - let _ = write!( - styled_suggestion, - "to pass '{}{subcmd}{}' as a value, use '{}{name} -- {subcmd}{}'", - invalid.render(), - invalid.render_reset(), - valid.render(), - valid.render_reset() - ); + let mut suggestions = vec![]; + if suggested_trailing_arg { + let mut styled_suggestion = StyledStr::new(); + let _ = write!( + styled_suggestion, + "to pass '{}{subcmd}{}' as a value, use '{}{name} -- {subcmd}{}'", + invalid.render(), + invalid.render_reset(), + valid.render(), + valid.render_reset() + ); + suggestions.push(styled_suggestion); + } err = err.extend_context_unchecked([ (ContextKind::InvalidSubcommand, ContextValue::String(subcmd)), @@ -474,7 +479,7 @@ impl Error { ), ( ContextKind::Suggested, - ContextValue::StyledStrs(vec![styled_suggestion]), + ContextValue::StyledStrs(suggestions), ), ]); if let Some(usage) = usage { diff --git a/clap_builder/src/parser/parser.rs b/clap_builder/src/parser/parser.rs index 3e02d4c0bf7..a4e15fef49d 100644 --- a/clap_builder/src/parser/parser.rs +++ b/clap_builder/src/parser/parser.rs @@ -474,6 +474,10 @@ impl<'cmd> Parser<'cmd> { } } + let suggested_trailing_arg = !trailing_values + && self.cmd.has_positionals() + && (arg_os.is_long() || arg_os.is_short()); + if !(self.cmd.is_args_conflicts_with_subcommands_set() && valid_arg_found) { let candidates = suggestions::did_you_mean( &arg_os.display().to_string(), @@ -489,6 +493,7 @@ impl<'cmd> Parser<'cmd> { .get_bin_name() .unwrap_or_else(|| self.cmd.get_name()) .to_owned(), + suggested_trailing_arg, Usage::new(self.cmd).create_usage_with_title(&[]), ); } @@ -505,9 +510,6 @@ impl<'cmd> Parser<'cmd> { } } - let suggested_trailing_arg = !trailing_values - && self.cmd.has_positionals() - && (arg_os.is_long() || arg_os.is_short()); ClapError::unknown_argument( self.cmd, arg_os.display().to_string(), diff --git a/tests/builder/error.rs b/tests/builder/error.rs index e4fe0b06970..c4ab3d2218f 100644 --- a/tests/builder/error.rs +++ b/tests/builder/error.rs @@ -202,7 +202,6 @@ fn cant_use_trailing_subcommand() { error: unrecognized subcommand 'baz' tip: a similar subcommand exists: 'bar' - tip: to pass 'baz' as a value, use 'test -- baz' Usage: test [COMMAND] diff --git a/tests/builder/subcommands.rs b/tests/builder/subcommands.rs index db57b2ecae4..a65cb3aca36 100644 --- a/tests/builder/subcommands.rs +++ b/tests/builder/subcommands.rs @@ -103,7 +103,6 @@ fn subcmd_did_you_mean_output() { error: unrecognized subcommand 'subcm' tip: a similar subcommand exists: 'subcmd' - tip: to pass 'subcm' as a value, use 'dym -- subcm' Usage: dym [COMMAND] @@ -123,7 +122,6 @@ fn subcmd_did_you_mean_output_ambiguous() { error: unrecognized subcommand 'te' tip: some similar subcommands exist: 'test', 'temp' - tip: to pass 'te' as a value, use 'dym -- te' Usage: dym [COMMAND] @@ -504,7 +502,6 @@ For more information, try 'help'. error: unrecognized subcommand 'baz' tip: a similar subcommand exists: 'bar' - tip: to pass 'baz' as a value, use ' -- baz' Usage: