proc_debug_macro/
lib.rs

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 &macro_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}