1use darling::ast::NestedMeta;
2use darling::FromMeta;
3use proc_macro::TokenStream as TokenStream1;
4use proc_macro2::Span;
5use proc_macro2::TokenStream;
6use proc_macro_error::{abort, proc_macro_error};
7use syn::spanned::Spanned;
8use syn::*;
9use template_quote::quote;
10
11#[derive(FromMeta, Debug, Clone, PartialEq)]
12struct Arguments {
13 label: Option<String>,
14}
15
16fn check_macro_kind(attrs: &[Attribute]) -> (&'static str, Option<String>) {
17 for attr in attrs {
18 if let Meta::Path(path) = &attr.meta {
19 if path.is_ident("proc_macro") {
20 return ("function", None);
21 } else if path.is_ident("proc_macro_attribute") {
22 return ("attribute", None);
23 }
24 } else if let Meta::List(MetaList { path, tokens, .. }) = &attr.meta {
25 if !path.is_ident("proc_macro_derive") {
26 continue;
27 }
28 if let Ok(ident) = parse2::<Ident>(tokens.clone()) {
29 return ("derive", Some(ident.to_string()));
30 }
31 }
32 }
33 ("unknown", None)
34}
35
36fn inner(args: Arguments, mut input: ItemFn) -> TokenStream {
37 let mut macro_inputs = Vec::new();
38 let (macro_kind, derive_ident) = check_macro_kind(&input.attrs);
39 if let Some(derive_ident) = derive_ident {
40 macro_inputs.push(quote!(#derive_ident.to_string()));
41 }
42 let mut inner_attrs = vec![];
43 let mut outer_attrs = vec![];
44 for attr in &input.attrs {
45 match &attr.meta {
46 Meta::Path(path)
47 | Meta::List(MetaList { path, .. })
48 | Meta::NameValue(MetaNameValue { path, .. })
49 if path.is_ident("proc_macro")
50 || path.is_ident("proc_macro_attribute")
51 || path.is_ident("proc_macro_derive")
52 || path.is_ident("proc_macro_error")
53 || path.is_ident("doc") =>
54 {
55 outer_attrs.push(attr.clone());
56 }
57
58 _ => inner_attrs.push(attr.clone()),
59 }
60 }
61 input.attrs = inner_attrs;
62 for (n, input) in input.sig.inputs.iter_mut().enumerate() {
63 match input {
64 FnArg::Typed(pat_type) => {
65 let ident = match pat_type.pat.as_ref() {
66 Pat::Ident(pat_ident) if &pat_ident.ident != "_" => pat_ident.ident.clone(),
67 _ => {
68 let ident = Ident::new(&format!("__proc_debug_arg_{}", n), pat_type.span());
69 pat_type.pat = Box::new(Pat::Ident(PatIdent {
70 attrs: vec![],
71 by_ref: None,
72 mutability: None,
73 ident: ident.clone(),
74 subpat: None,
75 }));
76 ident
77 }
78 };
79 macro_inputs.push(quote!(#ident.to_string()));
80 }
81 _ => (),
82 }
83 }
84 let (impl_generics, _, where_clause) = input.sig.generics.split_for_impl();
85 quote! {
86 #(#outer_attrs)*
87 #{&input.vis} #{&input.sig.constness} #{&input.sig.asyncness} #{&input.sig.unsafety} #{&input.sig.abi} #{&input.sig.fn_token} #{&input.sig.ident} #impl_generics (
88 #{&input.sig.inputs}
89 #{&input.sig.variadic}
90 ) #{&input.sig.output} #where_clause {
91 #input
92 ::proc_macro::TokenStream::from(
93 ::proc_debug::proc_wrapper(
94 #(if let Some(label) = &args.label) {
95 #label
96 } #(else) {
97 &::std::format!("{}::{}", ::std::module_path!(), #{input.sig.ident.to_string()})
98 },
99 ::std::file!(),
100 ::std::line!() as usize,
101 ::std::module_path!(),
102 #macro_kind,
103 #{input.sig.ident.to_string()},
104 &[ #(for input in ¯o_inputs),{#input} ],
105 || {
106 ::proc_macro2::TokenStream::from(
107 #{&input.sig.ident}(
108 #(for arg in &input.sig.inputs) {
109 #(if let FnArg::Receiver(arg) = arg) {
110 #{&arg.self_token}
111 }
112 #(if let FnArg::Typed(pat_type) = arg) {
113 #{&pat_type.pat}
114 },
115 }
116 #{&input.sig.variadic}
117 )
118 )
119 }
120 )
121 )
122 }
123 }
124}
125
126#[proc_macro_error]
127#[proc_macro_attribute]
128pub fn proc_debug(attr: TokenStream1, input: TokenStream1) -> TokenStream1 {
129 let args = Arguments::from_list(&NestedMeta::parse_meta_list(attr.into()).unwrap()).unwrap();
130 inner(
131 args,
132 parse(input).unwrap_or_else(|_| abort!(Span::call_site(), "Require function")),
133 )
134 .into()
135}