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
//! Provides macros for the `callback-trace` and `callback-trace-tls` crate.

extern crate proc_macro;
use proc_macro::TokenStream;
use quote::quote;
use syn::{parse_macro_input, ItemFn, parse_quote, Block, Expr, Token, punctuated::Punctuated};
use syn::parse::Parser;

/// The `#[trace]` macro. Needs to be applied to function definitions,
/// and will invoke callbacks managed by the the `callback-trace`
/// before and after the functions body.
///
/// # Example
/// ```notest
/// use call_trace::trace;
///
/// #[trace]
/// fn foo() {
///     // ...
/// }
/// ```
#[proc_macro_attribute]
pub fn trace(attr: TokenStream, input: TokenStream) -> TokenStream {
    assert!(attr.is_empty());
    let input = parse_macro_input!(input as ItemFn);

    // Hand the output tokens back to the compiler
    TokenStream::from(quote! {
        #[::call_trace::trace_with(::call_trace_tls::TRACE)]
        #input
    })
}

/// The `#[trace_with]` macro. Needs to be applied to function definitions,
/// and will invoke hte `Trace` interface on the user-provided expression.
///
/// # Example
/// ```notest
/// use call_trace::{trace_with, CallContext, Trace};
///
/// impl MyType {
///     #[trace_with(self)]
///     fn foo(&mut self) {
///         // ...
///     }
/// }
/// impl Trace for MyType {
///     fn on_pre(&mut self, ctx: &CallContext) {
///         // ...
///     }
///     fn on_post(&mut self, ctx: &CallContext) {
///         // ...
///     }
/// }
/// ```
#[proc_macro_attribute]
pub fn trace_with(attr: TokenStream, input: TokenStream) -> TokenStream {
    let parser = Punctuated::<Expr, Token![,]>::parse_terminated;
    let mut with_args: Vec<_> = parser.parse(attr).unwrap().into_iter().collect();
    let with: Expr = with_args.remove(0);

    let mut input = parse_macro_input!(input as ItemFn);
    let fn_name = input.ident.clone();
    let inner_block = input.block;
    let retty = input.decl.output.clone();

    let trace_wrapper: Block = parse_quote! {
        {
            let __ctx = ::call_trace::CallContext {
                file: file!(),
                line: line!(),
                fn_name: stringify!(#fn_name),
            };

            ::call_trace::Trace::on_pre(#with, &__ctx);
            let __r = {
                let mut __f = || #retty #inner_block;
                __f()
            };
            ::call_trace::Trace::on_post(#with, &__ctx);

            __r
        }
    };
    input.block = Box::new(trace_wrapper);

    TokenStream::from(quote! { #input })
}