Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(derive): Reserve behavior needed for implicit ArgGroups #4209

Merged
merged 3 commits into from Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -198,6 +198,7 @@ Subtle changes (i.e. compiler won't catch):
- *(env)* Parse `--help` and `--version` like any `ArgAction::SetTrue` flag (#3776)
- *(derive)* Leave `Arg::id` as `verbatim` casing, requiring updating of string references to other args like in `conflicts_with` or `requires` (#3282)
- *(derive)* Doc comments for `ValueEnum` variants will now show up in `--help` (#3312)
- *(derive)* When deriving `Args`, and `ArgGroup` is created using the type's name, reserving it for future use (#2621, #4209)

Easier to catch changes:

Expand Down
5 changes: 5 additions & 0 deletions clap_derive/src/attr.rs
Expand Up @@ -34,6 +34,11 @@ impl ClapAttr {
Some(Sp::new(AttrKind::StructOpt, attr.path.span()))
} else if attr.path.is_ident("command") {
Some(Sp::new(AttrKind::Command, attr.path.span()))
} else if attr.path.is_ident("group") {
abort!(
attr.path.span(),
"`#[group()]` attributes are not supported yet"
)
} else if attr.path.is_ident("arg") {
Some(Sp::new(AttrKind::Arg, attr.path.span()))
} else if attr.path.is_ident("value") {
Expand Down
6 changes: 5 additions & 1 deletion clap_derive/src/derives/args.rs
Expand Up @@ -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,
Expand Down Expand Up @@ -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
}}
Expand Down
30 changes: 26 additions & 4 deletions clap_derive/src/item.rs
Expand Up @@ -36,6 +36,7 @@ pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
#[derive(Clone)]
pub struct Item {
name: Name,
ident: Ident,
casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
ty: Option<Type>,
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -116,6 +120,7 @@ impl Item {
env_casing: Sp<CasingStyle>,
) -> 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 => {
Expand All @@ -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);
Expand Down Expand Up @@ -161,10 +173,12 @@ impl Item {
argument_casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> 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,
Expand All @@ -186,11 +200,13 @@ impl Item {
env_casing: Sp<CasingStyle>,
) -> 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,
Expand Down Expand Up @@ -234,13 +250,15 @@ impl Item {

fn new(
name: Name,
ident: Ident,
ty: Option<Type>,
casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
kind: Sp<Kind>,
) -> Self {
Self {
name,
ident,
ty,
casing,
env_casing,
Expand Down Expand Up @@ -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()
}
Expand Down
4 changes: 2 additions & 2 deletions clap_derive/src/lib.rs
Expand Up @@ -50,15 +50,15 @@ pub fn parser(input: TokenStream) -> TokenStream {
}

/// Generates the `Subcommand` impl.
#[proc_macro_derive(Subcommand, attributes(clap, command, arg))]
#[proc_macro_derive(Subcommand, attributes(clap, command, arg, group))]
#[proc_macro_error]
pub fn subcommand(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_subcommand(&input).into()
}

/// Generates the `Args` impl.
#[proc_macro_derive(Args, attributes(clap, command, arg))]
#[proc_macro_derive(Args, attributes(clap, command, arg, group))]
#[proc_macro_error]
pub fn args(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
Expand Down
4 changes: 4 additions & 0 deletions src/derive.rs
Expand Up @@ -282,6 +282,10 @@ pub trait FromArgMatches: Sized {
/// }
/// ```
pub trait Args: FromArgMatches + Sized {
/// Report the [`ArgGroup::id`][crate::ArgGroup::id] for this set of arguments
fn group_id() -> Option<crate::Id> {
None
}
/// Append to [`Command`] so it can instantiate `Self`.
///
/// See also [`CommandFactory`].
Expand Down
23 changes: 23 additions & 0 deletions 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()
);
}
1 change: 1 addition & 0 deletions tests/derive/main.rs
Expand Up @@ -13,6 +13,7 @@ mod explicit_name_no_renaming;
mod flags;
mod flatten;
mod generic;
mod groups;
mod help;
mod issues;
mod macros;
Expand Down
3 changes: 2 additions & 1 deletion tests/derive/naming.rs
@@ -1,3 +1,4 @@
use clap::Args;
use clap::Parser;

#[test]
Expand Down Expand Up @@ -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,
Expand Down