call_trace_macro/
lib.rs

1//! Provides the `#[trace_with]` attribute for the `callback-trace` crate.
2
3extern crate proc_macro;
4use proc_macro::TokenStream;
5use quote::quote;
6use syn::{parse_macro_input, ItemFn, parse_quote, Block, Expr, Token, punctuated::Punctuated};
7use syn::parse::Parser;
8
9/// The `#[trace_with]` macro. Needs to be applied to function definitions,
10/// and will invoke hte `Trace` interface on the user-provided expression.
11///
12/// # Example
13/// ```notest
14/// use call_trace::{trace_with, CallContext, Trace};
15///
16/// impl MyType {
17///     #[trace_with(self)]
18///     fn foo(&mut self) {
19///         // ...
20///     }
21/// }
22/// impl Trace for MyType {
23///     fn on_pre(&mut self, ctx: &CallContext) {
24///         // ...
25///     }
26///     fn on_post(&mut self, ctx: &CallContext) {
27///         // ...
28///     }
29/// }
30/// ```
31#[proc_macro_attribute]
32pub fn trace_with(attr: TokenStream, input: TokenStream) -> TokenStream {
33    let parser = Punctuated::<Expr, Token![,]>::parse_terminated;
34    let mut with_args: Vec<_> = parser.parse(attr).unwrap().into_iter().collect();
35    let with: Expr = with_args.remove(0);
36
37    let mut input = parse_macro_input!(input as ItemFn);
38    let fn_name = input.ident.clone();
39    let inner_block = input.block;
40    let retty = input.decl.output.clone();
41
42    let trace_wrapper: Block = parse_quote! {
43        {
44            use ::call_trace::Trace as _;
45            let __ctx = ::call_trace::CallContext {
46                file: file!(),
47                line: line!(),
48                fn_name: stringify!(#fn_name),
49            };
50
51            #with.on_pre(&__ctx);
52            let __r = {
53                let mut __f = || #retty #inner_block;
54                __f()
55            };
56            #with.on_post(&__ctx);
57
58            __r
59        }
60    };
61    input.block = Box::new(trace_wrapper);
62
63    TokenStream::from(quote! { #input })
64}