diff options
author | Robin Krahl <robin.krahl@ireas.org> | 2020-01-07 11:18:04 +0000 |
---|---|---|
committer | Daniel Mueller <deso@posteo.net> | 2020-01-08 09:20:25 -0800 |
commit | 5e20a29b4fdc8a2d442d1093681b396dcb4b816b (patch) | |
tree | 55ab083fa8999d2ccbb5e921c1ffe52560dca152 /proc-macro-error/proc-macro-error-attr | |
parent | 203e691f46d591a2cc8acdfd850fa9f5b0fb8a98 (diff) | |
download | nitrocli-5e20a29b4fdc8a2d442d1093681b396dcb4b816b.tar.gz nitrocli-5e20a29b4fdc8a2d442d1093681b396dcb4b816b.tar.bz2 |
Add structopt dependency in version 0.3.7
This patch series replaces argparse with structopt in the argument
handling code. As a first step, we need structopt as a dependency.
Import subrepo structopt/:structopt at efbdda4753592e27bc430fb01f7b9650b2f3174d
Import subrepo bitflags/:bitflags at 30668016aca6bd3b02c766e8347e0b4080d4c296
Import subrepo clap/:clap at 784524f7eb193e35f81082cc69454c8c21b948f7
Import subrepo heck/:heck at 093d56fbf001e1506e56dbfa38631d99b1066df1
Import subrepo proc-macro-error/:proc-macro-error at 6c4cfe79a622c5de8ae68557993542be46eacae2
Import subrepo proc-macro2/:proc-macro2 at d5d48eddca4566e5438e8a2cbed4a74e049544de
Import subrepo quote/:quote at 727436c6c137b20f0f34dde5d8fda2679b9747ad
Import subrepo rustversion/:rustversion at 0c5663313516263059ce9059ef81fc7a1cf655ca
Import subrepo syn-mid/:syn-mid at 5d3d85414a9e6674e1857ec22a87b96e04a6851a
Import subrepo syn/:syn at e87c27e87f6f4ef8919d0372bdb056d53ef0d8f3
Import subrepo textwrap/:textwrap at abcd618beae3f74841032aa5b53c1086b0a57ca2
Import subrepo unicode-segmentation/:unicode-segmentation at 637c9874c4fe0c205ff27787faf150a40295c6c3
Import subrepo unicode-width/:unicode-width at 3033826f8bf05e82724140a981d5941e48fce393
Import subrepo unicode-xid/:unicode-xid at 4baae9fffb156ba229665b972a9cd5991787ceb7
Diffstat (limited to 'proc-macro-error/proc-macro-error-attr')
-rw-r--r-- | proc-macro-error/proc-macro-error-attr/Cargo.toml | 18 | ||||
-rw-r--r-- | proc-macro-error/proc-macro-error-attr/src/lib.rs | 162 |
2 files changed, 180 insertions, 0 deletions
diff --git a/proc-macro-error/proc-macro-error-attr/Cargo.toml b/proc-macro-error/proc-macro-error-attr/Cargo.toml new file mode 100644 index 0000000..6dee68e --- /dev/null +++ b/proc-macro-error/proc-macro-error-attr/Cargo.toml @@ -0,0 +1,18 @@ +[package] +name = "proc-macro-error-attr" +version = "0.4.3" +authors = ["CreepySkeleton <creepy-skeleton@yandex.ru>"] +edition = "2018" +description = "attribute macro for proc-macro-error crate" +license = "MIT OR Apache-2.0" +repository = "https://gitlab.com/CreepySkeleton/proc-macro-error" + +[lib] +proc-macro = true + +[dependencies] +quote = "1" +proc-macro2 = "1" +syn = { version = "1", default-features = false, features = ["derive", "parsing", "proc-macro"] } +syn-mid = "0.4" +rustversion = "1.0" diff --git a/proc-macro-error/proc-macro-error-attr/src/lib.rs b/proc-macro-error/proc-macro-error-attr/src/lib.rs new file mode 100644 index 0000000..3c1013b --- /dev/null +++ b/proc-macro-error/proc-macro-error-attr/src/lib.rs @@ -0,0 +1,162 @@ +extern crate proc_macro; + +use proc_macro::TokenStream; +use proc_macro2::Ident; +use quote::quote; +use std::iter::FromIterator; +use syn::{ + parse::{Parse, ParseStream}, + parse_macro_input, + punctuated::Punctuated, + Attribute, Token, +}; +use syn_mid::{Block, ItemFn}; + +use self::Setting::*; + +#[proc_macro_attribute] +pub fn proc_macro_error(attr: TokenStream, input: TokenStream) -> TokenStream { + let input = parse_macro_input!(input as ItemFn); + let mut settings = match syn::parse::<Settings>(attr) { + Ok(settings) => settings, + Err(err) => { + let err = err.to_compile_error(); + return quote!(#input #err).into(); + } + }; + + let is_proc_macro = is_proc_macro(&input.attrs); + if is_proc_macro { + settings.set(AssertUnwindSafe); + } + + if detect_proc_macro_hack(&input.attrs) { + settings.set(ProcMacroHack); + } + + if !(settings.is_set(AllowNotMacro) || is_proc_macro) { + return quote!( + #input + compile_error!("#[proc_macro_error] attribute can be used only with a proc-macro\n\n \ + hint: if you are really sure that #[proc_macro_error] should be applied \ + to this exact function use #[proc_macro_error(allow_not_macro)]\n"); + ) + .into(); + } + + let ItemFn { + attrs, + vis, + constness, + asyncness, + unsafety, + abi, + fn_token, + ident, + generics, + inputs, + output, + block, + .. + } = input; + + let body = gen_body(block, settings); + + quote!( + #(#attrs)* + #vis + #constness + #asyncness + #unsafety + #abi + #fn_token + #ident + #generics + (#inputs) + #output + + { #body } + ) + .into() +} + +#[derive(PartialEq)] +enum Setting { + AssertUnwindSafe, + AllowNotMacro, + ProcMacroHack, +} + +impl Parse for Setting { + fn parse(input: ParseStream) -> syn::Result<Self> { + let ident: Ident = input.parse()?; + match &*ident.to_string() { + "assert_unwind_safe" => Ok(AssertUnwindSafe), + "allow_not_macro" => Ok(AllowNotMacro), + "proc_macro_hack" => Ok(ProcMacroHack), + _ => Err(syn::Error::new( + ident.span(), + format!( + "unknown setting `{}`, expected one of \ + `assert_unwind_safe`, `allow_not_macro`, `proc_macro_hack`", + ident + ), + )), + } + } +} + +struct Settings(Vec<Setting>); +impl Parse for Settings { + fn parse(input: ParseStream) -> syn::Result<Self> { + let punct = Punctuated::<Setting, Token![,]>::parse_terminated(input)?; + Ok(Settings(Vec::from_iter(punct))) + } +} + +impl Settings { + fn is_set(&self, setting: Setting) -> bool { + self.0.iter().any(|s| *s == setting) + } + + fn set(&mut self, setting: Setting) { + self.0.push(setting) + } +} + +#[rustversion::since(1.37)] +fn gen_body(block: Block, settings: Settings) -> proc_macro2::TokenStream { + let is_proc_macro_hack = settings.is_set(ProcMacroHack); + let closure = if settings.is_set(AssertUnwindSafe) { + quote!(::std::panic::AssertUnwindSafe(|| #block )) + } else { + quote!(|| #block) + }; + + quote!( ::proc_macro_error::entry_point(#closure, #is_proc_macro_hack) ) +} + +// FIXME: +// proc_macro::TokenStream does not implement UnwindSafe until 1.37.0. +// Considering this is the closure's return type the unwind safety check would fail +// for virtually every closure possible, the check is meaningless. +#[rustversion::before(1.37)] +fn gen_body(block: Block, settings: Settings) -> proc_macro2::TokenStream { + let is_proc_macro_hack = settings.is_set(ProcMacroHack); + let closure = quote!(::std::panic::AssertUnwindSafe(|| #block )); + quote!( ::proc_macro_error::entry_point(#closure, #is_proc_macro_hack) ) +} + +fn detect_proc_macro_hack(attrs: &[Attribute]) -> bool { + attrs + .iter() + .any(|attr| attr.path.is_ident("proc_macro_hack")) +} + +fn is_proc_macro(attrs: &[Attribute]) -> bool { + attrs.iter().any(|attr| { + attr.path.is_ident("proc_macro") + || attr.path.is_ident("proc_macro_derive") + || attr.path.is_ident("proc_macro_attribute") + }) +} |