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 item = Item::from_args_struct(input, name);
let fields = &fields.named;
gen_for_struct(&item, ident, &input.generics, fields)
let fields = 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 {
fields: Fields::Unit,
@ -45,8 +52,15 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream {
}) => {
let name = Name::Derived(ident.clone());
let item = Item::from_args_struct(input, name);
let fields = &Punctuated::<Field, Comma>::new();
gen_for_struct(&item, ident, &input.generics, fields)
let fields = Punctuated::<Field, Comma>::new();
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"),
}
@ -56,12 +70,12 @@ pub fn gen_for_struct(
item: &Item,
item_name: &Ident,
generics: &Generics,
fields: &Punctuated<Field, Comma>,
fields: &[(&Field, Item)],
) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let constructor = gen_constructor(fields, item);
let updater = gen_updater(fields, item, true);
let constructor = gen_constructor(fields);
let updater = gen_updater(fields, true);
let raw_deprecated = raw_deprecated();
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
/// the `fields` to an cmd.
pub fn gen_augment(
fields: &Punctuated<Field, Comma>,
fields: &[(&Field, Item)],
app_var: &Ident,
parent_item: &Item,
override_required: bool,
) -> TokenStream {
let mut subcmds = fields.iter().filter_map(|field| {
let item = Item::from_args_field(
field,
parent_item.casing(),
parent_item.env_casing(),
);
let mut subcmds = fields.iter().filter_map(|(field, item)| {
let kind = item.kind();
if let Kind::Subcommand(ty) = &*kind {
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 item = Item::from_args_field(
field,
parent_item.casing(),
parent_item.env_casing(),
);
let args = fields.iter().filter_map(|(field, item)| {
let kind = item.kind();
match &*kind {
Kind::Subcommand(_)
@ -313,13 +317,8 @@ pub fn gen_augment(
}}
}
pub fn gen_constructor(fields: &Punctuated<Field, Comma>, parent_item: &Item) -> TokenStream {
let fields = fields.iter().map(|field| {
let item = Item::from_args_field(
field,
parent_item.casing(),
parent_item.env_casing(),
);
pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
let fields = fields.iter().map(|(field, item)| {
let field_name = field.ident.as_ref().unwrap();
let kind = item.kind();
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) => {
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(
fields: &Punctuated<Field, Comma>,
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(),
);
pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
let fields = fields.iter().map(|(field, item)| {
let field_name = field.ident.as_ref().unwrap();
let kind = item.kind();
@ -447,7 +437,7 @@ pub fn gen_updater(
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 quote::quote;
use syn::Ident;
use syn::Token;
use syn::Variant;
use syn::{
self, punctuated::Punctuated, token::Comma, Data, DataStruct, DeriveInput, Field, Fields,
@ -42,8 +41,15 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
.ok()
.unwrap_or_default()));
let item = Item::from_args_struct(input, name);
let fields = &fields.named;
gen_for_struct(&item, ident, &input.generics, fields)
let fields = 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 {
fields: Fields::Unit,
@ -55,8 +61,15 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
.ok()
.unwrap_or_default()));
let item = Item::from_args_struct(input, name);
let fields = &Punctuated::<Field, Comma>::new();
gen_for_struct(&item, ident, &input.generics, fields)
let fields = Punctuated::<Field, Comma>::new();
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) => {
dummies::parser_enum(ident);
@ -65,8 +78,16 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
.ok()
.unwrap_or_default()));
let item = Item::from_subcommand_enum(input, name);
let variants = &e.variants;
gen_for_enum(&item, ident, &input.generics, variants)
let variants = e
.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"),
}
@ -76,7 +97,7 @@ fn gen_for_struct(
item: &Item,
item_name: &Ident,
generics: &Generics,
fields: &Punctuated<Field, Comma>,
fields: &[(&Field, Item)],
) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
@ -95,7 +116,7 @@ fn gen_for_enum(
item: &Item,
item_name: &Ident,
generics: &Generics,
variants: &Punctuated<Variant, Token![,]>,
variants: &[(&Variant, Item)],
) -> TokenStream {
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_macro_error::{abort, abort_call_site};
use quote::{format_ident, quote, quote_spanned};
use syn::{
punctuated::Punctuated, spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Token,
Variant,
};
use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, Variant};
use crate::derives::args;
use crate::dummies;
@ -34,8 +31,16 @@ pub fn derive_subcommand(input: &DeriveInput) -> TokenStream {
Data::Enum(ref e) => {
let name = Name::Derived(ident.clone());
let item = Item::from_subcommand_enum(input, name);
let variants = &e.variants;
gen_for_enum(&item, ident, &input.generics, variants)
let variants = e
.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"),
}
@ -45,16 +50,16 @@ pub fn gen_for_enum(
item: &Item,
item_name: &Ident,
generics: &Generics,
variants: &Punctuated<Variant, Token![,]>,
variants: &[(&Variant, Item)],
) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let from_arg_matches = gen_from_arg_matches(variants, item);
let update_from_arg_matches = gen_update_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);
let augmentation = gen_augment(variants, item, false);
let augmentation_update = gen_augment(variants, item, true);
let has_subcommand = gen_has_subcommand(variants, item);
let has_subcommand = gen_has_subcommand(variants);
quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
@ -111,7 +116,7 @@ pub fn gen_for_enum(
}
fn gen_augment(
variants: &Punctuated<Variant, Token![,]>,
variants: &[(&Variant, Item)],
parent_item: &Item,
override_required: bool,
) -> TokenStream {
@ -121,12 +126,7 @@ fn gen_augment(
let subcommands: Vec<_> = variants
.iter()
.filter_map(|variant| {
let item = Item::from_subcommand_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
.filter_map(|(variant, item)| {
let kind = item.kind();
match &*kind {
@ -238,7 +238,15 @@ fn gen_augment(
let sub_augment = match variant.fields {
Named(ref fields) => {
// 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 => {
let arg_block = quote!( #subcommand_var );
@ -300,23 +308,14 @@ fn gen_augment(
}
}
fn gen_has_subcommand(
variants: &Punctuated<Variant, Token![,]>,
parent_item: &Item,
) -> TokenStream {
fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
use syn::Fields::*;
let mut ext_subcmd = false;
let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
.iter()
.filter_map(|variant| {
let item = Item::from_subcommand_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
.filter_map(|(variant, item)| {
if let Kind::ExternalSubcommand = &*item.kind() {
ext_subcmd = true;
None
@ -367,10 +366,7 @@ fn gen_has_subcommand(
}
}
fn gen_from_arg_matches(
variants: &Punctuated<Variant, Token![,]>,
parent_item: &Item,
) -> TokenStream {
fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
use syn::Fields::*;
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 (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
.iter()
.filter_map(|variant| {
let item = Item::from_subcommand_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
.filter_map(|(variant, item)| {
if let Kind::ExternalSubcommand = &*item.kind() {
if ext_subcmd.is_some() {
abort!(
@ -443,7 +433,17 @@ fn gen_from_arg_matches(
let sub_name = item.cased_name();
let variant_name = &variant.ident;
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!(),
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
let ty = &fields.unnamed[0];
@ -519,21 +519,12 @@ fn gen_from_arg_matches(
}
}
fn gen_update_from_arg_matches(
variants: &Punctuated<Variant, Token![,]>,
parent_item: &Item,
) -> TokenStream {
fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
use syn::Fields::*;
let (flatten, variants): (Vec<_>, Vec<_>) = variants
.iter()
.filter_map(|variant| {
let item = Item::from_subcommand_variant(
variant,
parent_item.casing(),
parent_item.env_casing(),
);
.filter_map(|(variant, item)| {
match &*item.kind() {
// Fallback to `from_arg_matches_mut`
Kind::ExternalSubcommand => None,
@ -553,7 +544,15 @@ fn gen_update_from_arg_matches(
let field_names = fields.named.iter().map(|field| {
field.ident.as_ref().unwrap()
}).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 } ))
}
Unit => (quote!(), quote!({})),

View file

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