|
11 | 11 | //! a: String,
|
12 | 12 | //! b: Option<T>,
|
13 | 13 | //!
|
14 |
| -//! #[trace(skip)] // ignore this field for Trace. |
| 14 | +//! #[skip_trace] // ignore this field for Trace. |
15 | 15 | //! c: MyType,
|
16 | 16 | //! }
|
17 | 17 | //!
|
18 | 18 | //! struct MyType;
|
19 | 19 | //! ```
|
20 | 20 | extern crate proc_macro;
|
21 | 21 |
|
22 |
| -use proc_macro::TokenStream; |
23 | 22 | use quote::quote;
|
24 |
| -use quote::ToTokens; |
25 |
| -use syn::Data; |
| 23 | +use syn::Attribute; |
| 24 | +use synstructure::{decl_derive, AddBounds, BindStyle, Structure}; |
26 | 25 |
|
27 |
| -#[proc_macro_derive(Trace, attributes(trace))] |
28 |
| -pub fn gcmodule_trace_derive(input: TokenStream) -> TokenStream { |
29 |
| - let input = syn::parse_macro_input!(input as syn::DeriveInput); |
30 |
| - let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl(); |
31 |
| - let ident = input.ident; |
32 |
| - let mut trace_fn_body = Vec::new(); |
33 |
| - let mut is_type_tracked_fn_body = Vec::new(); |
34 |
| - if !input.attrs.into_iter().any(is_skipped) { |
35 |
| - match input.data { |
36 |
| - Data::Struct(data) => { |
37 |
| - for (i, field) in data.fields.into_iter().enumerate() { |
38 |
| - if field.attrs.into_iter().any(is_skipped) { |
39 |
| - continue; |
40 |
| - } |
41 |
| - let trace_field = match field.ident { |
42 |
| - Some(i) => quote! { |
43 |
| - if gcmodule::DEBUG_ENABLED { |
44 |
| - eprintln!("[gc] Trace({}): visit .{}", stringify!(#ident), stringify!(#i)); |
45 |
| - } |
46 |
| - self.#i.trace(tracer); |
47 |
| - }, |
48 |
| - None => { |
49 |
| - let i = syn::Index::from(i); |
50 |
| - quote! { |
51 |
| - if gcmodule::DEBUG_ENABLED { |
52 |
| - eprintln!("[gc] Trace({}): visit .{}", stringify!(#ident), stringify!(#i)); |
53 |
| - } |
54 |
| - self.#i.trace(tracer); |
55 |
| - } |
56 |
| - } |
57 |
| - }; |
58 |
| - trace_fn_body.push(trace_field); |
59 |
| - let ty = field.ty; |
60 |
| - is_type_tracked_fn_body.push(quote! { |
61 |
| - if <#ty as _gcmodule::Trace>::is_type_tracked() { |
62 |
| - return true; |
63 |
| - } |
64 |
| - }); |
65 |
| - } |
66 |
| - } |
67 |
| - Data::Enum(_) | Data::Union(_) => { |
68 |
| - trace_fn_body.push(quote! { |
69 |
| - compile_error!("enum or union are not supported"); |
70 |
| - }); |
71 |
| - } |
72 |
| - }; |
73 |
| - } |
74 |
| - let generated = quote! { |
75 |
| - const _: () = { |
76 |
| - extern crate gcmodule as _gcmodule; |
77 |
| - impl #impl_generics _gcmodule::Trace for #ident #ty_generics #where_clause { |
78 |
| - fn trace(&self, tracer: &mut _gcmodule::Tracer) { |
79 |
| - #( #trace_fn_body )* |
80 |
| - } |
| 26 | +decl_derive!([Trace, attributes(skip_trace, ignore_tracking, force_tracking)] => derive_trace); |
| 27 | + |
| 28 | +fn has_attr(attrs: &[Attribute], attr: &str) -> bool { |
| 29 | + attrs.iter().any(|a| a.path.is_ident(attr)) |
| 30 | +} |
| 31 | + |
| 32 | +fn derive_trace(mut s: Structure<'_>) -> proc_macro2::TokenStream { |
| 33 | + if has_attr(&s.ast().attrs, "skip_trace") { |
| 34 | + s.filter(|_| false); |
| 35 | + return s.bound_impl( |
| 36 | + quote! {::gcmodule::Trace}, |
| 37 | + quote! { |
| 38 | + fn trace(&self, _tracer: &mut ::gcmodule::Tracer) {} |
81 | 39 | fn is_type_tracked() -> bool {
|
82 |
| - #( #is_type_tracked_fn_body )* |
83 | 40 | false
|
84 | 41 | }
|
85 |
| - } |
86 |
| - }; |
87 |
| - }; |
88 |
| - generated.into() |
89 |
| -} |
| 42 | + }, |
| 43 | + ); |
| 44 | + } |
| 45 | + let force_tracking = has_attr(&s.ast().attrs, "force_tracking"); |
| 46 | + |
| 47 | + s.filter_variants(|f| !has_attr(f.ast().attrs, "skip_trace")); |
| 48 | + s.filter(|f| !has_attr(&f.ast().attrs, "skip_trace")); |
| 49 | + s.add_bounds(AddBounds::Fields); |
| 50 | + s.bind_with(|_| BindStyle::Ref); |
90 | 51 |
|
91 |
| -fn is_skipped(attr: syn::Attribute) -> bool { |
92 |
| - // check if `#[trace(skip)]` exists. |
93 |
| - if attr.path.to_token_stream().to_string() == "trace" { |
94 |
| - for token in attr.tokens { |
95 |
| - if token.to_string() == "(skip)" { |
| 52 | + let trace_body = s.each(|bi| quote!(::gcmodule::Trace::trace(#bi, tracer))); |
| 53 | + |
| 54 | + let is_type_tracked_body = if force_tracking { |
| 55 | + quote! { |
| 56 | + true |
| 57 | + } |
| 58 | + } else { |
| 59 | + s.filter(|f| !has_attr(&f.ast().attrs, "ignore_tracking")); |
| 60 | + let ty = s |
| 61 | + .variants() |
| 62 | + .iter() |
| 63 | + .flat_map(|v| v.bindings().iter()) |
| 64 | + .map(|bi| &bi.ast().ty); |
| 65 | + quote! { |
| 66 | + #( |
| 67 | + if <#ty>::is_type_tracked() { |
96 | 68 | return true;
|
97 | 69 | }
|
| 70 | + )* |
| 71 | + false |
98 | 72 | }
|
99 |
| - } |
100 |
| - false |
| 73 | + }; |
| 74 | + |
| 75 | + s.bound_impl( |
| 76 | + quote! {::gcmodule::Trace}, |
| 77 | + quote! { |
| 78 | + fn trace(&self, tracer: &mut ::gcmodule::Tracer) { |
| 79 | + match *self { #trace_body } |
| 80 | + } |
| 81 | + fn is_type_tracked() -> bool { |
| 82 | + #is_type_tracked_body |
| 83 | + } |
| 84 | + }, |
| 85 | + ) |
101 | 86 | }
|
0 commit comments