From 486869746436c79db094cfadeefe1de4d631a481 Mon Sep 17 00:00:00 2001 From: Ed Page Date: Tue, 13 Sep 2022 07:15:56 -0500 Subject: [PATCH] feat(derive): Reserve the T group name Since the `name` is changed to be the package name, we can't use it as (1) its not as predictable and (2) it can lead to conflicts if a `Parser` is flattened into a `Parser` --- clap_derive/src/derives/args.rs | 6 +++++- clap_derive/src/item.rs | 30 ++++++++++++++++++++++++++---- tests/derive/groups.rs | 23 +++++++++++++++++++++++ tests/derive/main.rs | 1 + tests/derive/naming.rs | 3 ++- 5 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 tests/derive/groups.rs diff --git a/clap_derive/src/derives/args.rs b/clap_derive/src/derives/args.rs index f14ac09d95ed..02cb052351b4 100644 --- a/clap_derive/src/derives/args.rs +++ b/clap_derive/src/derives/args.rs @@ -15,6 +15,7 @@ use proc_macro2::{Ident, Span, TokenStream}; use proc_macro_error::{abort, abort_call_site}; use quote::{format_ident, quote, quote_spanned}; +use syn::ext::IdentExt; use syn::{ punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field, Fields, Generics, @@ -317,10 +318,13 @@ pub fn gen_augment( quote!() }; let initial_app_methods = parent_item.initial_top_level_methods(); + let group_id = parent_item.ident().unraw().to_string(); let final_app_methods = parent_item.final_top_level_methods(); quote! {{ #deprecations - let #app_var = #app_var #initial_app_methods; + let #app_var = #app_var + #initial_app_methods + .group(clap::ArgGroup::new(#group_id).multiple(true)); #( #args )* #app_var #final_app_methods }} diff --git a/clap_derive/src/item.rs b/clap_derive/src/item.rs index 108f2894bf01..3808364b6fe2 100644 --- a/clap_derive/src/item.rs +++ b/clap_derive/src/item.rs @@ -36,6 +36,7 @@ pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake; #[derive(Clone)] pub struct Item { name: Name, + ident: Ident, casing: Sp, env_casing: Sp, ty: Option, @@ -54,13 +55,14 @@ pub struct Item { impl Item { pub fn from_args_struct(input: &DeriveInput, name: Name) -> Self { + let ident = input.ident.clone(); let span = input.ident.span(); let attrs = &input.attrs; let argument_casing = Sp::new(DEFAULT_CASING, span); let env_casing = Sp::new(DEFAULT_ENV_CASING, span); let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span); - let mut res = Self::new(name, None, argument_casing, env_casing, kind); + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); let parsed_attrs = ClapAttr::parse_all(attrs); res.infer_kind(&parsed_attrs); res.push_attrs(&parsed_attrs); @@ -70,13 +72,14 @@ impl Item { } pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Self { + let ident = input.ident.clone(); let span = input.ident.span(); let attrs = &input.attrs; let argument_casing = Sp::new(DEFAULT_CASING, span); let env_casing = Sp::new(DEFAULT_ENV_CASING, span); let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span); - let mut res = Self::new(name, None, argument_casing, env_casing, kind); + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); let parsed_attrs = ClapAttr::parse_all(attrs); res.infer_kind(&parsed_attrs); res.push_attrs(&parsed_attrs); @@ -86,13 +89,14 @@ impl Item { } pub fn from_value_enum(input: &DeriveInput, name: Name) -> Self { + let ident = input.ident.clone(); let span = input.ident.span(); let attrs = &input.attrs; let argument_casing = Sp::new(DEFAULT_CASING, span); let env_casing = Sp::new(DEFAULT_ENV_CASING, span); let kind = Sp::new(Kind::Value, span); - let mut res = Self::new(name, None, argument_casing, env_casing, kind); + let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind); let parsed_attrs = ClapAttr::parse_all(attrs); res.infer_kind(&parsed_attrs); res.push_attrs(&parsed_attrs); @@ -116,6 +120,7 @@ impl Item { env_casing: Sp, ) -> Self { let name = variant.ident.clone(); + let ident = variant.ident.clone(); let span = variant.span(); let ty = match variant.fields { syn::Fields::Unnamed(syn::FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => { @@ -126,7 +131,14 @@ impl Item { } }; let kind = Sp::new(Kind::Command(ty), span); - let mut res = Self::new(Name::Derived(name), None, struct_casing, env_casing, kind); + let mut res = Self::new( + Name::Derived(name), + ident, + None, + struct_casing, + env_casing, + kind, + ); let parsed_attrs = ClapAttr::parse_all(&variant.attrs); res.infer_kind(&parsed_attrs); res.push_attrs(&parsed_attrs); @@ -161,10 +173,12 @@ impl Item { argument_casing: Sp, env_casing: Sp, ) -> Self { + let ident = variant.ident.clone(); let span = variant.span(); let kind = Sp::new(Kind::Value, span); let mut res = Self::new( Name::Derived(variant.ident.clone()), + ident, None, argument_casing, env_casing, @@ -186,11 +200,13 @@ impl Item { env_casing: Sp, ) -> Self { let name = field.ident.clone().unwrap(); + let ident = field.ident.clone().unwrap(); let span = field.span(); let ty = Ty::from_syn_ty(&field.ty); let kind = Sp::new(Kind::Arg(ty), span); let mut res = Self::new( Name::Derived(name), + ident, Some(field.ty.clone()), struct_casing, env_casing, @@ -234,6 +250,7 @@ impl Item { fn new( name: Name, + ident: Ident, ty: Option, casing: Sp, env_casing: Sp, @@ -241,6 +258,7 @@ impl Item { ) -> Self { Self { name, + ident, ty, casing, env_casing, @@ -894,6 +912,10 @@ impl Item { quote!( #(#next_help_heading)* ) } + pub fn ident(&self) -> &Ident { + &self.ident + } + pub fn id(&self) -> TokenStream { self.name.clone().raw() } diff --git a/tests/derive/groups.rs b/tests/derive/groups.rs new file mode 100644 index 000000000000..edc2d93c91c1 --- /dev/null +++ b/tests/derive/groups.rs @@ -0,0 +1,23 @@ +use clap::Parser; + +#[test] +fn test_safely_nest_parser() { + #[derive(Parser, Debug, PartialEq)] + struct Opt { + #[command(flatten)] + foo: Foo, + } + + #[derive(Parser, Debug, PartialEq)] + struct Foo { + #[arg(long)] + foo: bool, + } + + assert_eq!( + Opt { + foo: Foo { foo: true } + }, + Opt::try_parse_from(&["test", "--foo"]).unwrap() + ); +} diff --git a/tests/derive/main.rs b/tests/derive/main.rs index 02e40d3ed2d9..33b59cdf9824 100644 --- a/tests/derive/main.rs +++ b/tests/derive/main.rs @@ -13,6 +13,7 @@ mod explicit_name_no_renaming; mod flags; mod flatten; mod generic; +mod groups; mod help; mod issues; mod macros; diff --git a/tests/derive/naming.rs b/tests/derive/naming.rs index 693856ff133e..9afad2abfd23 100644 --- a/tests/derive/naming.rs +++ b/tests/derive/naming.rs @@ -1,3 +1,4 @@ +use clap::Args; use clap::Parser; #[test] @@ -251,7 +252,7 @@ fn test_rename_all_is_not_propagated_from_struct_into_flattened() { foo: Foo, } - #[derive(Parser, Debug, PartialEq)] + #[derive(Args, Debug, PartialEq)] struct Foo { #[arg(long)] foo: bool,