Merge pull request #4777 from epage/error

perf(derive): Improve build times
This commit is contained in:
Ed Page 2023-03-23 12:11:14 -05:00 committed by GitHub
commit dbf1b9a8d7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
17 changed files with 718 additions and 636 deletions

68
Cargo.lock generated
View file

@ -242,10 +242,9 @@ name = "clap_derive"
version = "4.1.9"
dependencies = [
"heck",
"proc-macro-error",
"proc-macro2",
"quote",
"syn",
"syn 2.0.8",
]
[[package]]
@ -679,44 +678,20 @@ dependencies = [
"plotters-backend",
]
[[package]]
name = "proc-macro-error"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "da25490ff9892aab3fcf7c36f08cfb902dd3e71ca0f9f9517bea02a73a5ce38c"
dependencies = [
"proc-macro-error-attr",
"proc-macro2",
"quote",
"syn",
"version_check",
]
[[package]]
name = "proc-macro-error-attr"
version = "1.0.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a1be40180e52ecc98ad80b184934baf3d0d29f979574e439af5a55274b35f869"
dependencies = [
"proc-macro2",
"quote",
"version_check",
]
[[package]]
name = "proc-macro2"
version = "1.0.47"
version = "1.0.53"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5ea3d908b0e36316caf9e9e2c4625cdde190a7e6f440d794667ed17a1855e725"
checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73"
dependencies = [
"unicode-ident",
]
[[package]]
name = "quote"
version = "1.0.21"
version = "1.0.26"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bbe448f377a7d6961e30f5955f9b8d106c3f5e449d493ee1b125c1d43c2b5179"
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
dependencies = [
"proc-macro2",
]
@ -815,22 +790,22 @@ checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
[[package]]
name = "serde"
version = "1.0.147"
version = "1.0.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d193d69bae983fc11a79df82342761dfbf28a99fc8d203dca4c3c1b590948965"
checksum = "771d4d9c4163ee138805e12c710dd365e4f44be8be0503cb1bb9eb989425d9c9"
dependencies = [
"serde_derive",
]
[[package]]
name = "serde_derive"
version = "1.0.147"
version = "1.0.158"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4f1d362ca8fc9c3e3a7484440752472d68a6caa98f1ab81d99b5dfe517cec852"
checksum = "e801c1712f48475582b7696ac71e0ca34ebb30e09338425384269d9717c62cad"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 2.0.8",
]
[[package]]
@ -906,9 +881,20 @@ checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623"
[[package]]
name = "syn"
version = "1.0.103"
version = "1.0.109"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a864042229133ada95abf3b54fdc62ef5ccabe9515b64717bcb9a1919e59445d"
checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237"
dependencies = [
"proc-macro2",
"quote",
"unicode-ident",
]
[[package]]
name = "syn"
version = "2.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "bcc02725fd69ab9f26eab07fad303e2497fad6fb9eba4f96c4d1687bdf704ad9"
dependencies = [
"proc-macro2",
"quote",
@ -1055,9 +1041,9 @@ dependencies = [
[[package]]
name = "unicode-ident"
version = "1.0.5"
version = "1.0.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6ceab39d59e4c9499d4e5a8ee0e2735b891bb7308ac83dfb4e80cad195c9f6f3"
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
[[package]]
name = "unicode-width"
@ -1124,7 +1110,7 @@ dependencies = [
"once_cell",
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"wasm-bindgen-shared",
]
@ -1146,7 +1132,7 @@ checksum = "07bc0c051dc5f23e307b13285f9d75df86bfdf816c5721e573dec1f9b8aa193c"
dependencies = [
"proc-macro2",
"quote",
"syn",
"syn 1.0.109",
"wasm-bindgen-backend",
"wasm-bindgen-shared",
]

View file

@ -29,11 +29,10 @@ proc-macro = true
bench = false
[dependencies]
syn = { version = "1.0.74", features = ["full"] }
syn = { version = "2.0.8", features = ["full"] }
quote = "1.0.9"
proc-macro2 = "1.0.42"
heck = "0.4.0"
proc-macro-error = "1.0.4"
[features]
default = []

View file

@ -1,8 +1,6 @@
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 syn::spanned::Spanned;
@ -24,49 +22,44 @@ pub struct ClapAttr {
}
impl ClapAttr {
pub fn parse_all(all_attrs: &[Attribute]) -> Vec<Self> {
all_attrs
.iter()
.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 if attr.path.is_ident("command") {
Some(Sp::new(AttrKind::Command, attr.path.span()))
} else if attr.path.is_ident("group") {
Some(Sp::new(AttrKind::Group, attr.path.span()))
} else if attr.path.is_ident("arg") {
Some(Sp::new(AttrKind::Arg, attr.path.span()))
} else if attr.path.is_ident("value") {
Some(Sp::new(AttrKind::Value, 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()
pub fn parse_all(all_attrs: &[Attribute]) -> Result<Vec<Self>, syn::Error> {
let mut parsed = Vec::new();
for attr in all_attrs {
let kind = if attr.path().is_ident("clap") {
Sp::new(AttrKind::Clap, attr.path().span())
} else if attr.path().is_ident("structopt") {
Sp::new(AttrKind::StructOpt, attr.path().span())
} else if attr.path().is_ident("command") {
Sp::new(AttrKind::Command, attr.path().span())
} else if attr.path().is_ident("group") {
Sp::new(AttrKind::Group, attr.path().span())
} else if attr.path().is_ident("arg") {
Sp::new(AttrKind::Arg, attr.path().span())
} else if attr.path().is_ident("value") {
Sp::new(AttrKind::Value, attr.path().span())
} else {
continue;
};
for mut attr in
attr.parse_args_with(Punctuated::<ClapAttr, Token![,]>::parse_terminated)?
{
attr.kind = kind;
parsed.push(attr);
}
}
Ok(parsed)
}
pub fn value_or_abort(&self) -> &AttrValue {
pub fn value_or_abort(&self) -> Result<&AttrValue, syn::Error> {
self.value
.as_ref()
.unwrap_or_else(|| abort!(self.name, "attribute `{}` requires a value", self.name))
.ok_or_else(|| format_err!(self.name, "attribute `{}` requires a value", self.name))
}
pub fn lit_str_or_abort(&self) -> &LitStr {
let value = self.value_or_abort();
pub fn lit_str_or_abort(&self) -> Result<&LitStr, syn::Error> {
let value = self.value_or_abort()?;
match value {
AttrValue::LitStr(tokens) => tokens,
AttrValue::LitStr(tokens) => Ok(tokens),
AttrValue::Expr(_) | AttrValue::Call(_) => {
abort!(
self.name,
@ -133,7 +126,7 @@ impl Parse for ClapAttr {
let nested;
parenthesized!(nested in input);
let method_args: Punctuated<_, Token![,]> = nested.parse_terminated(Expr::parse)?;
let method_args: Punctuated<_, _> = nested.parse_terminated(Expr::parse, Token![,])?;
Some(AttrValue::Call(Vec::from_iter(method_args)))
} else {
None

View file

@ -13,7 +13,6 @@
// MIT/Apache 2.0 license.
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro_error::{abort, abort_call_site};
use quote::{format_ident, quote, quote_spanned};
use syn::ext::IdentExt;
use syn::{
@ -21,30 +20,27 @@ use syn::{
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 {
pub fn derive_args(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
let ident = &input.ident;
dummies::args(ident);
match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(ref fields),
..
}) => {
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
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
Ok((field, item))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, syn::Error>>()?;
gen_for_struct(&item, ident, &input.generics, &fields)
}
Data::Struct(DataStruct {
@ -52,15 +48,15 @@ pub fn derive_args(input: &DeriveInput) -> TokenStream {
..
}) => {
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 = fields
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
Ok((field, item))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, syn::Error>>()?;
gen_for_struct(&item, ident, &input.generics, &fields)
}
_ => abort_call_site!("`#[derive(Args)]` only supports non-tuple structs"),
@ -72,7 +68,7 @@ pub fn gen_for_struct(
item_name: &Ident,
generics: &Generics,
fields: &[(&Field, Item)],
) -> TokenStream {
) -> Result<TokenStream, syn::Error> {
if !matches!(&*item.kind(), Kind::Command(_)) {
abort! { item.kind().span(),
"`{}` cannot be used with `command`",
@ -82,13 +78,13 @@ pub fn gen_for_struct(
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let constructor = gen_constructor(fields);
let updater = gen_updater(fields, 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());
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)?;
let group_id = if item.skip_group() {
quote!(None)
@ -97,7 +93,7 @@ pub fn gen_for_struct(
quote!(Some(clap::Id::from(#group_id)))
};
quote! {
Ok(quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
clippy::style,
@ -157,7 +153,7 @@ pub fn gen_for_struct(
#augmentation_update
}
}
}
})
}
/// Generate a block of code to add arguments/subcommands corresponding to
@ -167,11 +163,12 @@ pub fn gen_augment(
app_var: &Ident,
parent_item: &Item,
override_required: bool,
) -> TokenStream {
) -> Result<TokenStream, syn::Error> {
let mut subcommand_specified = false;
let args = fields.iter().filter_map(|(field, item)| {
let mut args = Vec::new();
for (field, item) in fields {
let kind = item.kind();
match &*kind {
let genned = match &*kind {
Kind::Command(_)
| Kind::Value
| Kind::Skip(_, _)
@ -179,7 +176,10 @@ pub fn gen_augment(
| Kind::ExternalSubcommand => None,
Kind::Subcommand(ty) => {
if subcommand_specified {
abort!(field.span(), "`#[command(subcommand)]` can only be used once per container");
abort!(
field.span(),
"`#[command(subcommand)]` can only be used once per container"
);
}
subcommand_specified = true;
@ -354,8 +354,9 @@ pub fn gen_augment(
});
})
}
}
});
};
args.push(genned);
}
let deprecations = if !override_required {
parent_item.deprecations()
@ -408,7 +409,7 @@ pub fn gen_augment(
)
)
};
quote! {{
Ok(quote! {{
#deprecations
let #app_var = #app_var
#initial_app_methods
@ -416,15 +417,15 @@ pub fn gen_augment(
;
#( #args )*
#app_var #final_app_methods
}}
}})
}
pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
pub fn gen_constructor(fields: &[(&Field, Item)]) -> Result<TokenStream, syn::Error> {
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");
match &*kind {
let genned = match &*kind {
Kind::Command(_)
| Kind::Value
| Kind::ExternalSubcommand => {
@ -519,18 +520,20 @@ pub fn gen_constructor(fields: &[(&Field, Item)]) -> TokenStream {
},
Kind::Arg(ty) | Kind::FromGlobal(ty) => {
gen_parsers(item, ty, field_name, field, None)
gen_parsers(item, ty, field_name, field, None)?
}
}
});
};
Ok(genned)
}).collect::<Result<Vec<_>, syn::Error>>()?;
quote! {{
Ok(quote! {{
#( #fields ),*
}}
}})
}
pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
let fields = fields.iter().map(|(field, item)| {
pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> Result<TokenStream, syn::Error> {
let mut genned_fields = Vec::new();
for (field, item) in fields {
let field_name = field.ident.as_ref().unwrap();
let kind = item.kind();
@ -544,10 +547,8 @@ pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
};
let arg_matches = format_ident!("__clap_arg_matches");
match &*kind {
Kind::Command(_)
| Kind::Value
| Kind::ExternalSubcommand => {
let genned = match &*kind {
Kind::Command(_) | Kind::Value | Kind::ExternalSubcommand => {
abort! { kind.span(),
"`{}` cannot be used with `arg`",
kind.name(),
@ -617,17 +618,20 @@ pub fn gen_updater(fields: &[(&Field, Item)], use_self: bool) -> TokenStream {
#updater
}
}
},
}
Kind::Skip(_, _) => quote!(),
Kind::Arg(ty) | Kind::FromGlobal(ty) => gen_parsers(item, ty, field_name, field, Some(&access)),
}
});
quote! {
#( #fields )*
Kind::Arg(ty) | Kind::FromGlobal(ty) => {
gen_parsers(item, ty, field_name, field, Some(&access))?
}
};
genned_fields.push(genned);
}
Ok(quote! {
#( #genned_fields )*
})
}
fn gen_parsers(
@ -636,7 +640,7 @@ fn gen_parsers(
field_name: &Ident,
field: &Field,
update: Option<&TokenStream>,
) -> TokenStream {
) -> Result<TokenStream, syn::Error> {
let span = ty.span();
let convert_type = inner_type(&field.ty);
let id = item.id();
@ -709,7 +713,7 @@ fn gen_parsers(
}
};
if let Some(access) = update {
let genned = if let Some(access) = update {
quote_spanned! { field.span()=>
if #arg_matches.contains_id(#id) {
#access
@ -718,7 +722,8 @@ fn gen_parsers(
}
} else {
quote_spanned!(field.span()=> #field_name: #field_value )
}
};
Ok(genned)
}
#[cfg(feature = "raw-deprecated")]

View file

@ -18,7 +18,11 @@ use syn::{Generics, Ident};
use crate::item::Item;
pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream {
pub fn gen_for_struct(
item: &Item,
item_name: &Ident,
generics: &Generics,
) -> Result<TokenStream, syn::Error> {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let name = item.cased_name();
@ -51,16 +55,20 @@ pub fn gen_for_struct(item: &Item, item_name: &Ident, generics: &Generics) -> To
}
};
tokens
Ok(tokens)
}
pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> TokenStream {
pub fn gen_for_enum(
item: &Item,
item_name: &Ident,
generics: &Generics,
) -> Result<TokenStream, syn::Error> {
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let name = item.cased_name();
let app_var = Ident::new("__clap_app", Span::call_site());
quote! {
Ok(quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
clippy::style,
@ -89,5 +97,5 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, generics: &Generics) -> Toke
.arg_required_else_help(false)
}
}
}
})
}

View file

@ -13,7 +13,6 @@
// MIT/Apache 2.0 license.
use proc_macro2::TokenStream;
use proc_macro_error::abort_call_site;
use quote::quote;
use syn::Ident;
use syn::Variant;
@ -23,11 +22,10 @@ use syn::{
};
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 {
pub fn derive_parser(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
let ident = &input.ident;
let pkg_name = std::env::var("CARGO_PKG_NAME").ok().unwrap_or_default();
@ -36,52 +34,46 @@ pub fn derive_parser(input: &DeriveInput) -> TokenStream {
fields: Fields::Named(ref fields),
..
}) => {
dummies::parser_struct(ident);
let name = Name::Assigned(quote!(#pkg_name));
let item = Item::from_args_struct(input, name);
let item = Item::from_args_struct(input, name)?;
let fields = fields
.named
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
Ok((field, item))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, syn::Error>>()?;
gen_for_struct(&item, ident, &input.generics, &fields)
}
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => {
dummies::parser_struct(ident);
let name = Name::Assigned(quote!(#pkg_name));
let item = Item::from_args_struct(input, name);
let item = Item::from_args_struct(input, name)?;
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)
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
Ok((field, item))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, syn::Error>>()?;
gen_for_struct(&item, ident, &input.generics, &fields)
}
Data::Enum(ref e) => {
dummies::parser_enum(ident);
let name = Name::Assigned(quote!(#pkg_name));
let item = Item::from_subcommand_enum(input, name);
let item = Item::from_subcommand_enum(input, name)?;
let variants = e
.variants
.iter()
.map(|variant| {
let item =
Item::from_subcommand_variant(variant, item.casing(), item.env_casing());
(variant, item)
Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?;
Ok((variant, item))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, syn::Error>>()?;
gen_for_enum(&item, ident, &input.generics, &variants)
}
_ => abort_call_site!("`#[derive(Parser)]` only supports non-tuple structs and enums"),
@ -93,18 +85,18 @@ fn gen_for_struct(
item_name: &Ident,
generics: &Generics,
fields: &[(&Field, Item)],
) -> TokenStream {
) -> Result<TokenStream, syn::Error> {
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);
let into_app = into_app::gen_for_struct(item, item_name, generics)?;
let args = args::gen_for_struct(item, item_name, generics, fields)?;
quote! {
Ok(quote! {
impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
#into_app
#args
}
})
}
fn gen_for_enum(
@ -112,16 +104,16 @@ fn gen_for_enum(
item_name: &Ident,
generics: &Generics,
variants: &[(&Variant, Item)],
) -> TokenStream {
) -> Result<TokenStream, syn::Error> {
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, variants);
let into_app = into_app::gen_for_enum(item, item_name, generics)?;
let subcommand = subcommand::gen_for_enum(item, item_name, generics, variants)?;
quote! {
Ok(quote! {
impl #impl_generics clap::Parser for #item_name #ty_generics #where_clause {}
#into_app
#subcommand
}
})
}

View file

@ -13,33 +13,29 @@
// MIT/Apache 2.0 license.
use proc_macro2::{Ident, Span, TokenStream};
use proc_macro_error::{abort, abort_call_site};
use quote::{format_ident, quote, quote_spanned};
use syn::{spanned::Spanned, Data, DeriveInput, FieldsUnnamed, Generics, 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 {
pub fn derive_subcommand(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
let ident = &input.ident;
dummies::subcommand(ident);
match input.data {
Data::Enum(ref e) => {
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
.iter()
.map(|variant| {
let item =
Item::from_subcommand_variant(variant, item.casing(), item.env_casing());
(variant, item)
Item::from_subcommand_variant(variant, item.casing(), item.env_casing())?;
Ok((variant, item))
})
.collect::<Vec<_>>();
.collect::<Result<Vec<_>, syn::Error>>()?;
gen_for_enum(&item, ident, &input.generics, &variants)
}
_ => abort_call_site!("`#[derive(Subcommand)]` only supports enums"),
@ -51,7 +47,7 @@ pub fn gen_for_enum(
item_name: &Ident,
generics: &Generics,
variants: &[(&Variant, Item)],
) -> TokenStream {
) -> Result<TokenStream, syn::Error> {
if !matches!(&*item.kind(), Kind::Command(_)) {
abort! { item.kind().span(),
"`{}` cannot be used with `command`",
@ -61,14 +57,14 @@ pub fn gen_for_enum(
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
let from_arg_matches = gen_from_arg_matches(variants);
let update_from_arg_matches = gen_update_from_arg_matches(variants);
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);
let augmentation = gen_augment(variants, item, false)?;
let augmentation_update = gen_augment(variants, item, true)?;
let has_subcommand = gen_has_subcommand(variants)?;
quote! {
Ok(quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
clippy::style,
@ -119,225 +115,222 @@ pub fn gen_for_enum(
#has_subcommand
}
}
}
})
}
fn gen_augment(
variants: &[(&Variant, Item)],
parent_item: &Item,
override_required: bool,
) -> TokenStream {
) -> Result<TokenStream, syn::Error> {
use syn::Fields::*;
let app_var = Ident::new("__clap_app", Span::call_site());
let subcommands: Vec<_> = variants
.iter()
.filter_map(|(variant, item)| {
let kind = item.kind();
let mut subcommands = Vec::new();
for (variant, item) in variants {
let kind = item.kind();
match &*kind {
Kind::Skip(_, _) |
Kind::Arg(_) |
Kind::FromGlobal(_) |
Kind::Value => None,
let genned = match &*kind {
Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
Kind::ExternalSubcommand => {
let ty = match variant.fields {
Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
Kind::ExternalSubcommand => {
let ty = match variant.fields {
Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
_ => abort!(
variant,
"The enum variant marked with `external_subcommand` must be \
a single-typed tuple, and the type must be either `Vec<String>` \
or `Vec<OsString>`."
),
};
let deprecations = if !override_required {
item.deprecations()
} else {
quote!()
};
let subty = subty_if_name(ty, "Vec").unwrap_or_else(|| {
abort!(
ty.span(),
"The type must be `Vec<_>` \
to be used with `external_subcommand`."
)
});
let subcommand = quote_spanned! { kind.span()=>
#deprecations
let #app_var = #app_var
.external_subcommand_value_parser(clap::value_parser!(#subty));
};
Some(subcommand)
}
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 next_help_heading = item.next_help_heading();
let next_display_order = item.next_display_order();
let subcommand = if override_required {
quote! {
#deprecations
let #app_var = #app_var
#next_help_heading
#next_display_order;
let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
}
} else {
quote! {
#deprecations
let #app_var = #app_var
#next_help_heading
#next_display_order;
let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
}
};
Some(subcommand)
}
_ => abort!(
variant,
"`flatten` is usable only with single-typed tuple variants"
"The enum variant marked with `external_subcommand` must be \
a single-typed tuple, and the type must be either `Vec<String>` \
or `Vec<OsString>`."
),
},
};
let deprecations = if !override_required {
item.deprecations()
} else {
quote!()
};
let subty = subty_if_name(ty, "Vec").ok_or_else(|| {
format_err!(
ty.span(),
"The type must be `Vec<_>` \
to be used with `external_subcommand`."
)
})?;
let subcommand = quote_spanned! { kind.span()=>
#deprecations
let #app_var = #app_var
.external_subcommand_value_parser(clap::value_parser!(#subty));
};
Some(subcommand)
}
Kind::Subcommand(_) => {
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
let arg_block = match variant.fields {
Named(_) => {
abort!(variant, "non single-typed tuple enums are not supported")
}
Unit => quote!( #subcommand_var ),
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
let ty = &unnamed[0];
if override_required {
quote_spanned! { ty.span()=>
{
<#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
}
}
} else {
quote_spanned! { ty.span()=>
{
<#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
}
}
}
}
Unnamed(..) => {
abort!(variant, "non single-typed tuple enums are not supported")
}
};
let name = item.cased_name();
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 initial_app_methods = item.initial_top_level_methods();
let final_from_attrs = item.final_top_level_methods();
let override_methods = if override_required {
quote_spanned! { kind.span()=>
.subcommand_required(false)
.arg_required_else_help(false)
let next_help_heading = item.next_help_heading();
let next_display_order = item.next_display_order();
let subcommand = if override_required {
quote! {
#deprecations
let #app_var = #app_var
#next_help_heading
#next_display_order;
let #app_var = <#ty as clap::Subcommand>::augment_subcommands_for_update(#app_var);
}
} else {
quote!()
quote! {
#deprecations
let #app_var = #app_var
#next_help_heading
#next_display_order;
let #app_var = <#ty as clap::Subcommand>::augment_subcommands(#app_var);
}
};
let subcommand = quote! {
let #app_var = #app_var.subcommand({
#deprecations;
let #subcommand_var = clap::Command::new(#name);
let #subcommand_var = #subcommand_var
.subcommand_required(true)
.arg_required_else_help(true);
Some(subcommand)
}
_ => abort!(
variant,
"`flatten` is usable only with single-typed tuple variants"
),
},
Kind::Subcommand(_) => {
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
let arg_block = match variant.fields {
Named(_) => {
abort!(variant, "non single-typed tuple enums are not supported")
}
Unit => quote!( #subcommand_var ),
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
let ty = &unnamed[0];
if override_required {
quote_spanned! { ty.span()=>
{
<#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_var)
}
}
} else {
quote_spanned! { ty.span()=>
{
<#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
}
}
}
}
Unnamed(..) => {
abort!(variant, "non single-typed tuple enums are not supported")
}
};
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 override_methods = if override_required {
quote_spanned! { kind.span()=>
.subcommand_required(false)
.arg_required_else_help(false)
}
} else {
quote!()
};
let subcommand = quote! {
let #app_var = #app_var.subcommand({
#deprecations;
let #subcommand_var = clap::Command::new(#name);
let #subcommand_var = #subcommand_var
.subcommand_required(true)
.arg_required_else_help(true);
let #subcommand_var = #subcommand_var #initial_app_methods;
let #subcommand_var = #arg_block;
#subcommand_var #final_from_attrs #override_methods
});
};
Some(subcommand)
}
Kind::Command(_) => {
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
let sub_augment = match variant.fields {
Named(ref fields) => {
// Defer to `gen_augment` for adding cmd methods
let fields = fields
.named
.iter()
.map(|field| {
let item =
Item::from_args_field(field, item.casing(), item.env_casing())?;
Ok((field, item))
})
.collect::<Result<Vec<_>, syn::Error>>()?;
args::gen_augment(&fields, &subcommand_var, item, override_required)?
}
Unit => {
let arg_block = quote!( #subcommand_var );
let initial_app_methods = item.initial_top_level_methods();
let final_from_attrs = item.final_top_level_methods();
quote! {
let #subcommand_var = #subcommand_var #initial_app_methods;
let #subcommand_var = #arg_block;
#subcommand_var #final_from_attrs #override_methods
});
};
Some(subcommand)
}
Kind::Command(_) => {
let subcommand_var = Ident::new("__clap_subcommand", Span::call_site());
let sub_augment = match variant.fields {
Named(ref fields) => {
// Defer to `gen_augment` for adding cmd methods
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)
#subcommand_var #final_from_attrs
}
Unit => {
let arg_block = quote!( #subcommand_var );
let initial_app_methods = item.initial_top_level_methods();
let final_from_attrs = item.final_top_level_methods();
quote! {
let #subcommand_var = #subcommand_var #initial_app_methods;
let #subcommand_var = #arg_block;
#subcommand_var #final_from_attrs
}
},
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
let ty = &unnamed[0];
let arg_block = if override_required {
quote_spanned! { ty.span()=>
{
<#ty as clap::Args>::augment_args_for_update(#subcommand_var)
}
}
Unnamed(FieldsUnnamed { ref unnamed, .. }) if unnamed.len() == 1 => {
let ty = &unnamed[0];
let arg_block = if override_required {
quote_spanned! { ty.span()=>
{
<#ty as clap::Args>::augment_args_for_update(#subcommand_var)
}
} else {
quote_spanned! { ty.span()=>
{
<#ty as clap::Args>::augment_args(#subcommand_var)
}
}
};
let initial_app_methods = item.initial_top_level_methods();
let final_from_attrs = item.final_top_level_methods();
quote! {
let #subcommand_var = #subcommand_var #initial_app_methods;
let #subcommand_var = #arg_block;
#subcommand_var #final_from_attrs
}
} else {
quote_spanned! { ty.span()=>
{
<#ty as clap::Args>::augment_args(#subcommand_var)
}
}
};
let initial_app_methods = item.initial_top_level_methods();
let final_from_attrs = item.final_top_level_methods();
quote! {
let #subcommand_var = #subcommand_var #initial_app_methods;
let #subcommand_var = #arg_block;
#subcommand_var #final_from_attrs
}
Unnamed(..) => {
abort!(variant, "non single-typed tuple enums are not supported")
}
};
}
Unnamed(..) => {
abort!(variant, "non single-typed tuple enums are not supported")
}
};
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
});
};
Some(subcommand)
}
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
});
};
Some(subcommand)
}
})
.collect();
};
subcommands.push(genned);
}
let deprecations = if !override_required {
parent_item.deprecations()
@ -346,15 +339,15 @@ fn gen_augment(
};
let initial_app_methods = parent_item.initial_top_level_methods();
let final_app_methods = parent_item.final_top_level_methods();
quote! {
Ok(quote! {
#deprecations;
let #app_var = #app_var #initial_app_methods;
#( #subcommands )*;
#app_var #final_app_methods
}
})
}
fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
use syn::Fields::*;
let mut ext_subcmd = false;
@ -391,19 +384,20 @@ fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
.map(|(variant, _attrs)| match variant.fields {
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
let ty = &fields.unnamed[0];
quote! {
Ok(quote! {
if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
return true;
}
}
})
}
_ => abort!(
variant,
"`flatten` is usable only with single-typed tuple variants"
),
});
})
.collect::<Result<Vec<_>, syn::Error>>()?;
if ext_subcmd {
let genned = if ext_subcmd {
quote! { true }
} else {
quote! {
@ -413,77 +407,79 @@ fn gen_has_subcommand(variants: &[(&Variant, Item)]) -> TokenStream {
false
}
}
};
Ok(genned)
}
fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
use syn::Fields::*;
let mut ext_subcmd = None;
let subcommand_name_var = format_ident!("__clap_name");
let sub_arg_matches_var = format_ident!("__clap_arg_matches");
let (flatten_variants, variants): (Vec<_>, Vec<_>) = variants
.iter()
.filter_map(|(variant, item)| {
let kind = item.kind();
match &*kind {
Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => None,
Kind::ExternalSubcommand => {
if ext_subcmd.is_some() {
abort!(
item.kind().span(),
"Only one variant can be marked with `external_subcommand`, \
let mut ext_subcmd = None;
let mut flatten_variants = Vec::new();
let mut unflatten_variants = Vec::new();
for (variant, item) in variants {
let kind = item.kind();
match &*kind {
Kind::Skip(_, _) | Kind::Arg(_) | Kind::FromGlobal(_) | Kind::Value => {}
Kind::ExternalSubcommand => {
if ext_subcmd.is_some() {
abort!(
item.kind().span(),
"Only one variant can be marked with `external_subcommand`, \
this is the second"
);
}
);
}
let ty = match variant.fields {
Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
let ty = match variant.fields {
Unnamed(ref fields) if fields.unnamed.len() == 1 => &fields.unnamed[0].ty,
_ => abort!(
variant,
"The enum variant marked with `external_subcommand` must be \
_ => abort!(
variant,
"The enum variant marked with `external_subcommand` must be \
a single-typed tuple, and the type must be either `Vec<String>` \
or `Vec<OsString>`."
),
};
),
};
let (span, str_ty) = match subty_if_name(ty, "Vec") {
Some(subty) => {
if is_simple_ty(subty, "String") {
(subty.span(), quote!(::std::string::String))
} else if is_simple_ty(subty, "OsString") {
(subty.span(), quote!(::std::ffi::OsString))
} else {
abort!(
ty.span(),
"The type must be either `Vec<String>` or `Vec<OsString>` \
let (span, str_ty) = match subty_if_name(ty, "Vec") {
Some(subty) => {
if is_simple_ty(subty, "String") {
(subty.span(), quote!(::std::string::String))
} else if is_simple_ty(subty, "OsString") {
(subty.span(), quote!(::std::ffi::OsString))
} else {
abort!(
ty.span(),
"The type must be either `Vec<String>` or `Vec<OsString>` \
to be used with `external_subcommand`."
);
}
);
}
}
None => abort!(
ty.span(),
"The type must be either `Vec<String>` or `Vec<OsString>` \
None => abort!(
ty.span(),
"The type must be either `Vec<String>` or `Vec<OsString>` \
to be used with `external_subcommand`."
),
};
),
};
ext_subcmd = Some((span, &variant.ident, str_ty));
None
}
Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
ext_subcmd = Some((span, &variant.ident, str_ty));
}
})
.partition(|(_, item)| {
let kind = item.kind();
matches!(&*kind, Kind::Flatten(_))
});
Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => {
if matches!(&*item.kind(), Kind::Flatten(_)) {
flatten_variants.push((variant, item));
} else {
unflatten_variants.push((variant, item));
}
}
}
}
let subcommands = variants.iter().map(|(variant, item)| {
let subcommands = unflatten_variants.iter().map(|(variant, item)| {
let sub_name = item.cased_name();
let variant_name = &variant.ident;
let constructor_block = match variant.fields {
@ -492,11 +488,11 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
.named
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
Ok((field, item))
})
.collect::<Vec<_>>();
args::gen_constructor(&fields)
.collect::<Result<Vec<_>, syn::Error>>()?;
args::gen_constructor(&fields)?
},
Unit => quote!(),
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
@ -506,18 +502,18 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
Unnamed(..) => abort_call_site!("{}: tuple enums are not supported", variant.ident),
};
quote! {
Ok(quote! {
if #subcommand_name_var == #sub_name && !#sub_arg_matches_var.contains_id("") {
return ::std::result::Result::Ok(Self :: #variant_name #constructor_block)
}
}
});
})
}).collect::<Result<Vec<_>, syn::Error>>()?;
let child_subcommands = flatten_variants.iter().map(|(variant, _attrs)| {
let variant_name = &variant.ident;
match variant.fields {
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
let ty = &fields.unnamed[0];
quote! {
Ok(quote! {
if __clap_arg_matches
.subcommand_name()
.map(|__clap_name| <#ty as clap::Subcommand>::has_subcommand(__clap_name))
@ -526,14 +522,14 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
let __clap_res = <#ty as clap::FromArgMatches>::from_arg_matches_mut(__clap_arg_matches)?;
return ::std::result::Result::Ok(Self :: #variant_name (__clap_res));
}
}
})
}
_ => abort!(
variant,
"`flatten` is usable only with single-typed tuple variants"
),
}
});
}).collect::<Result<Vec<_>, syn::Error>>()?;
let wildcard = match ext_subcmd {
Some((span, var_name, str_ty)) => quote_spanned! { span=>
@ -555,7 +551,7 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
};
let raw_deprecated = args::raw_deprecated();
quote! {
Ok(quote! {
fn from_arg_matches_mut(__clap_arg_matches: &mut clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
#raw_deprecated
@ -570,10 +566,10 @@ fn gen_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
::std::result::Result::Err(clap::Error::raw(clap::error::ErrorKind::MissingSubcommand, "A subcommand is required but one was not provided."))
}
}
}
})
}
fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> Result<TokenStream, syn::Error> {
use syn::Fields::*;
let (flatten, variants): (Vec<_>, Vec<_>) = variants
@ -607,11 +603,11 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
.named
.iter()
.map(|field| {
let item = Item::from_args_field(field, item.casing(), item.env_casing());
(field, item)
let item = Item::from_args_field(field, item.casing(), item.env_casing())?;
Ok((field, item))
})
.collect::<Vec<_>>();
let update = args::gen_updater(&fields, false);
.collect::<Result<Vec<_>, syn::Error>>()?;
let update = args::gen_updater(&fields, false)?;
(quote!( { #( #field_names, )* }), quote!( { #update } ))
}
Unit => (quote!(), quote!({})),
@ -630,38 +626,38 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
}
};
quote! {
Ok(quote! {
Self :: #variant_name #pattern if #sub_name == __clap_name => {
let (_, mut __clap_arg_sub_matches) = __clap_arg_matches.remove_subcommand().unwrap();
let __clap_arg_matches = &mut __clap_arg_sub_matches;
#updater
}
}
});
})
}).collect::<Result<Vec<_>, _>>()?;
let child_subcommands = flatten.iter().map(|(variant, _attrs)| {
let variant_name = &variant.ident;
match variant.fields {
Unnamed(ref fields) if fields.unnamed.len() == 1 => {
let ty = &fields.unnamed[0];
quote! {
Ok(quote! {
if <#ty as clap::Subcommand>::has_subcommand(__clap_name) {
if let Self :: #variant_name (child) = s {
<#ty as clap::FromArgMatches>::update_from_arg_matches_mut(child, __clap_arg_matches)?;
return ::std::result::Result::Ok(());
}
}
}
})
}
_ => abort!(
variant,
"`flatten` is usable only with single-typed tuple variants"
),
}
});
}).collect::<Result<Vec<_>, _>>()?;
let raw_deprecated = args::raw_deprecated();
quote! {
Ok(quote! {
fn update_from_arg_matches_mut<'b>(
&mut self,
__clap_arg_matches: &mut clap::ArgMatches,
@ -679,5 +675,5 @@ fn gen_update_from_arg_matches(variants: &[(&Variant, Item)]) -> TokenStream {
}
::std::result::Result::Ok(())
}
}
})
}

View file

@ -9,39 +9,36 @@
// except according to those terms.
use proc_macro2::TokenStream;
use proc_macro_error::{abort, abort_call_site};
use quote::quote;
use quote::quote_spanned;
use syn::{spanned::Spanned, Data, DeriveInput, Fields, Ident, Variant};
use crate::dummies;
use crate::item::{Item, Kind, Name};
pub fn derive_value_enum(input: &DeriveInput) -> TokenStream {
pub fn derive_value_enum(input: &DeriveInput) -> Result<TokenStream, syn::Error> {
let ident = &input.ident;
dummies::value_enum(ident);
match input.data {
Data::Enum(ref e) => {
let name = Name::Derived(ident.clone());
let item = Item::from_value_enum(input, name);
let variants = e
.variants
.iter()
.map(|variant| {
let item =
Item::from_value_enum_variant(variant, item.casing(), item.env_casing());
(variant, item)
})
.collect::<Vec<_>>();
let item = Item::from_value_enum(input, name)?;
let mut variants = Vec::new();
for variant in &e.variants {
let item =
Item::from_value_enum_variant(variant, item.casing(), item.env_casing())?;
variants.push((variant, item));
}
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: &[(&Variant, Item)]) -> TokenStream {
pub fn gen_for_enum(
item: &Item,
item_name: &Ident,
variants: &[(&Variant, Item)],
) -> Result<TokenStream, syn::Error> {
if !matches!(&*item.kind(), Kind::Value) {
abort! { item.kind().span(),
"`{}` cannot be used with `value`",
@ -49,11 +46,11 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item)
}
}
let lits = lits(variants);
let lits = lits(variants)?;
let value_variants = gen_value_variants(&lits);
let to_possible_value = gen_to_possible_value(item, &lits);
quote! {
Ok(quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
clippy::style,
@ -71,33 +68,31 @@ pub fn gen_for_enum(item: &Item, item_name: &Ident, variants: &[(&Variant, Item)
#value_variants
#to_possible_value
}
}
})
}
fn lits(variants: &[(&Variant, Item)]) -> Vec<(TokenStream, Ident)> {
variants
.iter()
.filter_map(|(variant, item)| {
if let Kind::Skip(_, _) = &*item.kind() {
None
} else {
if !matches!(variant.fields, Fields::Unit) {
abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
}
let fields = item.field_methods();
let deprecations = item.deprecations();
let name = item.cased_name();
Some((
quote_spanned! { variant.span()=> {
#deprecations
clap::builder::PossibleValue::new(#name)
#fields
}},
variant.ident.clone(),
))
}
})
.collect::<Vec<_>>()
fn lits(variants: &[(&Variant, Item)]) -> Result<Vec<(TokenStream, Ident)>, syn::Error> {
let mut genned = Vec::new();
for (variant, item) in variants {
if let Kind::Skip(_, _) = &*item.kind() {
continue;
}
if !matches!(variant.fields, Fields::Unit) {
abort!(variant.span(), "`#[derive(ValueEnum)]` only supports unit variants. Non-unit variants must be skipped");
}
let fields = item.field_methods();
let deprecations = item.deprecations();
let name = item.cased_name();
genned.push((
quote_spanned! { variant.span()=> {
#deprecations
clap::builder::PossibleValue::new(#name)
#fields
}},
variant.ident.clone(),
));
}
Ok(genned)
}
fn gen_value_variants(lits: &[(TokenStream, Ident)]) -> TokenStream {

View file

@ -1,23 +1,20 @@
//! Dummy implementations that we emit along with an error.
use proc_macro2::Ident;
use proc_macro_error::append_dummy;
use quote::quote;
pub fn parser_struct(name: &Ident) {
into_app(name);
args(name);
append_dummy(quote!( impl clap::Parser for #name {} ));
#[must_use]
pub fn parser(name: &Ident) -> proc_macro2::TokenStream {
let into_app = into_app(name);
quote!(
impl clap::Parser for #name {}
#into_app
)
}
pub fn parser_enum(name: &Ident) {
into_app(name);
subcommand(name);
append_dummy(quote!( impl clap::Parser for #name {} ));
}
pub fn into_app(name: &Ident) {
append_dummy(quote! {
#[must_use]
pub fn into_app(name: &Ident) -> proc_macro2::TokenStream {
quote! {
impl clap::CommandFactory for #name {
fn command<'b>() -> clap::Command {
unimplemented!()
@ -26,11 +23,12 @@ pub fn into_app(name: &Ident) {
unimplemented!()
}
}
});
}
}
pub fn from_arg_matches(name: &Ident) {
append_dummy(quote! {
#[must_use]
pub fn from_arg_matches(name: &Ident) -> proc_macro2::TokenStream {
quote! {
impl clap::FromArgMatches for #name {
fn from_arg_matches(_m: &clap::ArgMatches) -> ::std::result::Result<Self, clap::Error> {
unimplemented!()
@ -39,12 +37,13 @@ pub fn from_arg_matches(name: &Ident) {
unimplemented!()
}
}
});
}
}
pub fn subcommand(name: &Ident) {
from_arg_matches(name);
append_dummy(quote! {
#[must_use]
pub fn subcommand(name: &Ident) -> proc_macro2::TokenStream {
let from_arg_matches = from_arg_matches(name);
quote! {
impl clap::Subcommand for #name {
fn augment_subcommands(_cmd: clap::Command) -> clap::Command {
unimplemented!()
@ -56,12 +55,14 @@ pub fn subcommand(name: &Ident) {
unimplemented!()
}
}
});
#from_arg_matches
}
}
pub fn args(name: &Ident) {
from_arg_matches(name);
append_dummy(quote! {
#[must_use]
pub fn args(name: &Ident) -> proc_macro2::TokenStream {
let from_arg_matches = from_arg_matches(name);
quote! {
impl clap::Args for #name {
fn augment_args(_cmd: clap::Command) -> clap::Command {
unimplemented!()
@ -70,11 +71,13 @@ pub fn args(name: &Ident) {
unimplemented!()
}
}
});
#from_arg_matches
}
}
pub fn value_enum(name: &Ident) {
append_dummy(quote! {
#[must_use]
pub fn value_enum(name: &Ident) -> proc_macro2::TokenStream {
quote! {
impl clap::ValueEnum for #name {
fn value_variants<'a>() -> &'a [Self]{
unimplemented!()
@ -86,5 +89,5 @@ pub fn value_enum(name: &Ident) {
unimplemented!()
}
}
})
}
}

View file

@ -16,7 +16,6 @@ use std::env;
use heck::{ToKebabCase, ToLowerCamelCase, ToShoutySnakeCase, ToSnakeCase, ToUpperCamelCase};
use proc_macro2::{self, Span, TokenStream};
use proc_macro_error::abort;
use quote::{format_ident, quote, quote_spanned, ToTokens};
use syn::DeriveInput;
use syn::{self, ext::IdentExt, spanned::Spanned, Attribute, Field, Ident, LitStr, Type, Variant};
@ -53,7 +52,7 @@ pub struct Item {
}
impl Item {
pub fn from_args_struct(input: &DeriveInput, name: Name) -> Self {
pub fn from_args_struct(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
let ident = input.ident.clone();
let span = input.ident.span();
let attrs = &input.attrs;
@ -62,15 +61,15 @@ impl Item {
let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
let parsed_attrs = ClapAttr::parse_all(attrs);
res.infer_kind(&parsed_attrs);
res.push_attrs(&parsed_attrs);
let parsed_attrs = ClapAttr::parse_all(attrs)?;
res.infer_kind(&parsed_attrs)?;
res.push_attrs(&parsed_attrs)?;
res.push_doc_comment(attrs, "about", Some("long_about"));
res
Ok(res)
}
pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Self {
pub fn from_subcommand_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
let ident = input.ident.clone();
let span = input.ident.span();
let attrs = &input.attrs;
@ -79,15 +78,15 @@ impl Item {
let kind = Sp::new(Kind::Command(Sp::new(Ty::Other, span)), span);
let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
let parsed_attrs = ClapAttr::parse_all(attrs);
res.infer_kind(&parsed_attrs);
res.push_attrs(&parsed_attrs);
let parsed_attrs = ClapAttr::parse_all(attrs)?;
res.infer_kind(&parsed_attrs)?;
res.push_attrs(&parsed_attrs)?;
res.push_doc_comment(attrs, "about", Some("long_about"));
res
Ok(res)
}
pub fn from_value_enum(input: &DeriveInput, name: Name) -> Self {
pub fn from_value_enum(input: &DeriveInput, name: Name) -> Result<Self, syn::Error> {
let ident = input.ident.clone();
let span = input.ident.span();
let attrs = &input.attrs;
@ -96,9 +95,9 @@ impl Item {
let kind = Sp::new(Kind::Value, span);
let mut res = Self::new(name, ident, None, argument_casing, env_casing, kind);
let parsed_attrs = ClapAttr::parse_all(attrs);
res.infer_kind(&parsed_attrs);
res.push_attrs(&parsed_attrs);
let parsed_attrs = ClapAttr::parse_all(attrs)?;
res.infer_kind(&parsed_attrs)?;
res.push_attrs(&parsed_attrs)?;
// Ignoring `push_doc_comment` as there is no top-level clap builder to add documentation
// to
@ -110,14 +109,14 @@ impl Item {
);
}
res
Ok(res)
}
pub fn from_subcommand_variant(
variant: &Variant,
struct_casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
) -> Result<Self, syn::Error> {
let name = variant.ident.clone();
let ident = variant.ident.clone();
let span = variant.span();
@ -138,9 +137,9 @@ impl Item {
env_casing,
kind,
);
let parsed_attrs = ClapAttr::parse_all(&variant.attrs);
res.infer_kind(&parsed_attrs);
res.push_attrs(&parsed_attrs);
let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
res.infer_kind(&parsed_attrs)?;
res.push_attrs(&parsed_attrs)?;
if matches!(&*res.kind, Kind::Command(_) | Kind::Subcommand(_)) {
res.push_doc_comment(&variant.attrs, "about", Some("long_about"));
}
@ -164,14 +163,14 @@ impl Item {
| Kind::Arg(_) => (),
}
res
Ok(res)
}
pub fn from_value_enum_variant(
variant: &Variant,
argument_casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
) -> Result<Self, syn::Error> {
let ident = variant.ident.clone();
let span = variant.span();
let kind = Sp::new(Kind::Value, span);
@ -183,21 +182,21 @@ impl Item {
env_casing,
kind,
);
let parsed_attrs = ClapAttr::parse_all(&variant.attrs);
res.infer_kind(&parsed_attrs);
res.push_attrs(&parsed_attrs);
let parsed_attrs = ClapAttr::parse_all(&variant.attrs)?;
res.infer_kind(&parsed_attrs)?;
res.push_attrs(&parsed_attrs)?;
if matches!(&*res.kind, Kind::Value) {
res.push_doc_comment(&variant.attrs, "help", None);
}
res
Ok(res)
}
pub fn from_args_field(
field: &Field,
struct_casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
) -> Self {
) -> Result<Self, syn::Error> {
let name = field.ident.clone().unwrap();
let ident = field.ident.clone().unwrap();
let span = field.span();
@ -211,9 +210,9 @@ impl Item {
env_casing,
kind,
);
let parsed_attrs = ClapAttr::parse_all(&field.attrs);
res.infer_kind(&parsed_attrs);
res.push_attrs(&parsed_attrs);
let parsed_attrs = ClapAttr::parse_all(&field.attrs)?;
res.infer_kind(&parsed_attrs)?;
res.push_attrs(&parsed_attrs)?;
if matches!(&*res.kind, Kind::Arg(_)) {
res.push_doc_comment(&field.attrs, "help", Some("long_help"));
}
@ -244,7 +243,7 @@ impl Item {
| Kind::ExternalSubcommand => {}
}
res
Ok(res)
}
fn new(
@ -329,7 +328,7 @@ impl Item {
}
}
fn infer_kind(&mut self, attrs: &[ClapAttr]) {
fn infer_kind(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
for attr in attrs {
if let Some(AttrValue::Call(_)) = &attr.value {
continue;
@ -339,7 +338,7 @@ impl Item {
let kind = match &attr.magic {
Some(MagicAttrName::FromGlobal) => {
if attr.value.is_some() {
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
abort!(expr, "attribute `{}` does not accept a value", attr.name);
}
let ty = self
@ -352,7 +351,7 @@ impl Item {
}
Some(MagicAttrName::Subcommand) if attr.value.is_none() => {
if attr.value.is_some() {
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
abort!(expr, "attribute `{}` does not accept a value", attr.name);
}
let ty = self
@ -365,7 +364,7 @@ impl Item {
}
Some(MagicAttrName::ExternalSubcommand) if attr.value.is_none() => {
if attr.value.is_some() {
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
abort!(expr, "attribute `{}` does not accept a value", attr.name);
}
let kind = Sp::new(Kind::ExternalSubcommand, attr.name.clone().span());
@ -373,7 +372,7 @@ impl Item {
}
Some(MagicAttrName::Flatten) if attr.value.is_none() => {
if attr.value.is_some() {
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
abort!(expr, "attribute `{}` does not accept a value", attr.name);
}
let ty = self
@ -396,12 +395,14 @@ impl Item {
};
if let Some(kind) = kind {
self.set_kind(kind);
self.set_kind(kind)?;
}
}
Ok(())
}
fn push_attrs(&mut self, attrs: &[ClapAttr]) {
fn push_attrs(&mut self, attrs: &[ClapAttr]) -> Result<(), syn::Error> {
for attr in attrs {
let actual_attr_kind = *attr.kind.get();
let expected_attr_kind = self.kind.attr_kind();
@ -437,7 +438,7 @@ impl Item {
match &attr.magic {
Some(MagicAttrName::Short) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
self.push_method(
*attr.kind.get(),
@ -447,13 +448,13 @@ impl Item {
}
Some(MagicAttrName::Long) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
self.push_method(*attr.kind.get(), attr.name.clone(), self.name.clone().translate(*self.casing));
}
Some(MagicAttrName::ValueParser) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
self.deprecations.push(Deprecation {
span: attr.name.span(),
@ -465,7 +466,7 @@ impl Item {
}
Some(MagicAttrName::Action) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
self.deprecations.push(Deprecation {
span: attr.name.span(),
@ -477,7 +478,7 @@ impl Item {
}
Some(MagicAttrName::Env) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
self.push_method(
*attr.kind.get(),
@ -487,7 +488,7 @@ impl Item {
}
Some(MagicAttrName::ValueEnum) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
self.is_enum = true
}
@ -497,45 +498,45 @@ impl Item {
}
Some(MagicAttrName::About) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Command]);
assert_attr_kind(attr, &[AttrKind::Command])?;
if let Some(method) =
Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")
Method::from_env(attr.name.clone(), "CARGO_PKG_DESCRIPTION")?
{
self.methods.push(method);
}
}
Some(MagicAttrName::LongAbout) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Command]);
assert_attr_kind(attr, &[AttrKind::Command])?;
self.force_long_help = true;
}
Some(MagicAttrName::LongHelp) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
self.force_long_help = true;
}
Some(MagicAttrName::Author) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Command]);
assert_attr_kind(attr, &[AttrKind::Command])?;
if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS") {
if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_AUTHORS")? {
self.methods.push(method);
}
}
Some(MagicAttrName::Version) if attr.value.is_none() => {
assert_attr_kind(attr, &[AttrKind::Command]);
assert_attr_kind(attr, &[AttrKind::Command])?;
if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION") {
if let Some(method) = Method::from_env(attr.name.clone(), "CARGO_PKG_VERSION")? {
self.methods.push(method);
}
}
Some(MagicAttrName::DefaultValueT) => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
let ty = if let Some(ty) = self.ty.as_ref() {
ty
@ -543,7 +544,7 @@ impl Item {
abort!(
attr.name.clone(),
"#[arg(default_value_t)] (without an argument) can be used \
only on field level";
only on field level\n\n= note: {note}\n\n",
note = "see \
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
@ -583,7 +584,7 @@ impl Item {
}
Some(MagicAttrName::DefaultValuesT) => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
let ty = if let Some(ty) = self.ty.as_ref() {
ty
@ -591,18 +592,18 @@ impl Item {
abort!(
attr.name.clone(),
"#[arg(default_values_t)] (without an argument) can be used \
only on field level";
only on field level\n\n= note: {note}\n\n",
note = "see \
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
};
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
let container_type = Ty::from_syn_ty(ty);
if *container_type != Ty::Vec {
abort!(
attr.name.clone(),
"#[arg(default_values_t)] can be used only on Vec types";
"#[arg(default_values_t)] can be used only on Vec types\n\n= note: {note}\n\n",
note = "see \
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
@ -667,7 +668,7 @@ impl Item {
}
Some(MagicAttrName::DefaultValueOsT) => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
let ty = if let Some(ty) = self.ty.as_ref() {
ty
@ -675,7 +676,7 @@ impl Item {
abort!(
attr.name.clone(),
"#[arg(default_value_os_t)] (without an argument) can be used \
only on field level";
only on field level\n\n= note: {note}\n\n",
note = "see \
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
@ -715,7 +716,7 @@ impl Item {
}
Some(MagicAttrName::DefaultValuesOsT) => {
assert_attr_kind(attr, &[AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Arg])?;
let ty = if let Some(ty) = self.ty.as_ref() {
ty
@ -723,18 +724,18 @@ impl Item {
abort!(
attr.name.clone(),
"#[arg(default_values_os_t)] (without an argument) can be used \
only on field level";
only on field level\n\n= note: {note}\n\n",
note = "see \
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
};
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
let container_type = Ty::from_syn_ty(ty);
if *container_type != Ty::Vec {
abort!(
attr.name.clone(),
"#[arg(default_values_os_t)] can be used only on Vec types";
"#[arg(default_values_os_t)] can be used only on Vec types\n\n= note: {note}\n\n",
note = "see \
https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes")
@ -799,29 +800,29 @@ impl Item {
}
Some(MagicAttrName::NextDisplayOrder) => {
assert_attr_kind(attr, &[AttrKind::Command]);
assert_attr_kind(attr, &[AttrKind::Command])?;
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
self.next_display_order = Some(Method::new(attr.name.clone(), quote!(#expr)));
}
Some(MagicAttrName::NextHelpHeading) => {
assert_attr_kind(attr, &[AttrKind::Command]);
assert_attr_kind(attr, &[AttrKind::Command])?;
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
self.next_help_heading = Some(Method::new(attr.name.clone(), quote!(#expr)));
}
Some(MagicAttrName::RenameAll) => {
let lit = attr.lit_str_or_abort();
self.casing = CasingStyle::from_lit(lit);
let lit = attr.lit_str_or_abort()?;
self.casing = CasingStyle::from_lit(lit)?;
}
Some(MagicAttrName::RenameAllEnv) => {
assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg]);
assert_attr_kind(attr, &[AttrKind::Command, AttrKind::Arg])?;
let lit = attr.lit_str_or_abort();
self.env_casing = CasingStyle::from_lit(lit);
let lit = attr.lit_str_or_abort()?;
self.env_casing = CasingStyle::from_lit(lit)?;
}
Some(MagicAttrName::Skip) if actual_attr_kind == AttrKind::Group => {
@ -839,20 +840,20 @@ impl Item {
| Some(MagicAttrName::Author)
| Some(MagicAttrName::Version)
=> {
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
self.push_method(*attr.kind.get(), attr.name.clone(), expr);
}
// Magic only for the default, otherwise just forward to the builder
Some(MagicAttrName::ValueParser) | Some(MagicAttrName::Action) => {
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
self.push_method(*attr.kind.get(), attr.name.clone(), expr);
}
// Directives that never receive a value
Some(MagicAttrName::ValueEnum)
| Some(MagicAttrName::VerbatimDocComment) => {
let expr = attr.value_or_abort();
let expr = attr.value_or_abort()?;
abort!(expr, "attribute `{}` does not accept a value", attr.name);
}
@ -883,6 +884,8 @@ impl Item {
);
}
}
Ok(())
}
fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) {
@ -912,7 +915,7 @@ impl Item {
}
}
fn set_kind(&mut self, kind: Sp<Kind>) {
fn set_kind(&mut self, kind: Sp<Kind>) -> Result<(), syn::Error> {
match (self.kind.get(), kind.get()) {
(Kind::Arg(_), Kind::FromGlobal(_))
| (Kind::Arg(_), Kind::Subcommand(_))
@ -932,6 +935,7 @@ impl Item {
abort!(kind.span(), "`{}` cannot be used with `{}`", new, old);
}
}
Ok(())
}
pub fn find_default_method(&self) -> Option<&Method> {
@ -1211,19 +1215,21 @@ impl Method {
Method { name, args }
}
fn from_env(ident: Ident, env_var: &str) -> Option<Self> {
fn from_env(ident: Ident, env_var: &str) -> Result<Option<Self>, syn::Error> {
let mut lit = match env::var(env_var) {
Ok(val) => {
if val.is_empty() {
return None;
return Ok(None);
}
LitStr::new(&val, ident.span())
}
Err(_) => {
abort!(ident,
"cannot derive `{}` from Cargo.toml", ident;
note = "`{}` environment variable is not set", env_var;
help = "use `{} = \"...\"` to set {} manually", ident, ident;
abort!(
ident,
"cannot derive `{}` from Cargo.toml\n\n= note: {note}\n\n= help: {help}\n\n",
ident,
note = format_args!("`{}` environment variable is not set", env_var),
help = format_args!("use `{} = \"...\"` to set {} manually", ident, ident)
);
}
};
@ -1233,7 +1239,7 @@ impl Method {
lit = LitStr::new(&edited, lit.span());
}
Some(Method::new(ident, quote!(#lit)))
Ok(Some(Method::new(ident, quote!(#lit))))
}
pub(crate) fn args(&self) -> &TokenStream {
@ -1299,7 +1305,7 @@ impl ToTokens for Deprecation {
}
}
fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) {
fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) -> Result<(), syn::Error> {
if *attr.kind.get() == AttrKind::Clap || *attr.kind.get() == AttrKind::StructOpt {
// deprecated
} else if !possible_kind.contains(attr.kind.get()) {
@ -1315,6 +1321,7 @@ fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) {
options.join(", ")
);
}
Ok(())
}
/// replace all `:` with `, ` when not inside the `<>`
@ -1364,13 +1371,13 @@ pub enum CasingStyle {
}
impl CasingStyle {
fn from_lit(name: &LitStr) -> Sp<Self> {
fn from_lit(name: &LitStr) -> Result<Sp<Self>, syn::Error> {
use self::CasingStyle::*;
let normalized = name.value().to_upper_camel_case().to_lowercase();
let cs = |kind| Sp::new(kind, name.span());
match normalized.as_ref() {
let s = match normalized.as_ref() {
"camel" | "camelcase" => cs(Camel),
"kebab" | "kebabcase" => cs(Kebab),
"pascal" | "pascalcase" => cs(Pascal),
@ -1380,7 +1387,8 @@ impl CasingStyle {
"upper" | "uppercase" => cs(Upper),
"verbatim" | "verbatimcase" => cs(Verbatim),
s => abort!(name, "unsupported casing: `{}`", s),
}
};
Ok(s)
}
}

View file

@ -19,8 +19,11 @@
extern crate proc_macro;
use proc_macro::TokenStream;
use proc_macro_error::proc_macro_error;
use syn::{parse_macro_input, DeriveInput};
use syn::{Data, DataStruct, Fields};
#[macro_use]
mod macros;
mod attr;
mod derives;
@ -30,10 +33,14 @@ mod utils;
/// Generates the `ValueEnum` impl.
#[proc_macro_derive(ValueEnum, attributes(clap, value))]
#[proc_macro_error]
pub fn value_enum(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_value_enum(&input).into()
derives::derive_value_enum(&input)
.unwrap_or_else(|err| {
let dummy = dummies::value_enum(&input.ident);
to_compile_error(err, dummy)
})
.into()
}
/// Generates the `Parser` implementation.
@ -43,24 +50,67 @@ pub fn value_enum(input: TokenStream) -> TokenStream {
/// implementing a conversion code to instantiate an instance of the user
/// context struct.
#[proc_macro_derive(Parser, attributes(clap, structopt, command, arg, group))]
#[proc_macro_error]
pub fn parser(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_parser(&input).into()
derives::derive_parser(&input)
.unwrap_or_else(|err| {
let specific_dummy = match input.data {
Data::Struct(DataStruct {
fields: Fields::Named(ref _fields),
..
}) => Some(dummies::args(&input.ident)),
Data::Struct(DataStruct {
fields: Fields::Unit,
..
}) => Some(dummies::args(&input.ident)),
Data::Enum(_) => Some(dummies::subcommand(&input.ident)),
_ => None,
};
let dummy = specific_dummy
.map(|specific_dummy| {
let parser_dummy = dummies::parser(&input.ident);
quote::quote! {
#parser_dummy
#specific_dummy
}
})
.unwrap_or_else(|| quote::quote!());
to_compile_error(err, dummy)
})
.into()
}
/// Generates the `Subcommand` impl.
#[proc_macro_derive(Subcommand, attributes(clap, command, arg, group))]
#[proc_macro_error]
pub fn subcommand(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_subcommand(&input).into()
derives::derive_subcommand(&input)
.unwrap_or_else(|err| {
let dummy = dummies::subcommand(&input.ident);
to_compile_error(err, dummy)
})
.into()
}
/// Generates the `Args` impl.
#[proc_macro_derive(Args, attributes(clap, command, arg, group))]
#[proc_macro_error]
pub fn args(input: TokenStream) -> TokenStream {
let input: DeriveInput = parse_macro_input!(input);
derives::derive_args(&input).into()
derives::derive_args(&input)
.unwrap_or_else(|err| {
let dummy = dummies::args(&input.ident);
to_compile_error(err, dummy)
})
.into()
}
fn to_compile_error(
error: syn::Error,
dummy: proc_macro2::TokenStream,
) -> proc_macro2::TokenStream {
let compile_errors = error.to_compile_error();
quote::quote!(
#dummy
#compile_errors
)
}

21
clap_derive/src/macros.rs Normal file
View file

@ -0,0 +1,21 @@
macro_rules! format_err {
($obj:expr, $($format:tt)+) => {{
#[allow(unused_imports)]
use $crate::utils::error::*;
let msg = format!($($format)+);
$obj.EXPECTED_Span_OR_ToTokens(msg)
}};
}
macro_rules! abort {
($obj:expr, $($format:tt)+) => {{
return Err(format_err!($obj, $($format)+));
}};
}
macro_rules! abort_call_site {
($($format:tt)+) => {{
let span = proc_macro2::Span::call_site();
abort!(span, $($format)+)
}};
}

View file

@ -6,24 +6,26 @@
use std::iter;
pub fn extract_doc_comment(attrs: &[syn::Attribute]) -> Vec<String> {
use syn::Lit::*;
use syn::Meta::*;
use syn::MetaNameValue;
// multiline comments (`/** ... */`) may have LFs (`\n`) in them,
// we need to split so we could handle the lines correctly
//
// we also need to remove leading and trailing blank lines
let mut lines: Vec<_> = attrs
.iter()
.filter(|attr| attr.path.is_ident("doc"))
.filter(|attr| attr.path().is_ident("doc"))
.filter_map(|attr| {
if let Ok(NameValue(MetaNameValue { lit: Str(s), .. })) = attr.parse_meta() {
Some(s.value())
} else {
// non #[doc = "..."] attributes are not our concern
// we leave them for rustc to handle
None
// non #[doc = "..."] attributes are not our concern
// we leave them for rustc to handle
match &attr.meta {
syn::Meta::NameValue(syn::MetaNameValue {
value:
syn::Expr::Lit(syn::ExprLit {
lit: syn::Lit::Str(s),
..
}),
..
}) => Some(s.value()),
_ => None,
}
})
.skip_while(|s| is_blank(s))

View file

@ -0,0 +1,22 @@
pub trait SpanError {
#[allow(non_snake_case)]
fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error;
}
pub trait ToTokensError {
#[allow(non_snake_case)]
fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error;
}
impl<T: quote::ToTokens> ToTokensError for T {
fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error {
// Curb monomorphization from generating too many identical `new_spanned`.
syn::Error::new_spanned(self.to_token_stream(), msg)
}
}
impl SpanError for proc_macro2::Span {
fn EXPECTED_Span_OR_ToTokens<D: std::fmt::Display>(&self, msg: D) -> syn::Error {
syn::Error::new(*self, msg)
}
}

View file

@ -1,3 +1,5 @@
pub mod error;
mod doc_comments;
mod spanned;
mod ty;

View file

@ -1,8 +1,8 @@
error: expected attribute arguments in parentheses: #[command(...)]
--> tests/derive_ui/clap_empty_attr.rs:4:1
--> tests/derive_ui/clap_empty_attr.rs:4:3
|
4 | #[command]
| ^^^^^^^^^^
| ^^^^^^^
error: expected parentheses: #[arg(...)]
--> tests/derive_ui/clap_empty_attr.rs:9:11

View file

@ -1,6 +1,6 @@
error: #[arg(default_values_t)] can be used only on Vec types
= note: see https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes
= note: see https://github.com/clap-rs/clap/blob/master/examples/derive_ref/README.md#magic-attributes
--> tests/derive_ui/default_values_t_invalid.rs:6:11
|