graphannis_malloc_size_of_derive/
lib.rs1extern crate quote;
11#[macro_use]
12extern crate syn;
13#[macro_use]
14extern crate synstructure;
15
16extern crate proc_macro2;
17
18#[cfg(not(test))]
19decl_derive!([MallocSizeOf, attributes(ignore_malloc_size_of, with_malloc_size_of_func)] => malloc_size_of_derive);
20
21fn malloc_size_of_derive(s: synstructure::Structure) -> proc_macro2::TokenStream {
22 let match_body = s.each(|binding| {
23 let ignore = binding
24 .ast()
25 .attrs
26 .iter()
27 .any(|attr| match attr.interpret_meta().unwrap() {
28 syn::Meta::Word(ref ident) | syn::Meta::List(syn::MetaList { ref ident, .. })
29 if ident == "ignore_malloc_size_of" =>
30 {
31 panic!(
32 "#[ignore_malloc_size_of] should have an explanation, \
33 e.g. #[ignore_malloc_size_of = \"because reasons\"]"
34 );
35 }
36 syn::Meta::NameValue(syn::MetaNameValue { ref ident, .. })
37 if ident == "ignore_malloc_size_of" =>
38 {
39 true
40 },
41 _ => false,
42 });
43 let with_function : Option<syn::Path> = binding
44 .ast()
45 .attrs
46 .iter()
47 .filter_map(|attr| match attr.interpret_meta().unwrap() {
48 syn::Meta::Word(ref ident) | syn::Meta::List(syn::MetaList { ref ident, .. })
49 if ident == "with_malloc_size_of_func" =>
50 {
51 panic!(
52 "#[with_malloc_size_of_func] must have a function name as argument, \
53 e.g. #[with_malloc_size_of_func = \"util::measure_btreemap\"]"
54 );
55 }
56 syn::Meta::NameValue(syn::MetaNameValue { ref ident, ref lit, .. })
57 if ident == "with_malloc_size_of_func" =>
58 {
59 if let syn::Lit::Str(ref lit) = lit {
60 let as_path = lit.parse::<syn::Path>();
62 match as_path {
63 Ok(as_path) => Some(as_path),
64 Err(_err) => {
65 panic!("The argument of #[with_malloc_size_of_func = \"...\"] must be the path to a function which is in scope.");
66 }
67 }
68 } else {
69 panic!(
70 "#[with_malloc_size_of_func] must have a function name as argument and this must be a string, \
71 e.g. #[with_malloc_size_of_func = \"util::measure_btreemap\"]"
72 );
73 }
74 },
75 _ => None,
76 })
77 .next();
78 if ignore {
79 None
80 } else if let Some(with_function) = with_function {
81 Some(quote! {
82 sum += #with_function(#binding, ops);
83 })
84 } else if let syn::Type::Array(..) = binding.ast().ty {
85 Some(quote! {
86 for item in #binding.iter() {
87 sum += ::malloc_size_of::MallocSizeOf::size_of(item, ops);
88 }
89 })
90 } else {
91 Some(quote! {
92 sum += ::malloc_size_of::MallocSizeOf::size_of(#binding, ops);
93 })
94 }
95 });
96
97 let ast = s.ast();
98 let name = &ast.ident;
99 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
100 let mut where_clause = where_clause.unwrap_or(&parse_quote!(where)).clone();
101 for param in ast.generics.type_params() {
102 let ident = ¶m.ident;
103 where_clause
104 .predicates
105 .push(parse_quote!(#ident: ::malloc_size_of::MallocSizeOf));
106 }
107
108 let tokens = quote! {
109 impl #impl_generics ::malloc_size_of::MallocSizeOf for #name #ty_generics #where_clause {
110 #[inline]
111 #[allow(unused_variables, unused_mut, unreachable_code)]
112 fn size_of(&self, ops: &mut ::malloc_size_of::MallocSizeOfOps) -> usize {
113 let mut sum = 0;
114 match *self {
115 #match_body
116 }
117 sum
118 }
119 }
120 };
121
122 tokens
123}
124
125#[test]
126fn test_struct() {
127 let source = syn::parse_str(
128 "struct Foo<T> { bar: Bar, baz: T, #[ignore_malloc_size_of = \"\"] z: Arc<T> }",
129 ).unwrap();
130 let source = synstructure::Structure::new(&source);
131
132 let expanded = malloc_size_of_derive(source).to_string();
133 let mut no_space = expanded.replace(" ", "");
134 macro_rules! match_count {
135 ($e: expr, $count: expr) => {
136 assert_eq!(
137 no_space.matches(&$e.replace(" ", "")).count(),
138 $count,
139 "counting occurences of {:?} in {:?} (whitespace-insensitive)",
140 $e,
141 expanded
142 )
143 };
144 }
145 match_count!("struct", 0);
146 match_count!("ignore_malloc_size_of", 0);
147 match_count!("impl<T> ::malloc_size_of::MallocSizeOf for Foo<T> where T: ::malloc_size_of::MallocSizeOf {", 1);
148 match_count!("sum += ::malloc_size_of::MallocSizeOf::size_of(", 2);
149
150 let source = syn::parse_str("struct Bar([Baz; 3]);").unwrap();
151 let source = synstructure::Structure::new(&source);
152 let expanded = malloc_size_of_derive(source).to_string();
153 no_space = expanded.replace(" ", "");
154 match_count!("for item in", 1);
155}
156
157#[should_panic(expected = "should have an explanation")]
158#[test]
159fn test_no_reason() {
160 let input = syn::parse_str("struct A { #[ignore_malloc_size_of] b: C }").unwrap();
161 malloc_size_of_derive(synstructure::Structure::new(&input));
162}
163
164
165#[test]
166fn test_with_function() {
167 let source = syn::parse_str(
168 "struct Foo { bar: Bar, #[with_malloc_size_of_func = \"col::anothermod::custom_func\"] baz: Baz,
169 #[with_malloc_size_of_func = \"my_func\"]
170 baz2: Baz}",
171 ).unwrap();
172
173 let source = synstructure::Structure::new(&source);
174
175 let expanded = malloc_size_of_derive(source).to_string();
176
177 let no_space = expanded.replace(" ", "");
178 macro_rules! match_count {
179 ($e: expr, $count: expr) => {
180 assert_eq!(
181 no_space.matches(&$e.replace(" ", "")).count(),
182 $count,
183 "counting occurences of {:?} in {:?} (whitespace-insensitive)",
184 $e,
185 expanded
186 )
187 };
188 }
189 match_count!("struct", 0);
190 match_count!("ignore_malloc_size_of", 0);
191 match_count!("impl::malloc_size_of::MallocSizeOf for Foo {", 1);
192 match_count!("sum += ::malloc_size_of::MallocSizeOf::size_of(", 1);
193 match_count!("sum += col::anothermod::custom_func(", 1);
194 match_count!("sum += my_func(", 1);
195}