1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
extern crate proc_macro;
use proc_macro2::{TokenStream, TokenTree};
#[doc(hidden)]
pub use quote::quote;
pub fn fix(
attr: proc_macro::TokenStream,
item: proc_macro::TokenStream,
target: TokenStream,
imports: &[TokenStream],
passthru: Option<TokenStream>,
) -> proc_macro::TokenStream {
fix_(attr.into(), item.into(), target, imports, passthru)
.unwrap_or_else(|err| err.to_compile_error())
.into()
}
fn fix_(
attr: TokenStream,
item: TokenStream,
target: TokenStream,
imports: &[TokenStream],
passthru: Option<TokenStream>,
) -> syn::Result<TokenStream> {
let mut item = syn::parse2::<syn::DeriveInput>(item)?;
let vis = item.vis.clone();
item.vis = syn::parse2(quote!(pub)).unwrap();
let ident = &item.ident;
let unused = item.attrs.iter().any(|attr| {
attr.path.is_ident("allow") && {
for token in attr.tokens.clone() {
if let TokenTree::Group(group) = token {
for token in group.stream() {
if let TokenTree::Ident(ident) = token {
if ident == "unused"
|| ident == "unused_imports"
|| ident == "dead_code"
{
return true;
}
}
}
}
}
false
}
});
let unused = if unused {
quote!(#[allow(unused_imports)])
} else {
quote!()
};
let mod_name = quote::format_ident!("__qualify_derive_{}", ident);
let passthru = passthru.and_then(|attr_name| {
if attr.is_empty() {
None
} else {
Some(quote!(#[#attr_name(#attr)]))
}
});
let output = quote! {
#[allow(non_snake_case)]
mod #mod_name {
use super::*;
#(
#[allow(unused_imports)]
use #imports;
)*
#[derive(#target)]
#passthru
#item
}
#unused
#vis use #mod_name::#ident;
};
Ok(output)
}
#[macro_export]
macro_rules! declare {
($(#[$meta:meta])* $name:ident derives $target:ty; $(use $imports:ty;)*) => {
$crate::declare!(@INTERNAL $(#[$meta])* $name; $target; $($imports),*; None);
};
($(#[$meta:meta])* $name:ident derives $target:ty; $(use $imports:ty;)* attr $attr:ident $(;)?) => {
$crate::declare!(@INTERNAL $(#[$meta])* $name; $target; $($imports),*; Some($crate::quote!($attr)));
};
(@INTERNAL $(#[$meta:meta])* $name:ident; $target:ty; $($imports:ty),*; $passthru:expr) => {
#[proc_macro_attribute]
$(#[$meta])*
pub fn $name(attr: ::proc_macro::TokenStream, item: ::proc_macro::TokenStream) -> ::proc_macro::TokenStream {
use $crate::quote;
$crate::fix(attr, item, quote!($target), &[$(quote!($imports)),*], $passthru)
}
};
}