diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs index 4105608829d..d6fecc01970 100644 --- a/clap_derive/src/derives/args.rs +++ b/clap_derive/src/derives/args.rs @@ -228,8 +228,16 @@ pub(crate) fn gen_augment( let next_help_heading = item.next_help_heading(); let next_display_order = item.next_display_order(); + let flatten_group_assert = if matches!(**ty, Ty::Option) { + quote_spanned! { kind.span()=> + <#inner_type as clap::Args>::group_id().expect("cannot `#[flatten]` an `Option` with `#[group(skip)]"); + } + } else { + quote! {} + }; if override_required { Some(quote_spanned! { kind.span()=> + #flatten_group_assert let #app_var = #app_var #next_help_heading #next_display_order; @@ -237,6 +245,7 @@ pub(crate) fn gen_augment( }) } else { Some(quote_spanned! { kind.span()=> + #flatten_group_assert let #app_var = #app_var #next_help_heading #next_display_order; @@ -499,7 +508,7 @@ pub(crate) fn gen_constructor(fields: &[(&Field, Item)]) -> Result #field_name: { let group_id = <#inner_type as clap::Args>::group_id() - .expect("`#[arg(flatten)]`ed field type implements `Args::group_id`"); + .expect("asserted during `Arg` creation"); if #arg_matches.contains_id(group_id.as_str()) { Some( <#inner_type as clap::FromArgMatches>::from_arg_matches_mut(#arg_matches)? diff --git a/tests/derive/flatten.rs b/tests/derive/flatten.rs index bdd0387338c..95ad43e2bee 100644 --- a/tests/derive/flatten.rs +++ b/tests/derive/flatten.rs @@ -255,3 +255,22 @@ fn docstrings_ordering_with_multiple_clap_partial() { assert!(short_help.contains("This is the docstring for Flattened")); } + +#[test] +#[should_panic = "cannot `#[flatten]` an `Option` with `#[group(skip)]"] +fn flatten_skipped_group() { + #[derive(clap::Parser, Debug)] + struct Cli { + #[clap(flatten)] + args: Option, + } + + #[derive(clap::Args, Debug)] + #[group(skip)] + struct Args { + #[clap(short)] + param: bool, + } + + Cli::try_parse_from(["test"]).unwrap(); +}