perf(derive): Further consolidate parsing of attrs

This commit is contained in:
Ed Page 2022-09-01 11:24:57 -05:00
parent d8524b5ad1
commit 0b5f95e3a3
7 changed files with 135 additions and 172 deletions

View file

@ -1,12 +1,12 @@
use std::iter::FromIterator;
use proc_macro2::TokenStream;
use proc_macro_error::abort;
use proc_macro_error::ResultExt;
use quote::quote;
use quote::ToTokens;
use proc_macro2::TokenStream;
use proc_macro_error::{abort, ResultExt};
use syn::{
self, parenthesized,
parenthesized,
parse::{Parse, ParseStream},
punctuated::Punctuated,
Attribute, Expr, Ident, LitStr, Token,

View file

@ -12,20 +12,18 @@
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use crate::{
dummies,
item::{Item, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
utils::{inner_type, sub_type, Sp, Ty},
};
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, token::Comma, Attribute, Data, DataStruct,
DeriveInput, Field, Fields, Generics,
punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataStruct, DeriveInput, Field,
Fields, Generics,
};
use crate::dummies;
use crate::item::{Item, Kind, Name};
use crate::utils::{inner_type, sub_type, Sp, Ty};
pub fn derive_args(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
@ -35,43 +33,43 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream {
Data::Struct(DataStruct {
fields: Fields::Named(ref fields),
..
}) => gen_for_struct(ident, &input.generics, &fields.named, &input.attrs),
}) => {
let name = Name::Derived(ident.clone());
let item = Item::from_args_struct(input, name);
gen_for_struct(&item, ident, &input.generics, &fields.named)
}
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => gen_for_struct(
ident,
&input.generics,
&Punctuated::<Field, Comma>::new(),
&input.attrs,
),
}) => {
let name = Name::Derived(ident.clone());
let item = Item::from_args_struct(input, name);
gen_for_struct(
&item,
ident,
&input.generics,
&Punctuated::<Field, Comma>::new(),
)
}
_ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
}
}
pub fn gen_for_struct(
struct_name: &Ident,
item: &Item,
item_name: &Ident,
generics: &Generics,
fields: &Punctuated<Field, Comma>,
attrs: &[Attribute],
) -> TokenStream {
let item = Item::from_args_struct(
Span::call_site(),
attrs,
Name::Derived(struct_name.clone()),
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
);
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, item);
let updater = gen_updater(fields, item, true);
let raw_deprecated = raw_deprecated();
let app_var = Ident::new("__clap_app", Span::call_site());
let augmentation = gen_augment(fields, &app_var, &item, false);
let augmentation_update = gen_augment(fields, &app_var, &item, true);
let augmentation = gen_augment(fields, &app_var, item, false);
let augmentation_update = gen_augment(fields, &app_var, item, true);
quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
@ -87,14 +85,14 @@ pub fn gen_for_struct(
clippy::suspicious_else_formatting,
)]
#[deny(clippy::correctness)]
impl #impl_generics clap::FromArgMatches for #struct_name #ty_generics #where_clause {
impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
}
fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
#raw_deprecated
let v = #struct_name #constructor;
let v = #item_name #constructor;
::std::result::Result::Ok(v)
}
@ -122,7 +120,7 @@ pub fn gen_for_struct(
clippy::suspicious_else_formatting,
)]
#[deny(clippy::correctness)]
impl #impl_generics clap::Args for #struct_name #ty_generics #where_clause {
impl #impl_generics clap::Args for #item_name #ty_generics #where_clause {
fn augment_args<'b>(#app_var: clap::Command) -> clap::Command {
#augmentation
}

View file

@ -12,36 +12,18 @@
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use std::env;
use proc_macro2::{Span, TokenStream};
use quote::quote;
use syn::{Attribute, Generics, Ident};
use syn::{Generics, Ident};
use crate::{
item::{Item, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
utils::Sp,
};
use crate::item::Item;
pub fn gen_for_struct(
struct_name: &Ident,
generics: &Generics,
attrs: &[Attribute],
) -> TokenStream {
let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let item = Item::from_args_struct(
Span::call_site(),
attrs,
Name::Assigned(quote!(#app_name)),
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
);
let name = item.cased_name();
let app_var = Ident::new("__clap_app", Span::call_site());
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let tokens = quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
@ -56,7 +38,7 @@ pub fn gen_for_struct(
clippy::suspicious_else_formatting,
)]
#[deny(clippy::correctness)]
impl #impl_generics clap::CommandFactory for #struct_name #ty_generics #where_clause {
impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause {
fn command<'b>() -> clap::Command {
let #app_var = clap::Command::new(#name);
<Self as clap::Args>::augment_args(#app_var)
@ -72,21 +54,12 @@ pub fn gen_for_struct(
tokens
}
pub fn gen_for_enum(enum_name: &Ident, generics: &Generics, attrs: &[Attribute]) -> TokenStream {
let app_name = env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let item = Item::from_subcommand_enum(
Span::call_site(),
attrs,
Name::Assigned(quote!(#app_name)),
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
);
let name = item.cased_name();
let app_var = Ident::new("__clap_app", Span::call_site());
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
@ -101,7 +74,7 @@ pub fn gen_for_enum(enum_name: &Ident, generics: &Generics, attrs: &[Attribute])
clippy::suspicious_else_formatting,
)]
#[deny(clippy::correctness)]
impl #impl_generics clap::CommandFactory for #enum_name #ty_generics #where_clause {
impl #impl_generics clap::CommandFactory for #item_name #ty_generics #where_clause {
fn command<'b>() -> clap::Command {
let #app_var = clap::Command::new(#name)
.subcommand_required(true)

View file

@ -12,19 +12,20 @@
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use crate::{
derives::{args, into_app, subcommand},
dummies,
};
use proc_macro2::TokenStream;
use proc_macro_error::abort_call_site;
use quote::quote;
use syn::Ident;
use syn::{
self, punctuated::Punctuated, token::Comma, Attribute, Data, DataEnum, DataStruct, DeriveInput,
Field, Fields, Generics, Ident,
self, punctuated::Punctuated, token::Comma, Data, DataEnum, DataStruct, DeriveInput, Field,
Fields, Generics,
};
use crate::derives::{args, into_app, subcommand};
use crate::dummies;
use crate::item::Item;
use crate::item::Name;
pub fn derive_parser(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
@ -34,60 +35,70 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
..
}) => {
dummies::parser_struct(ident);
gen_for_struct(ident, &input.generics, &fields.named, &input.attrs)
let name = Name::Assigned(quote!(std::env::var("CARGO_PKG_NAME")
.ok()
.unwrap_or_default()));
let item = Item::from_args_struct(input, name);
gen_for_struct(&item, ident, &input.generics, &fields.named)
}
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => {
dummies::parser_struct(ident);
let name = Name::Assigned(quote!(std::env::var("CARGO_PKG_NAME")
.ok()
.unwrap_or_default()));
let item = Item::from_args_struct(input, name);
gen_for_struct(
&item,
ident,
&input.generics,
&Punctuated::<Field, Comma>::new(),
&input.attrs,
)
}
Data::Enum(ref e) => {
dummies::parser_enum(ident);
gen_for_enum(ident, &input.generics, &input.attrs, e)
let name = Name::Assigned(quote!(std::env::var("CARGO_PKG_NAME")
.ok()
.unwrap_or_default()));
let item = Item::from_subcommand_enum(input, name);
gen_for_enum(&item, ident, &input.generics, e)
}
_ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
}
}
fn gen_for_struct(
name: &Ident,
item: &Item,
item_name: &Ident,
generics: &Generics,
fields: &Punctuated<Field, Comma>,
attrs: &[Attribute],
) -> TokenStream {
let into_app = into_app::gen_for_struct(name, generics, attrs);
let args = args::gen_for_struct(name, generics, fields, attrs);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let into_app = into_app::gen_for_struct(item, item_name, generics);
let args = args::gen_for_struct(item, item_name, generics, fields);
quote! {
impl #impl_generics clap::Parser for #name #ty_generics #where_clause {}
impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
#into_app
#args
}
}
fn gen_for_enum(
name: &Ident,
generics: &Generics,
attrs: &[Attribute],
e: &DataEnum,
) -> TokenStream {
let into_app = into_app::gen_for_enum(name, generics, attrs);
let subcommand = subcommand::gen_for_enum(name, generics, attrs, e);
fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics, e: &DataEnum) -> TokenStream {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let into_app = into_app::gen_for_enum(item, item_name, generics);
let subcommand = subcommand::gen_for_enum(item, item_name, generics, e);
quote! {
impl #impl_generics clap::Parser for #name #ty_generics #where_clause {}
impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
#into_app
#subcommand

View file

@ -11,55 +11,50 @@
// This work was derived from Structopt (https://github.com/TeXitoi/structopt)
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use crate::{
derives::args,
dummies,
item::{Item, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
utils::{is_simple_ty, subty_if_name, Sp},
};
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, Attribute, Data, DataEnum, DeriveInput,
FieldsUnnamed, Generics, Token, Variant,
punctuated::Punctuated, spanned::Spanned, Data, DataEnum, DeriveInput, FieldsUnnamed, Generics,
Token, Variant,
};
use crate::derives::args;
use crate::dummies;
use crate::item::{Item, Kind, Name};
use crate::utils::{is_simple_ty, subty_if_name};
pub fn derive_subcommand(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
dummies::subcommand(ident);
match input.data {
Data::Enum(ref e) => gen_for_enum(ident, &input.generics, &input.attrs, e),
Data::Enum(ref e) => {
let name = Name::Derived(ident.clone());
let item = Item::from_subcommand_enum(input, name);
gen_for_enum(&item, ident, &input.generics, e)
}
_ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
}
}
pub fn gen_for_enum(
enum_name: &Ident,
item: &Item,
item_name: &Ident,
generics: &Generics,
attrs: &[Attribute],
e: &DataEnum,
) -> TokenStream {
let item = Item::from_subcommand_enum(
Span::call_site(),
attrs,
Name::Derived(enum_name.clone()),
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
);
let from_arg_matches = gen_from_arg_matches(enum_name, &e.variants, &item);
let update_from_arg_matches = gen_update_from_arg_matches(enum_name, &e.variants, &item);
let augmentation = gen_augment(&e.variants, &item, false);
let augmentation_update = gen_augment(&e.variants, &item, true);
let has_subcommand = gen_has_subcommand(&e.variants, &item);
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let from_arg_matches = gen_from_arg_matches(item_name, &e.variants, item);
let update_from_arg_matches = gen_update_from_arg_matches(item_name, &e.variants, item);
let augmentation = gen_augment(&e.variants, item, false);
let augmentation_update = gen_augment(&e.variants, item, true);
let has_subcommand = gen_has_subcommand(&e.variants, item);
quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
@ -74,7 +69,7 @@ pub fn gen_for_enum(
clippy::suspicious_else_formatting,
)]
#[deny(clippy::correctness)]
impl #impl_generics clap::FromArgMatches for #enum_name #ty_generics #where_clause {
impl #impl_generics clap::FromArgMatches for #item_name #ty_generics #where_clause {
fn from_arg_matches(__clap_arg_matches: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
Self::from_arg_matches_mut(&mut __clap_arg_matches.clone())
}
@ -100,7 +95,7 @@ pub fn gen_for_enum(
clippy::suspicious_else_formatting,
)]
#[deny(clippy::correctness)]
impl #impl_generics clap::Subcommand for #enum_name #ty_generics #where_clause {
impl #impl_generics clap::Subcommand for #item_name #ty_generics #where_clause {
fn augment_subcommands <'b>(__clap_app: clap::Command) -> clap::Command {
#augmentation
}

View file

@ -8,42 +8,35 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use crate::{
dummies,
item::{Item, Kind, Name, DEFAULT_CASING, DEFAULT_ENV_CASING},
utils::Sp,
};
use proc_macro2::{Span, TokenStream};
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, Attribute, Data, DataEnum, DeriveInput,
Fields, Ident, Variant,
punctuated::Punctuated, spanned::Spanned, token::Comma, Data, DataEnum, DeriveInput, Fields,
Ident, Variant,
};
use crate::dummies;
use crate::item::{Item, Kind, Name};
pub fn derive_value_enum(input: &DeriveInput) -> TokenStream {
let ident = &input.ident;
dummies::value_enum(ident);
match input.data {
Data::Enum(ref e) => gen_for_enum(ident, &input.attrs, e),
Data::Enum(ref e) => {
let name = Name::Derived(ident.clone());
let item = Item::from_value_enum(input, name);
gen_for_enum(&item, ident, e)
}
_ => abort_call_site!("`#[derive(ValueEnum)]` only supports enums"),
}
}
pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStream {
let item = Item::from_value_enum(
Span::call_site(),
attrs,
Name::Derived(name.clone()),
Sp::call_site(DEFAULT_CASING),
Sp::call_site(DEFAULT_ENV_CASING),
);
let lits = lits(&e.variants, &item);
pub fn gen_for_enum(item: &Item, item_name: &Ident, e: &DataEnum) -> TokenStream {
let lits = lits(&e.variants, item);
let value_variants = gen_value_variants(&lits);
let to_possible_value = gen_to_possible_value(&lits);
@ -61,7 +54,7 @@ pub fn gen_for_enum(name: &Ident, attrs: &[Attribute], e: &DataEnum) -> TokenStr
clippy::suspicious_else_formatting,
)]
#[deny(clippy::correctness)]
impl clap::ValueEnum for #name {
impl clap::ValueEnum for #item_name {
#value_variants
#to_possible_value
}

View file

@ -12,22 +12,21 @@
// commit#ea76fa1b1b273e65e3b0b1046643715b49bec51f which is licensed under the
// MIT/Apache 2.0 license.
use crate::{
attr::*,
utils::{inner_type, is_simple_ty, process_doc_comment, Sp, Ty},
};
use std::env;
use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
use proc_macro2::{self, Span, TokenStream};
use proc_macro_error::abort;
use quote::{quote, quote_spanned, ToTokens};
use syn::DeriveInput;
use syn::{
self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, MetaNameValue, Type,
Variant,
};
use crate::attr::*;
use crate::utils::{inner_type, is_simple_ty, process_doc_comment, Sp, Ty};
/// Default casing style for generated arguments.
pub const DEFAULT_CASING: CasingStyle = CasingStyle::Kebab;
@ -54,33 +53,27 @@ pub struct Item {
}
impl Item {
pub fn from_args_struct(
span: Span,
attrs: &[Attribute],
name: Name,
argument_casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
pub fn from_args_struct(input: &DeriveInput, name: Name) -> Self {
let span = Span::call_site();
let attrs = &input.attrs;
let argument_casing = Sp::call_site(DEFAULT_CASING);
let env_casing = Sp::call_site(DEFAULT_ENV_CASING);
Self::from_struct(span, attrs, name, argument_casing, env_casing)
}
pub fn from_subcommand_enum(
span: Span,
attrs: &[Attribute],
name: Name,
argument_casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Self {
let span = Span::call_site();
let attrs = &input.attrs;
let argument_casing = Sp::call_site(DEFAULT_CASING);
let env_casing = Sp::call_site(DEFAULT_ENV_CASING);
Self::from_struct(span, attrs, name, argument_casing, env_casing)
}
pub fn from_value_enum(
span: Span,
attrs: &[Attribute],
name: Name,
argument_casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
pub fn from_value_enum(input: &DeriveInput, name: Name) -> Self {
let span = Span::call_site();
let attrs = &input.attrs;
let argument_casing = Sp::call_site(DEFAULT_CASING);
let env_casing = Sp::call_site(DEFAULT_ENV_CASING);
Self::from_struct(span, attrs, name, argument_casing, env_casing)
}