Skip to content

Commit

Permalink
Merge pull request #4179 from epage/attr
Browse files Browse the repository at this point in the history
refactor(derive): Prepare for builder attributes
  • Loading branch information
epage committed Sep 2, 2022
2 parents ebe181a + dbdd449 commit 20ba828
Show file tree
Hide file tree
Showing 13 changed files with 360 additions and 121 deletions.
1 change: 0 additions & 1 deletion CHANGELOG.md
Expand Up @@ -55,7 +55,6 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
- *(derive)* Changed the default for arguments from `parse` to `value_parser`., removing `parse` support
- *(derive)* `subcommand_required(true).arg_required_else_help(true)` is set instead of `SubcommandRequiredElseHelp` (#3280)
- *(derive)* Remove `arg_enum` attribute in favor of `value_enum`
- *(derive)* Remove `structopt()` attributes in favor of `clap()`

### Compatibility

Expand Down
44 changes: 41 additions & 3 deletions clap_derive/src/attr.rs
Expand Up @@ -5,15 +5,19 @@ use proc_macro_error::abort;
use proc_macro_error::ResultExt;
use quote::quote;
use quote::ToTokens;
use syn::spanned::Spanned;
use syn::{
parenthesized,
parse::{Parse, ParseStream},
punctuated::Punctuated,
Attribute, Expr, Ident, LitStr, Token,
};

use crate::utils::Sp;

#[derive(Clone)]
pub struct ClapAttr {
pub kind: Sp<AttrKind>,
pub name: Ident,
pub magic: Option<MagicAttrName>,
pub value: Option<AttrValue>,
Expand All @@ -23,10 +27,24 @@ impl ClapAttr {
pub fn parse_all(all_attrs: &[Attribute]) -> Vec<Self> {
all_attrs
.iter()
.filter(|attr| attr.path.is_ident("clap"))
.flat_map(|attr| {
.filter_map(|attr| {
let kind = if attr.path.is_ident("clap") {
Some(Sp::new(AttrKind::Clap, attr.path.span()))
} else if attr.path.is_ident("structopt") {
Some(Sp::new(AttrKind::StructOpt, attr.path.span()))
} else {
None
};
kind.map(|k| (k, attr))
})
.flat_map(|(k, attr)| {
attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated)
.unwrap_or_abort()
.into_iter()
.map(move |mut a| {
a.kind = k;
a
})
})
.collect()
}
Expand Down Expand Up @@ -113,7 +131,12 @@ impl Parse for ClapAttr {
None
};

Ok(Self { name, magic, value })
Ok(Self {
kind: Sp::new(AttrKind::Clap, name.span()),
name,
magic,
value,
})
}
}

Expand Down Expand Up @@ -166,3 +189,18 @@ impl ToTokens for AttrValue {
}
}
}

#[derive(Copy, Clone, PartialEq, Eq)]
pub enum AttrKind {
Clap,
StructOpt,
}

impl AttrKind {
pub fn as_str(&self) -> &'static str {
match self {
Self::Clap => "clap",
Self::StructOpt => "structopt",
}
}
}
38 changes: 33 additions & 5 deletions clap_derive/src/derives/args.rs
Expand Up @@ -72,6 +72,13 @@ pub fn gen_for_struct(
generics: &Generics,
fields: &[(&Field, Item)],
) -> TokenStream {
if !matches!(&*item.kind(), Kind::Command(_)) {
abort! { item.kind().span(),
"`{}` cannot be used with `command`",
item.kind().name(),
}
}

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

let constructor = gen_constructor(fields);
Expand Down Expand Up @@ -194,7 +201,9 @@ pub fn gen_augment(
let args = fields.iter().filter_map(|(field, item)| {
let kind = item.kind();
match &*kind {
Kind::Subcommand(_)
Kind::Command(_)
| Kind::Value(_)
| Kind::Subcommand(_)
| Kind::Skip(_)
| Kind::FromGlobal(_)
| Kind::ExternalSubcommand => None,
Expand Down Expand Up @@ -291,9 +300,16 @@ pub fn gen_augment(

let id = item.id();
let explicit_methods = item.field_methods(true);
let deprecations = if !override_required {
item.deprecations()
} else {
quote!()
};

Some(quote_spanned! { field.span()=>
let #app_var = #app_var.arg({
#deprecations

#[allow(deprecated)]
let arg = clap::Arg::new(#id)
#implicit_methods;
Expand All @@ -307,9 +323,15 @@ pub fn gen_augment(
}
});

let deprecations = if !override_required {
parent_item.deprecations()
} else {
quote!()
};
let initial_app_methods = parent_item.initial_top_level_methods();
let final_app_methods = parent_item.final_top_level_methods();
quote! {{
#deprecations
let #app_var = #app_var #initial_app_methods;
#( #args )*
#subcmd
Expand All @@ -323,9 +345,12 @@ pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
let kind = item.kind();
let arg_matches = format_ident!("__clap_arg_matches");
match &*kind {
Kind::ExternalSubcommand => {
Kind::Command(_)
| Kind::Value(_)
| Kind::ExternalSubcommand => {
abort! { kind.span(),
"`external_subcommand` can be used only on enum variants"
"`{}` cannot be used with `arg`",
kind.name(),
}
}
Kind::Subcommand(ty) => {
Expand Down Expand Up @@ -391,9 +416,12 @@ pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
let arg_matches = format_ident!("__clap_arg_matches");

match &*kind {
Kind::ExternalSubcommand => {
Kind::Command(_)
| Kind::Value(_)
| Kind::ExternalSubcommand => {
abort! { kind.span(),
"`external_subcommand` can be used only on enum variants"
"`{}` cannot be used with `arg`",
kind.name(),
}
}
Kind::Subcommand(ty) => {
Expand Down
44 changes: 41 additions & 3 deletions clap_derive/src/derives/subcommand.rs
Expand Up @@ -52,6 +52,13 @@ pub fn gen_for_enum(
generics: &Generics,
variants: &[(&Variant, Item)],
) -> TokenStream {
if !matches!(&*item.kind(), Kind::Command(_)) {
abort! { item.kind().span(),
"`{}` cannot be used with `command`",
item.kind().name(),
}
}

let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

let from_arg_matches = gen_from_arg_matches(variants);
Expand Down Expand Up @@ -143,9 +150,15 @@ fn gen_augment(
or `Vec<OsString>`."
),
};
let deprecations = if !override_required {
item.deprecations()
} else {
quote!()
};
let subcommand = match subty_if_name(ty, "Vec") {
Some(subty) => {
quote_spanned! { kind.span()=>
#deprecations
let #app_var = #app_var.external_subcommand_value_parser(clap::value_parser!(#subty));
}
}
Expand All @@ -162,18 +175,25 @@ fn gen_augment(
Kind::Flatten => match variant.fields {
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
let ty = &unnamed[0];
let deprecations = if !override_required {
item.deprecations()
} else {
quote!()
};
let old_heading_var = format_ident!("__clap_old_heading");
let next_help_heading = item.next_help_heading();
let next_display_order = item.next_display_order();
let subcommand = if override_required {
quote! {
#deprecations
let #old_heading_var = #app_var.get_next_help_heading().map(|s| clap::builder::Str::from(s.to_owned()));
let #app_var = #app_var #next_help_heading #next_display_order;
let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
let #app_var = #app_var.next_help_heading(clap::builder::Resettable::from(#old_heading_var));
}
} else {
quote! {
#deprecations
let #old_heading_var = #app_var.get_next_help_heading().map(|s| clap::builder::Str::from(s.to_owned()));
let #app_var = #app_var #next_help_heading #next_display_order;
let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
Expand Down Expand Up @@ -217,10 +237,16 @@ fn gen_augment(
};

let name = item.cased_name();
let deprecations = if !override_required {
item.deprecations()
} else {
quote!()
};
let initial_app_methods = item.initial_top_level_methods();
let final_from_attrs = item.final_top_level_methods();
let subcommand = quote! {
let #app_var = #app_var.subcommand({
#deprecations;
let #subcommand_var = clap::Command::new(#name);
let #subcommand_var = #subcommand_var #initial_app_methods;
let #subcommand_var = #arg_block;
Expand Down Expand Up @@ -286,9 +312,15 @@ fn gen_augment(
}
};

let deprecations = if !override_required {
item.deprecations()
} else {
quote!()
};
let name = item.cased_name();
let subcommand = quote! {
let #app_var = #app_var.subcommand({
#deprecations
let #subcommand_var = clap::Command::new(#name);
#sub_augment
});
Expand All @@ -299,12 +331,18 @@ fn gen_augment(
})
.collect();

let deprecations = if !override_required {
parent_item.deprecations()
} else {
quote!()
};
let initial_app_methods = parent_item.initial_top_level_methods();
let final_app_methods = parent_item.final_top_level_methods();
quote! {
let #app_var = #app_var #initial_app_methods;
#( #subcommands )*;
#app_var #final_app_methods
#deprecations;
let #app_var = #app_var #initial_app_methods;
#( #subcommands )*;
#app_var #final_app_methods
}
}

Expand Down
22 changes: 17 additions & 5 deletions clap_derive/src/derives/value_enum.rs
Expand Up @@ -41,10 +41,17 @@ pub fn derive_value_enum(input: &DeriveInput) -> TokenStream {
}
}

pub fn gen_for_enum(_item: &Item, item_name: &Ident, variants: &[(&Variant, Item)]) -> TokenStream {
pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item)]) -> TokenStream {
if !matches!(&*item.kind(), Kind::Value(_)) {
abort! { item.kind().span(),
"`{}` cannot be used with `value`",
item.kind().name(),
}
}

let lits = lits(variants);
let value_variants = gen_value_variants(&lits);
let to_possible_value = gen_to_possible_value(&lits);
let to_possible_value = gen_to_possible_value(item, &lits);

quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
Expand Down Expand Up @@ -78,12 +85,14 @@ fn lits(variants: &[(&Variant, Item)]) -> Vec<(TokenStream, Ident)> {
abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
}
let fields = item.field_methods(false);
let deprecations = item.deprecations();
let name = item.cased_name();
Some((
quote_spanned! { variant.span()=>
quote_spanned! { variant.span()=> {
#deprecations
clap::builder::PossibleValue::new(#name)
#fields
},
}},
variant.ident.clone(),
))
}
Expand All @@ -101,11 +110,14 @@ fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream {
}
}

fn gen_to_possible_value(lits: &[(TokenStream, Ident)]) -> TokenStream {
fn gen_to_possible_value(item: &Item, lits: &[(TokenStream, Ident)]) -> TokenStream {
let (lit, variant): (Vec<TokenStream>, Vec<Ident>) = lits.iter().cloned().unzip();

let deprecations = item.deprecations();

quote! {
fn to_possible_value<'a>(&self) -> ::std::option::Option<clap::builder::PossibleValue> {
#deprecations
match self {
#(Self::#variant => Some(#lit),)*
_ => None
Expand Down

0 comments on commit 20ba828

Please sign in to comment.