aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMatthew Maurer <mmaurer@google.com>2023-05-27 02:52:10 +0000
committerAutomerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>2023-05-27 02:52:10 +0000
commit357124343b4fcfbb877a89c3ff22bc0b2f3fa007 (patch)
treef200d0f45f3a1a7c29e4a492c18a55e58c6b830a
parent15e795a530941bdc110ec1b83b883f62563e7099 (diff)
parentec93ee10247fff1ae7465810679ba9d2519f7b25 (diff)
downloadclap_derive-357124343b4fcfbb877a89c3ff22bc0b2f3fa007.tar.gz
Upgrade clap_derive to 4.2.0 am: 324bd852e0 am: 5e5c42af66 am: 424e13ee69 am: 1c7a38475e am: fcfe0b80e5 am: ec93ee1024
Original change: https://android-review.googlesource.com/c/platform/external/rust/crates/clap_derive/+/2520756 Change-Id: Ief2b676421352aaebb87819cbe7630853cc04cbc Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
-rw-r--r--.cargo_vcs_info.json2
-rw-r--r--Android.bp3
-rw-r--r--Cargo.toml7
-rw-r--r--Cargo.toml.orig5
-rw-r--r--METADATA6
-rw-r--r--src/attr.rs71
-rw-r--r--src/derives/args.rs115
-rw-r--r--src/derives/into_app.rs18
-rw-r--r--src/derives/parser.rs54
-rw-r--r--src/derives/subcommand.rs550
-rw-r--r--src/derives/value_enum.rs81
-rw-r--r--src/dummies.rs59
-rw-r--r--src/item.rs261
-rw-r--r--src/lib.rs68
-rw-r--r--src/macros.rs21
-rw-r--r--src/utils/doc_comments.rs24
-rw-r--r--src/utils/error.rs22
-rw-r--r--src/utils/mod.rs2
18 files changed, 741 insertions, 628 deletions
diff --git a/.cargo_vcs_info.json b/.cargo_vcs_info.json
index 447f5f0..89e449a 100644
--- a/.cargo_vcs_info.json
+++ b/.cargo_vcs_info.json
@@ -1,6 +1,6 @@
{
"git": {
- "sha1": "f931de694a5478d7e4bd424613e6699f7d4f4e9f"
+ "sha1": "8fc65e28b66878f6d1ee45c725fbfb31b35321e3"
},
"path_in_vcs": "clap_derive"
} \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index b504f5e..16c0dc4 100644
--- a/Android.bp
+++ b/Android.bp
@@ -41,14 +41,13 @@ rust_proc_macro {
name: "libclap_derive",
crate_name: "clap_derive",
cargo_env_compat: true,
- cargo_pkg_version: "4.1.8",
+ cargo_pkg_version: "4.2.0",
srcs: ["src/lib.rs"],
edition: "2021",
features: ["default"],
rustlibs: [
"libheck",
"libproc_macro2",
- "libproc_macro_error",
"libquote",
"libsyn",
],
diff --git a/Cargo.toml b/Cargo.toml
index be05b33..48793fb 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -13,7 +13,7 @@
edition = "2021"
rust-version = "1.64.0"
name = "clap_derive"
-version = "4.1.8"
+version = "4.2.0"
include = [
"build.rs",
"src/**/*",
@@ -54,9 +54,6 @@ proc-macro = true
[dependencies.heck]
version = "0.4.0"
-[dependencies.proc-macro-error]
-version = "1"
-
[dependencies.proc-macro2]
version = "1.0.42"
@@ -64,7 +61,7 @@ version = "1.0.42"
version = "1.0.9"
[dependencies.syn]
-version = "1.0.74"
+version = "2.0.8"
features = ["full"]
[features]
diff --git a/Cargo.toml.orig b/Cargo.toml.orig
index a32d823..bd5b5ef 100644
--- a/Cargo.toml.orig
+++ b/Cargo.toml.orig
@@ -1,6 +1,6 @@
[package]
name = "clap_derive"
-version = "4.1.8"
+version = "4.2.0"
description = "Parse command line argument by defining a struct, derive crate."
repository = "https://github.com/clap-rs/clap/tree/master/clap_derive"
categories = ["command-line-interface", "development-tools::procedural-macro-helpers"]
@@ -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"
[features]
default = []
diff --git a/METADATA b/METADATA
index d2ca19f..684ef04 100644
--- a/METADATA
+++ b/METADATA
@@ -11,13 +11,13 @@ third_party {
}
url {
type: ARCHIVE
- value: "https://static.crates.io/crates/clap_derive/clap_derive-4.1.8.crate"
+ value: "https://static.crates.io/crates/clap_derive/clap_derive-4.2.0.crate"
}
- version: "4.1.8"
+ version: "4.2.0"
license_type: NOTICE
last_upgrade_date {
year: 2023
month: 3
- day: 2
+ day: 31
}
}
diff --git a/src/attr.rs b/src/attr.rs
index e282a8f..3bc9ac0 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -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
diff --git a/src/derives/args.rs b/src/derives/args.rs
index d3b3692..e8611b6 100644
--- a/src/derives/args.rs
+++ b/src/derives/args.rs
@@ -13,38 +13,33 @@
// 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::{
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 {
+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 +47,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 +67,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,22 +77,22 @@ 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)
} else {
- let group_id = item.ident().unraw().to_string();
+ let group_id = item.group_id();
quote!(Some(clap::Id::from(#group_id)))
};
- quote! {
+ Ok(quote! {
#[allow(dead_code, unreachable_code, unused_variables, unused_braces)]
#[allow(
clippy::style,
@@ -157,7 +152,7 @@ pub fn gen_for_struct(
#augmentation_update
}
}
- }
+ })
}
/// Generate a block of code to add arguments/subcommands corresponding to
@@ -167,11 +162,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 +175,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 +353,9 @@ pub fn gen_augment(
});
})
}
- }
- });
+ };
+ args.push(genned);
+ }
let deprecations = if !override_required {
parent_item.deprecations()
@@ -367,7 +367,7 @@ pub fn gen_augment(
let group_app_methods = if parent_item.skip_group() {
quote!()
} else {
- let group_id = parent_item.ident().unraw().to_string();
+ let group_id = parent_item.group_id();
let literal_group_members = fields
.iter()
.filter_map(|(_field, item)| {
@@ -400,15 +400,18 @@ pub fn gen_augment(
}};
}
+ let group_methods = parent_item.group_methods();
+
quote!(
.group(
clap::ArgGroup::new(#group_id)
.multiple(true)
+ #group_methods
.args(#literal_group_members)
)
)
};
- quote! {{
+ Ok(quote! {{
#deprecations
let #app_var = #app_var
#initial_app_methods
@@ -416,15 +419,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 +522,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 +549,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 +620,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 +642,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 +715,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 +724,8 @@ fn gen_parsers(
}
} else {
quote_spanned!(field.span()=> #field_name: #field_value )
- }
+ };
+ Ok(genned)
}
#[cfg(feature = "raw-deprecated")]
diff --git a/src/derives/into_app.rs b/src/derives/into_app.rs
index dfb676a..72f081f 100644
--- a/src/derives/into_app.rs
+++ b/src/derives/into_app.rs
@@ -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)
}
}
- }
+ })
}
diff --git a/src/derives/parser.rs b/src/derives/parser.rs
index 617857c..39b0b8a 100644
--- a/src/derives/parser.rs
+++ b/src/derives/parser.rs
@@ -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
- }
+ })
}
diff --git a/src/derives/subcommand.rs b/src/derives/subcommand.rs
index ffe22ec..403fe45 100644
--- a/src/derives/subcommand.rs
+++ b/src/derives/subcommand.rs
@@ -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 \
+ _ => 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").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::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 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));
+ 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"
+ ),
+ },
- Kind::Flatten(_) => match variant.fields {
+ 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];
- 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);
+ if override_required {
+ quote_spanned! { ty.span()=>
+ {
+ <#ty as clap::Subcommand>::augment_subcommands_for_update(#subcommand_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"
- ),
- },
-
- 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)
- }
+ quote_spanned! { ty.span()=>
+ {
+ <#ty as clap::Subcommand>::augment_subcommands(#subcommand_var)
}
}
}
- Unnamed(..) => {
- abort!(variant, "non single-typed tuple enums are not supported")
- }
- };
+ }
+ 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 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)
- }
+ }
+ } 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
}
+ };
+ 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
+ ext_subcmd = Some((span, &variant.ident, str_ty));
+ }
+ Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => {
+ if matches!(&*item.kind(), Kind::Flatten(_)) {
+ flatten_variants.push((variant, item));
+ } else {
+ unflatten_variants.push((variant, item));
}
- Kind::Flatten(_) | Kind::Subcommand(_) | Kind::Command(_) => Some((variant, item)),
}
- })
- .partition(|(_, item)| {
- let kind = item.kind();
- matches!(&*kind, Kind::Flatten(_))
- });
+ }
+ }
- 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(())
}
- }
+ })
}
diff --git a/src/derives/value_enum.rs b/src/derives/value_enum.rs
index 7a9d870..6f107c0 100644
--- a/src/derives/value_enum.rs
+++ b/src/derives/value_enum.rs
@@ -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 {
diff --git a/src/dummies.rs b/src/dummies.rs
index 1f54e1d..b10bedc 100644
--- a/src/dummies.rs
+++ b/src/dummies.rs
@@ -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!()
}
}
- })
+ }
}
diff --git a/src/item.rs b/src/item.rs
index 5e8272a..9b29ff9 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -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};
@@ -33,7 +32,6 @@ pub const DEFAULT_ENV_CASING: CasingStyle = CasingStyle::ScreamingSnake;
#[derive(Clone)]
pub struct Item {
name: Name,
- ident: Ident,
casing: Sp<CasingStyle>,
env_casing: Sp<CasingStyle>,
ty: Option<Type>,
@@ -49,11 +47,13 @@ pub struct Item {
is_enum: bool,
is_positional: bool,
skip_group: bool,
+ group_id: Name,
+ group_methods: Vec<Method>,
kind: Sp<Kind>,
}
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 +62,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 +79,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 +96,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 +110,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 +138,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 +164,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 +183,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 +211,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 +244,7 @@ impl Item {
| Kind::ExternalSubcommand => {}
}
- res
+ Ok(res)
}
fn new(
@@ -255,9 +255,9 @@ impl Item {
env_casing: Sp<CasingStyle>,
kind: Sp<Kind>,
) -> Self {
+ let group_id = Name::Derived(ident);
Self {
name,
- ident,
ty,
casing,
env_casing,
@@ -273,11 +273,17 @@ impl Item {
is_enum: false,
is_positional: true,
skip_group: false,
+ group_id,
+ group_methods: vec![],
kind,
}
}
fn push_method(&mut self, kind: AttrKind, name: Ident, arg: impl ToTokens) {
+ self.push_method_(kind, name, arg.to_token_stream());
+ }
+
+ fn push_method_(&mut self, kind: AttrKind, name: Ident, arg: TokenStream) {
if name == "id" {
match kind {
AttrKind::Command | AttrKind::Value => {
@@ -291,10 +297,15 @@ impl Item {
kind.as_str()
),
});
+ self.name = Name::Assigned(arg);
+ }
+ AttrKind::Group => {
+ self.group_id = Name::Assigned(arg);
+ }
+ AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => {
+ self.name = Name::Assigned(arg);
}
- AttrKind::Group | AttrKind::Arg | AttrKind::Clap | AttrKind::StructOpt => {}
}
- self.name = Name::Assigned(quote!(#arg));
} else if name == "name" {
match kind {
AttrKind::Arg => {
@@ -309,27 +320,29 @@ impl Item {
kind.as_str()
),
});
+ self.name = Name::Assigned(arg);
+ }
+ AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
+ AttrKind::Command | AttrKind::Value | AttrKind::Clap | AttrKind::StructOpt => {
+ self.name = Name::Assigned(arg);
}
- AttrKind::Group
- | AttrKind::Command
- | AttrKind::Value
- | AttrKind::Clap
- | AttrKind::StructOpt => {}
}
- self.name = Name::Assigned(quote!(#arg));
} else if name == "value_parser" {
- self.value_parser = Some(ValueParser::Explicit(Method::new(name, quote!(#arg))));
+ self.value_parser = Some(ValueParser::Explicit(Method::new(name, arg)));
} else if name == "action" {
- self.action = Some(Action::Explicit(Method::new(name, quote!(#arg))));
+ self.action = Some(Action::Explicit(Method::new(name, arg)));
} else {
if name == "short" || name == "long" {
self.is_positional = false;
}
- self.methods.push(Method::new(name, quote!(#arg)));
+ match kind {
+ AttrKind::Group => self.group_methods.push(Method::new(name, arg)),
+ _ => self.methods.push(Method::new(name, arg)),
+ };
}
}
- 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 +352,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 +365,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 +378,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 +386,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 +409,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 +452,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 +462,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 +480,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 +492,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 +502,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 +512,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 +558,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")
@@ -560,7 +575,7 @@ impl Item {
.any(|a| a.magic == Some(MagicAttrName::ValueEnum))
{
quote_spanned!(attr.name.clone().span()=> {
- static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<String> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_VALUE: clap::__derive_refs::once_cell::sync::Lazy<String> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
let val: #ty = #val;
clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
});
@@ -569,7 +584,7 @@ impl Item {
})
} else {
quote_spanned!(attr.name.clone().span()=> {
- static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<String> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_VALUE: clap::__derive_refs::once_cell::sync::Lazy<String> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
let val: #ty = #val;
::std::string::ToString::to_string(&val)
});
@@ -583,7 +598,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 +606,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")
@@ -628,11 +643,11 @@ impl Item {
})
}
- static DEFAULT_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_STRINGS: clap::__derive_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
iter_to_vals(#expr).collect()
});
- static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_VALUES: clap::__derive_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
DEFAULT_STRINGS.iter().map(::std::string::String::as_str).collect()
});
DEFAULT_VALUES.iter().copied()
@@ -648,11 +663,11 @@ impl Item {
iterable.into_iter().map(|val| val.borrow().to_string())
}
- static DEFAULT_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_STRINGS: clap::__derive_refs::once_cell::sync::Lazy<Vec<::std::string::String>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
iter_to_vals(#expr).collect()
});
- static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_VALUES: clap::__derive_refs::once_cell::sync::Lazy<Vec<&str>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
DEFAULT_STRINGS.iter().map(::std::string::String::as_str).collect()
});
DEFAULT_VALUES.iter().copied()
@@ -667,7 +682,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 +690,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")
@@ -692,7 +707,7 @@ impl Item {
.any(|a| a.magic == Some(MagicAttrName::ValueEnum))
{
quote_spanned!(attr.name.clone().span()=> {
- static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_VALUE: clap::__derive_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
let val: #ty = #val;
clap::ValueEnum::to_possible_value(&val).unwrap().get_name().to_owned()
});
@@ -701,7 +716,7 @@ impl Item {
})
} else {
quote_spanned!(attr.name.clone().span()=> {
- static DEFAULT_VALUE: clap::__macro_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_VALUE: clap::__derive_refs::once_cell::sync::Lazy<::std::ffi::OsString> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
let val: #ty = #val;
::std::ffi::OsString::from(val)
});
@@ -715,7 +730,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 +738,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")
@@ -760,11 +775,11 @@ impl Item {
})
}
- static DEFAULT_OS_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_OS_STRINGS: clap::__derive_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
iter_to_vals(#expr).collect()
});
- static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_VALUES: clap::__derive_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
DEFAULT_OS_STRINGS.iter().map(::std::ffi::OsString::as_os_str).collect()
});
DEFAULT_VALUES.iter().copied()
@@ -780,11 +795,11 @@ impl Item {
iterable.into_iter().map(|val| val.borrow().into())
}
- static DEFAULT_OS_STRINGS: clap::__macro_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_OS_STRINGS: clap::__derive_refs::once_cell::sync::Lazy<Vec<::std::ffi::OsString>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
iter_to_vals(#expr).collect()
});
- static DEFAULT_VALUES: clap::__macro_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__macro_refs::once_cell::sync::Lazy::new(|| {
+ static DEFAULT_VALUES: clap::__derive_refs::once_cell::sync::Lazy<Vec<&::std::ffi::OsStr>> = clap::__derive_refs::once_cell::sync::Lazy::new(|| {
DEFAULT_OS_STRINGS.iter().map(::std::ffi::OsString::as_os_str).collect()
});
DEFAULT_VALUES.iter().copied()
@@ -799,29 +814,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 +854,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 +898,8 @@ impl Item {
);
}
}
+
+ Ok(())
}
fn push_doc_comment(&mut self, attrs: &[Attribute], short_name: &str, long_name: Option<&str>) {
@@ -912,7 +929,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 +949,7 @@ impl Item {
abort!(kind.span(), "`{}` cannot be used with `{}`", new, old);
}
}
+ Ok(())
}
pub fn find_default_method(&self) -> Option<&Method> {
@@ -964,6 +982,15 @@ impl Item {
quote!( #(#doc_comment)* #(#methods)* )
}
+ pub fn group_id(&self) -> TokenStream {
+ self.group_id.clone().raw()
+ }
+
+ pub fn group_methods(&self) -> TokenStream {
+ let group_methods = &self.group_methods;
+ quote!( #(#group_methods)* )
+ }
+
pub fn deprecations(&self) -> proc_macro2::TokenStream {
let deprecations = &self.deprecations;
quote!( #(#deprecations)* )
@@ -979,10 +1006,6 @@ impl Item {
quote!( #(#next_help_heading)* )
}
- pub fn ident(&self) -> &Ident {
- &self.ident
- }
-
pub fn id(&self) -> TokenStream {
self.name.clone().raw()
}
@@ -1211,19 +1234,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 +1258,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 +1324,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 +1340,7 @@ fn assert_attr_kind(attr: &ClapAttr, possible_kind: &[AttrKind]) {
options.join(", ")
);
}
+ Ok(())
}
/// replace all `:` with `, ` when not inside the `<>`
@@ -1364,13 +1390,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 +1406,8 @@ impl CasingStyle {
"upper" | "uppercase" => cs(Upper),
"verbatim" | "verbatimcase" => cs(Verbatim),
s => abort!(name, "unsupported casing: `{}`", s),
- }
+ };
+ Ok(s)
}
}
diff --git a/src/lib.rs b/src/lib.rs
index 84f2f0d..d3cae9b 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -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
+ )
}
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..282048b
--- /dev/null
+++ b/src/macros.rs
@@ -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)+)
+ }};
+}
diff --git a/src/utils/doc_comments.rs b/src/utils/doc_comments.rs
index 5183b6b..63c6ad1 100644
--- a/src/utils/doc_comments.rs
+++ b/src/utils/doc_comments.rs
@@ -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))
diff --git a/src/utils/error.rs b/src/utils/error.rs
new file mode 100644
index 0000000..276e349
--- /dev/null
+++ b/src/utils/error.rs
@@ -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)
+ }
+}
diff --git a/src/utils/mod.rs b/src/utils/mod.rs
index 9f8b6f3..13e6e71 100644
--- a/src/utils/mod.rs
+++ b/src/utils/mod.rs
@@ -1,3 +1,5 @@
+pub mod error;
+
mod doc_comments;
mod spanned;
mod ty;