perf(derive): Consolidate field/variant parsing

This commit is contained in:
Ed Page 2022-09-01 13:41:51 -05:00
parent 6e8b366107
commit 5264a62181
4 changed files with 127 additions and 121 deletions

View file

@ -36,8 +36,15 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream {
}) => { }) => {
let name = Name::Derived(ident.clone()); let name = Name::Derived(ident.clone());
let item = Item::from_args_struct(input, name); let item = Item::from_args_struct(input, name);
let fields = &fields.named; let fields = fields
gen_for_struct(&item, ident, &input.generics, fields) .named
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
})
.collect::<Vec<_>>();
gen_for_struct(&item, ident, &input.generics, &fields)
} }
Data::Struct(DataStruct { Data::Struct(DataStruct {
fields: Fields::Unit, fields: Fields::Unit,
@ -45,8 +52,15 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream {
}) => { }) => {
let name = Name::Derived(ident.clone()); let name = Name::Derived(ident.clone());
let item = Item::from_args_struct(input, name); let item = Item::from_args_struct(input, name);
let fields = &Punctuated::<Field, Comma>::new(); let fields = Punctuated::<Field, Comma>::new();
gen_for_struct(&item, ident, &input.generics, fields) let fields = fields
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
})
.collect::<Vec<_>>();
gen_for_struct(&item, ident, &input.generics, &fields)
} }
_ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"), _ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
} }
@ -56,12 +70,12 @@ pub fn gen_for_struct(
item: &Item, item: &Item,
item_name: &Ident, item_name: &Ident,
generics: &Generics, generics: &Generics,
fields: &Punctuated<Field, Comma>, fields: &[(&Field, Item)],
) -> TokenStream { ) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let constructor = gen_constructor(fields, item); let constructor = gen_constructor(fields);
let updater = gen_updater(fields, item, true); let updater = gen_updater(fields, true);
let raw_deprecated = raw_deprecated(); let raw_deprecated = raw_deprecated();
let app_var = Ident::new("__clap_app", Span::call_site()); let app_var = Ident::new("__clap_app", Span::call_site());
@ -131,17 +145,12 @@ pub fn gen_for_struct(
/// Generate a block of code to add arguments/subcommands corresponding to /// Generate a block of code to add arguments/subcommands corresponding to
/// the `fields` to an cmd. /// the `fields` to an cmd.
pub fn gen_augment( pub fn gen_augment(
fields: &Punctuated<Field, Comma>, fields: &[(&Field, Item)],
app_var: &Ident, app_var: &Ident,
parent_item: &Item, parent_item: &Item,
override_required: bool, override_required: bool,
) -> TokenStream { ) -> TokenStream {
let mut subcmds = fields.iter().filter_map(|field| { let mut subcmds = fields.iter().filter_map(|(field, item)| {
let item = Item::from_args_field(
field,
parent_item.casing(),
parent_item.env_casing(),
);
let kind = item.kind(); let kind = item.kind();
if let Kind::Subcommand(ty) = &*kind { if let Kind::Subcommand(ty) = &*kind {
let subcmd_type = match (**ty, sub_type(&field.ty)) { let subcmd_type = match (**ty, sub_type(&field.ty)) {
@ -182,12 +191,7 @@ pub fn gen_augment(
); );
} }
let args = fields.iter().filter_map(|field| { let args = fields.iter().filter_map(|(field, item)| {
let item = Item::from_args_field(
field,
parent_item.casing(),
parent_item.env_casing(),
);
let kind = item.kind(); let kind = item.kind();
match &*kind { match &*kind {
Kind::Subcommand(_) Kind::Subcommand(_)
@ -313,13 +317,8 @@ pub fn gen_augment(
}} }}
} }
pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_item: &Item) -> TokenStream { pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
let fields = fields.iter().map(|field| { let fields = fields.iter().map(|(field, item)| {
let item = Item::from_args_field(
field,
parent_item.casing(),
parent_item.env_casing(),
);
let field_name = field.ident.as_ref().unwrap(); let field_name = field.ident.as_ref().unwrap();
let kind = item.kind(); let kind = item.kind();
let arg_matches = format_ident!("__clap_arg_matches"); let arg_matches = format_ident!("__clap_arg_matches");
@ -366,7 +365,7 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_item: &Item) ->
}, },
Kind::Arg(ty) | Kind::FromGlobal(ty) => { Kind::Arg(ty) | Kind::FromGlobal(ty) => {
gen_parsers(&item, ty, field_name, field, None) gen_parsers(item, ty, field_name, field, None)
} }
} }
}); });
@ -376,17 +375,8 @@ pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_item: &Item) ->
}} }}
} }
pub fn gen_updater( pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
fields: &Punctuated<Field, Comma>, let fields = fields.iter().map(|(field, item)| {
parent_item: &Item,
use_self: bool,
) -> TokenStream {
let fields = fields.iter().map(|field| {
let item = Item::from_args_field(
field,
parent_item.casing(),
parent_item.env_casing(),
);
let field_name = field.ident.as_ref().unwrap(); let field_name = field.ident.as_ref().unwrap();
let kind = item.kind(); let kind = item.kind();
@ -447,7 +437,7 @@ pub fn gen_updater(
Kind::Skip(_) => quote!(), Kind::Skip(_) => quote!(),
Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(&item, ty, field_name, field, Some(&access)), Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(item, ty, field_name, field, Some(&access)),
} }
}); });

View file

@ -16,7 +16,6 @@ use proc_macro2::TokenStream;
use proc_macro_error::abort_call_site; use proc_macro_error::abort_call_site;
use quote::quote; use quote::quote;
use syn::Ident; use syn::Ident;
use syn::Token;
use syn::Variant; use syn::Variant;
use syn::{ use syn::{
self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields, self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields,
@ -42,8 +41,15 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
.ok() .ok()
.unwrap_or_default())); .unwrap_or_default()));
let item = Item::from_args_struct(input, name); let item = Item::from_args_struct(input, name);
let fields = &fields.named; let fields = fields
gen_for_struct(&item, ident, &input.generics, fields) .named
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
})
.collect::<Vec<_>>();
gen_for_struct(&item, ident, &input.generics, &fields)
} }
Data::Struct(DataStruct { Data::Struct(DataStruct {
fields: Fields::Unit, fields: Fields::Unit,
@ -55,8 +61,15 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
.ok() .ok()
.unwrap_or_default())); .unwrap_or_default()));
let item = Item::from_args_struct(input, name); let item = Item::from_args_struct(input, name);
let fields = &Punctuated::<Field, Comma>::new(); let fields = Punctuated::<Field, Comma>::new();
gen_for_struct(&item, ident, &input.generics, fields) let fields = fields
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
})
.collect::<Vec<_>>();
gen_for_struct(&item, ident, &input.generics, &fields)
} }
Data::Enum(ref e) => { Data::Enum(ref e) => {
dummies::parser_enum(ident); dummies::parser_enum(ident);
@ -65,8 +78,16 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
.ok() .ok()
.unwrap_or_default())); .unwrap_or_default()));
let item = Item::from_subcommand_enum(input, name); let item = Item::from_subcommand_enum(input, name);
let variants = &e.variants; let variants = e
gen_for_enum(&item, ident, &input.generics, variants) .variants
.iter()
.map(|variant| {
let item =
Item::from_subcommand_variant(variant, item.casing(), item.env_casing());
(variant, item)
})
.collect::<Vec<_>>();
gen_for_enum(&item, ident, &input.generics, &variants)
} }
_ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"), _ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
} }
@ -76,7 +97,7 @@ fn gen_for_struct(
item: &Item, item: &Item,
item_name: &Ident, item_name: &Ident,
generics: &Generics, generics: &Generics,
fields: &Punctuated<Field, Comma>, fields: &[(&Field, Item)],
) -> TokenStream { ) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
@ -95,7 +116,7 @@ fn gen_for_enum(
item: &Item, item: &Item,
item_name: &Ident, item_name: &Ident,
generics: &Generics, generics: &Generics,
variants: &Punctuated<Variant, Token![,]>, variants: &[(&Variant, Item)],
) -> TokenStream { ) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();

View file

@ -15,10 +15,7 @@
use proc_macro2::{Ident, Span, TokenStream}; use proc_macro2::{Ident, Span, TokenStream};
use proc_macro_error::{abort, abort_call_site}; use proc_macro_error::{abort, abort_call_site};
use quote::{format_ident, quote, quote_spanned}; use quote::{format_ident, quote, quote_spanned};
use syn::{ use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant};
punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Token,
Variant,
};
use crate::derives::args; use crate::derives::args;
use crate::dummies; use crate::dummies;
@ -34,8 +31,16 @@ pub fn derive_subcommand(input: &DeriveInput) -> TokenStream {
Data::Enum(ref e) => { Data::Enum(ref e) => {
let name = Name::Derived(ident.clone()); let name = Name::Derived(ident.clone());
let item = Item::from_subcommand_enum(input, name); let item = Item::from_subcommand_enum(input, name);
let variants = &e.variants; let variants = e
gen_for_enum(&item, ident, &input.generics, variants) .variants
.iter()
.map(|variant| {
let item =
Item::from_subcommand_variant(variant, item.casing(), item.env_casing());
(variant, item)
})
.collect::<Vec<_>>();
gen_for_enum(&item, ident, &input.generics, &variants)
} }
_ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"), _ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
} }
@ -45,16 +50,16 @@ pub fn gen_for_enum(
item: &Item, item: &Item,
item_name: &Ident, item_name: &Ident,
generics: &Generics, generics: &Generics,
variants: &Punctuated<Variant, Token![,]>, variants: &[(&Variant, Item)],
) -> TokenStream { ) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let from_arg_matches = gen_from_arg_matches(variants, item); let from_arg_matches = gen_from_arg_matches(variants);
let update_from_arg_matches = gen_update_from_arg_matches(variants, item); let update_from_arg_matches = gen_update_from_arg_matches(variants);
let augmentation = gen_augment(variants, item, false); let augmentation = gen_augment(variants, item, false);
let augmentation_update = gen_augment(variants, item, true); let augmentation_update = gen_augment(variants, item, true);
let has_subcommand = gen_has_subcommand(variants, item); let has_subcommand = gen_has_subcommand(variants);
quote! { quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)] #[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
@ -111,7 +116,7 @@ pub fn gen_for_enum(
} }
fn gen_augment( fn gen_augment(
variants: &Punctuated<Variant, Token![,]>, variants: &[(&Variant, Item)],
parent_item: &Item, parent_item: &Item,
override_required: bool, override_required: bool,
) -> TokenStream { ) -> TokenStream {
@ -121,12 +126,7 @@ fn gen_augment(
let subcommands: Vec<_> = variants let subcommands: Vec<_> = variants
.iter() .iter()
.filter_map(|variant| { .filter_map(|(variant, item)| {
let item = Item::from_subcommand_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
let kind = item.kind(); let kind = item.kind();
match &*kind { match &*kind {
@ -238,7 +238,15 @@ fn gen_augment(
let sub_augment = match variant.fields { let sub_augment = match variant.fields {
Named(ref fields) => { Named(ref fields) => {
// Defer to `gen_augment` for adding cmd methods // Defer to `gen_augment` for adding cmd methods
args::gen_augment(&fields.named, &subcommand_var, &item, override_required) let fields = fields
.named
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
})
.collect::<Vec<_>>();
args::gen_augment(&fields, &subcommand_var, item, override_required)
} }
Unit => { Unit => {
let arg_block = quote!( #subcommand_var ); let arg_block = quote!( #subcommand_var );
@ -300,23 +308,14 @@ fn gen_augment(
} }
} }
fn gen_has_subcommand( fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
variants: &Punctuated<Variant, Token![,]>,
parent_item: &Item,
) -> TokenStream {
use syn::Fields::*; use syn::Fields::*;
let mut ext_subcmd = false; let mut ext_subcmd = false;
let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
.iter() .iter()
.filter_map(|variant| { .filter_map(|(variant, item)| {
let item = Item::from_subcommand_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
if let Kind::ExternalSubcommand = &*item.kind() { if let Kind::ExternalSubcommand = &*item.kind() {
ext_subcmd = true; ext_subcmd = true;
None None
@ -367,10 +366,7 @@ fn gen_has_subcommand(
} }
} }
fn gen_from_arg_matches( fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
variants: &Punctuated<Variant, Token![,]>,
parent_item: &Item,
) -> TokenStream {
use syn::Fields::*; use syn::Fields::*;
let mut ext_subcmd = None; let mut ext_subcmd = None;
@ -379,13 +375,7 @@ fn gen_from_arg_matches(
let sub_arg_matches_var = format_ident!("__clap_arg_matches"); let sub_arg_matches_var = format_ident!("__clap_arg_matches");
let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
.iter() .iter()
.filter_map(|variant| { .filter_map(|(variant, item)| {
let item = Item::from_subcommand_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
if let Kind::ExternalSubcommand = &*item.kind() { if let Kind::ExternalSubcommand = &*item.kind() {
if ext_subcmd.is_some() { if ext_subcmd.is_some() {
abort!( abort!(
@ -443,7 +433,17 @@ fn gen_from_arg_matches(
let sub_name = item.cased_name(); let sub_name = item.cased_name();
let variant_name = &variant.ident; let variant_name = &variant.ident;
let constructor_block = match variant.fields { let constructor_block = match variant.fields {
Named(ref fields) => args::gen_constructor(&fields.named, item), Named(ref fields) => {
let fields = fields
.named
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
})
.collect::<Vec<_>>();
args::gen_constructor(&fields)
},
Unit => quote!(), Unit => quote!(),
Unnamed(ref fields) if fields.unnamed.len() == 1 => { Unnamed(ref fields) if fields.unnamed.len() == 1 => {
let ty = &fields.unnamed[0]; let ty = &fields.unnamed[0];
@ -519,21 +519,12 @@ fn gen_from_arg_matches(
} }
} }
fn gen_update_from_arg_matches( fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
variants: &Punctuated<Variant, Token![,]>,
parent_item: &Item,
) -> TokenStream {
use syn::Fields::*; use syn::Fields::*;
let (flatten, variants): (Vec<_>, Vec<_>) = variants let (flatten, variants): (Vec<_>, Vec<_>) = variants
.iter() .iter()
.filter_map(|variant| { .filter_map(|(variant, item)| {
let item = Item::from_subcommand_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
match &*item.kind() { match &*item.kind() {
// Fallback to `from_arg_matches_mut` // Fallback to `from_arg_matches_mut`
Kind::ExternalSubcommand => None, Kind::ExternalSubcommand => None,
@ -553,7 +544,15 @@ fn gen_update_from_arg_matches(
let field_names = fields.named.iter().map(|field| { let field_names = fields.named.iter().map(|field| {
field.ident.as_ref().unwrap() field.ident.as_ref().unwrap()
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
let update = args::gen_updater(&fields.named, item, false); let fields = fields
.named
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
})
.collect::<Vec<_>>();
let update = args::gen_updater(&fields, false);
(quote!( { #( #field_names, )* }), quote!( { #update } )) (quote!( { #( #field_names, )* }), quote!( { #update } ))
} }
Unit => (quote!(), quote!({})), Unit => (quote!(), quote!({})),

View file

@ -12,10 +12,7 @@ use proc_macro2::TokenStream;
use proc_macro_error::{abort, abort_call_site}; use proc_macro_error::{abort, abort_call_site};
use quote::quote; use quote::quote;
use quote::quote_spanned; use quote::quote_spanned;
use syn::{ use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant};
punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DeriveInput, Fields, Ident,
Variant,
};
use crate::dummies; use crate::dummies;
use crate::item::{Item, Kind, Name}; use crate::item::{Item, Kind, Name};
@ -29,19 +26,23 @@ pub fn derive_value_enum(input: &DeriveInput) -> TokenStream {
Data::Enum(ref e) => { Data::Enum(ref e) => {
let name = Name::Derived(ident.clone()); let name = Name::Derived(ident.clone());
let item = Item::from_value_enum(input, name); let item = Item::from_value_enum(input, name);
let variants = &e.variants; let variants = e
gen_for_enum(&item, ident, variants) .variants
.iter()
.map(|variant| {
let item =
Item::from_value_enum_variant(variant, item.casing(), item.env_casing());
(variant, item)
})
.collect::<Vec<_>>();
gen_for_enum(&item, ident, &variants)
} }
_ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"), _ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"),
} }
} }
pub fn gen_for_enum( pub fn gen_for_enum(_item: &Item, item_name: &Ident, variants: &[(&Variant, Item)]) -> TokenStream {
item: &Item, let lits = lits(variants);
item_name: &Ident,
variants: &Punctuated<Variant, Comma>,
) -> TokenStream {
let lits = lits(variants, item);
let value_variants = gen_value_variants(&lits); let value_variants = gen_value_variants(&lits);
let to_possible_value = gen_to_possible_value(&lits); let to_possible_value = gen_to_possible_value(&lits);
@ -66,15 +67,10 @@ pub fn gen_for_enum(
} }
} }
fn lits(variants: &Punctuated<Variant, Comma>, parent_item: &Item) -> Vec<(TokenStream, Ident)> { fn lits(variants: &[(&Variant, Item)]) -> Vec<(TokenStream, Ident)> {
variants variants
.iter() .iter()
.filter_map(|variant| { .filter_map(|(variant, item)| {
let item = Item::from_value_enum_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
if let Kind::Skip(_) = &*item.kind() { if let Kind::Skip(_) = &*item.kind() {
None None
} else { } else {