diff options
Diffstat (limited to 'syn/examples/heapsize/heapsize_derive')
-rw-r--r-- | syn/examples/heapsize/heapsize_derive/Cargo.toml | 14 | ||||
-rw-r--r-- | syn/examples/heapsize/heapsize_derive/src/lib.rs | 96 |
2 files changed, 110 insertions, 0 deletions
diff --git a/syn/examples/heapsize/heapsize_derive/Cargo.toml b/syn/examples/heapsize/heapsize_derive/Cargo.toml new file mode 100644 index 0000000..f4357b9 --- /dev/null +++ b/syn/examples/heapsize/heapsize_derive/Cargo.toml @@ -0,0 +1,14 @@ +[package] +name = "heapsize_derive" +version = "0.0.0" +authors = ["David Tolnay <dtolnay@gmail.com>"] +edition = "2018" +publish = false + +[lib] +proc-macro = true + +[dependencies] +proc-macro2 = "1.0" +quote = "1.0" +syn = { path = "../../.." } diff --git a/syn/examples/heapsize/heapsize_derive/src/lib.rs b/syn/examples/heapsize/heapsize_derive/src/lib.rs new file mode 100644 index 0000000..9176b29 --- /dev/null +++ b/syn/examples/heapsize/heapsize_derive/src/lib.rs @@ -0,0 +1,96 @@ +extern crate proc_macro; + +use proc_macro2::TokenStream; +use quote::{quote, quote_spanned}; +use syn::spanned::Spanned; +use syn::{parse_macro_input, parse_quote, Data, DeriveInput, Fields, GenericParam, Generics, Index}; + +#[proc_macro_derive(HeapSize)] +pub fn derive_heap_size(input: proc_macro::TokenStream) -> proc_macro::TokenStream { + // Parse the input tokens into a syntax tree. + let input = parse_macro_input!(input as DeriveInput); + + // Used in the quasi-quotation below as `#name`. + let name = input.ident; + + // Add a bound `T: HeapSize` to every type parameter T. + let generics = add_trait_bounds(input.generics); + let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); + + // Generate an expression to sum up the heap size of each field. + let sum = heap_size_sum(&input.data); + + let expanded = quote! { + // The generated impl. + impl #impl_generics heapsize::HeapSize for #name #ty_generics #where_clause { + fn heap_size_of_children(&self) -> usize { + #sum + } + } + }; + + // Hand the output tokens back to the compiler. + proc_macro::TokenStream::from(expanded) +} + +// Add a bound `T: HeapSize` to every type parameter T. +fn add_trait_bounds(mut generics: Generics) -> Generics { + for param in &mut generics.params { + if let GenericParam::Type(ref mut type_param) = *param { + type_param.bounds.push(parse_quote!(heapsize::HeapSize)); + } + } + generics +} + +// Generate an expression to sum up the heap size of each field. +fn heap_size_sum(data: &Data) -> TokenStream { + match *data { + Data::Struct(ref data) => { + match data.fields { + Fields::Named(ref fields) => { + // Expands to an expression like + // + // 0 + self.x.heap_size() + self.y.heap_size() + self.z.heap_size() + // + // but using fully qualified function call syntax. + // + // We take some care to use the span of each `syn::Field` as + // the span of the corresponding `heap_size_of_children` + // call. This way if one of the field types does not + // implement `HeapSize` then the compiler's error message + // underlines which field it is. An example is shown in the + // readme of the parent directory. + let recurse = fields.named.iter().map(|f| { + let name = &f.ident; + quote_spanned! {f.span()=> + heapsize::HeapSize::heap_size_of_children(&self.#name) + } + }); + quote! { + 0 #(+ #recurse)* + } + } + Fields::Unnamed(ref fields) => { + // Expands to an expression like + // + // 0 + self.0.heap_size() + self.1.heap_size() + self.2.heap_size() + let recurse = fields.unnamed.iter().enumerate().map(|(i, f)| { + let index = Index::from(i); + quote_spanned! {f.span()=> + heapsize::HeapSize::heap_size_of_children(&self.#index) + } + }); + quote! { + 0 #(+ #recurse)* + } + } + Fields::Unit => { + // Unit structs cannot own more than 0 bytes of heap memory. + quote!(0) + } + } + } + Data::Enum(_) | Data::Union(_) => unimplemented!(), + } +} |