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

refactor(derive): Prepare for builder attributes #4179

Merged
merged 9 commits into from Sep 2, 2022
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