From a89e20cf02f93d80a750066b634f2db0fb7035d4 Mon Sep 17 00:00:00 2001 From: Stefan Hristov Date: Tue, 10 Mar 2026 17:11:05 -0700 Subject: [PATCH 1/2] test: add test for hidden subcommand not appearing in suggestions --- tests/builder/subcommands.rs | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/tests/builder/subcommands.rs b/tests/builder/subcommands.rs index a3f6a205859..d4d31196bef 100644 --- a/tests/builder/subcommands.rs +++ b/tests/builder/subcommands.rs @@ -136,6 +136,24 @@ For more information, try '--help'. utils::assert_output(cmd, "dym te", DYM_SUBCMD_AMBIGUOUS, true); } +#[test] +#[cfg(feature = "suggestions")] +#[cfg(feature = "error-context")] +fn subcmd_did_you_mean_hidden_not_suggested() { + static DYM_SUBCMD_HIDDEN: &str = "\ +error: unrecognized subcommand 'tes' + +Usage: dym [COMMAND] + +For more information, try '--help'. +"; + + let cmd = Command::new("dym") + .subcommand(Command::new("test").hide(true)) + .subcommand(Command::new("other")); + utils::assert_output(cmd, "dym tes", DYM_SUBCMD_HIDDEN, true); +} + #[test] #[cfg(feature = "suggestions")] #[cfg(feature = "error-context")] @@ -502,7 +520,10 @@ For more information, try 'help'. #[cfg(feature = "suggestions")] { let err = cmd.clone().try_get_matches_from(["baz"]).unwrap_err(); - utils::assert_error(err, ErrorKind::InvalidSubcommand, str![[r#" + utils::assert_error( + err, + ErrorKind::InvalidSubcommand, + str![[r#" error: unrecognized subcommand 'baz' tip: a similar subcommand exists: 'bar' @@ -511,7 +532,9 @@ Usage: For more information, try 'help'. -"#]], true); +"#]], + true, + ); } // Verify whatever we did to get the above to work didn't disable `--help` and `--version`. From 4e3f32a19766060a627ce9a21db47126e7e79840 Mon Sep 17 00:00:00 2001 From: Stefan Hristov Date: Tue, 10 Mar 2026 17:11:15 -0700 Subject: [PATCH 2/2] fix(parser): exclude hidden subcommands from suggestions Hidden subcommands marked with .hide(true) were still appearing in 'did you mean' suggestions when users typed similar unrecognized commands. This change filters out hidden subcommands in all_subcommand_names() to maintain consistency with help text behavior where hidden commands are not displayed. The function is only used in two places: 1. Parser - for generating 'did you mean' suggestions 2. Validator - for listing available subcommands in error messages Both are user-facing error messages where hidden commands should not appear. Fixes the issue where hidden commands would be suggested despite being intentionally hidden from users. --- clap_builder/src/builder/command.rs | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/clap_builder/src/builder/command.rs b/clap_builder/src/builder/command.rs index b0d5bebd838..946a7876236 100644 --- a/clap_builder/src/builder/command.rs +++ b/clap_builder/src/builder/command.rs @@ -5024,11 +5024,13 @@ impl Command { /// Iterate through all the names of all subcommands (not recursively), including aliases. /// Used for suggestions. pub(crate) fn all_subcommand_names(&self) -> impl Iterator + Captures<'_> { - self.get_subcommands().flat_map(|sc| { - let name = sc.get_name(); - let aliases = sc.get_all_aliases(); - std::iter::once(name).chain(aliases) - }) + self.get_subcommands() + .filter(|sc| !sc.is_hide_set()) + .flat_map(|sc| { + let name = sc.get_name(); + let aliases = sc.get_all_aliases(); + std::iter::once(name).chain(aliases) + }) } pub(crate) fn required_graph(&self) -> ChildGraph {