Wrap up heapsize2 example
diff --git a/examples/heapsize/heapsize_derive/src/lib.rs b/examples/heapsize/heapsize_derive/src/lib.rs
index 9eb492e..acf1e27 100644
--- a/examples/heapsize/heapsize_derive/src/lib.rs
+++ b/examples/heapsize/heapsize_derive/src/lib.rs
@@ -55,7 +55,7 @@
Fields::Named(ref fields) => {
// Expands to an expression like
//
- // 0 + self.x.heap_size + self.y.heap_size() + self.z.heap_size()
+ // 0 + self.x.heap_size() + self.y.heap_size() + self.z.heap_size()
//
// but using fully qualified function call syntax.
let fnames = fields.named.iter().map(|f| f.ident);
@@ -68,7 +68,7 @@
Fields::Unnamed(ref fields) => {
// Expands to an expression like
//
- // 0 + self.0.heap_size + self.1.heap_size() + self.2.heap_size()
+ // 0 + self.0.heap_size() + self.1.heap_size() + self.2.heap_size()
let indices = 0..fields.unnamed.len();
quote! {
0 #(
diff --git a/examples/heapsize2/example/src/main.rs b/examples/heapsize2/example/src/main.rs
index 710bab0..4e7d478 100644
--- a/examples/heapsize2/example/src/main.rs
+++ b/examples/heapsize2/example/src/main.rs
@@ -1,12 +1,55 @@
#[macro_use]
extern crate heapsize_derive;
+// Demonstrate that hygiene is working correctly by having no `extern crate
+// heapsize` in scope here. The macro-generated impl is not for something like
+// `::heapsize::HeapSize` like is common in Macros 1.1. It is for the `HeapSize`
+// trait brought into scope by the procedural macro definition.
#[derive(HeapSize)]
struct Demo<'a, T: ?Sized> {
a: Box<T>,
b: u8,
c: &'a str,
- d: String,
+ d: HeapSize,
}
-fn main() {}
+// This derive is going to generate `impl HeapSize for HeapSize` which is fine
+// because the two `HeapSize` tokens have different hygiene context. The first
+// one resolves to the `HeapSize` trait brough into scop eby the procedural
+// macro definition, and the second one refers to this struct.
+//
+// Also demonstrate that even though both `impl HeapSize for Demo` and `impl
+// HeapSize for HeapSize` spit out a `mod scope`, they do not conflict because
+// of hygiene.
+#[derive(HeapSize)]
+struct HeapSize {
+ e: String,
+}
+
+// Try to trip up the custom derive by having an inherent method with the same
+// name as the trait method. The `impl HeapSize for Demo` is going to generate
+// an expression that calls `self.d.heap_size_of_children()`. Ordinarily
+// inherent methods take precedence over trait methods, so an unhygienic macro
+// implementation would resolve to this method. Instead the call in the
+// generated code resolves correctly to the `HeapSize` trait method because that
+// is what is in scope within the macro definition.
+impl HeapSize {
+ #[allow(dead_code)]
+ fn heap_size_of_children(&self) -> usize {
+ unimplemented!()
+ }
+}
+
+fn main() {
+ extern crate heapsize;
+
+ let demo = Demo {
+ a: b"bytestring".to_vec().into_boxed_slice(),
+ b: 255,
+ c: "&'static str",
+ d: HeapSize {
+ e: "String".to_owned(),
+ },
+ };
+ println!("heap size = {}", heapsize::HeapSize::heap_size_of_children(&demo));
+}
diff --git a/examples/heapsize2/heapsize_derive/src/lib.rs b/examples/heapsize2/heapsize_derive/src/lib.rs
index 818bd76..3389b61 100644
--- a/examples/heapsize2/heapsize_derive/src/lib.rs
+++ b/examples/heapsize2/heapsize_derive/src/lib.rs
@@ -1,30 +1,102 @@
extern crate proc_macro;
-use proc_macro::TokenStream;
-
extern crate syn;
-use syn::DeriveInput;
#[macro_use]
extern crate quote;
+use proc_macro::TokenStream;
+use syn::{DeriveInput, Data, Fields, Generics, GenericParam};
+use quote::Tokens;
+
#[proc_macro_derive(HeapSize)]
pub fn derive_heap_size(input: TokenStream) -> TokenStream {
+ // Parse the input tokens into a syntax tree.
let input: DeriveInput = syn::parse(input).unwrap();
+
+ // Used in the quasi-quotation below as `#name`.
let name = input.ident;
- let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
+
+ // 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! {
mod scope {
extern crate heapsize;
use self::heapsize::HeapSize;
+ // The generated impl. Okay to use `HeapSize` unqualified here, it
+ // is guaranteed to resolve to the import on the previous line. This
+ // works even in edge cases like the user's struct having the name
+ // `HeapSize` as demonstrated in main.rs, in which case the
+ // generated code looks like `impl HeapSize for HeapSize`.
impl #impl_generics HeapSize for #name #ty_generics #where_clause {
fn heap_size_of_children(&self) -> usize {
- 0
+ #sum
}
}
}
};
+ // Hand the output tokens back to the compiler.
expanded.into()
}
+
+// 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 {
+ let bound = syn::parse(quote!(HeapSize).into()).unwrap();
+ type_param.bounds.push(bound);
+ }
+ }
+ generics
+}
+
+// Generate an expression to sum up the heap size of each field.
+fn heap_size_sum(data: &Data) -> Tokens {
+ 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()
+ //
+ // Does not need to use fully qualified function call syntax
+ // like `::heapsize::HeapSize::heap_size_of_children(&...)`.
+ // Our procedural macro places the `HeapSize` trait in scope
+ // within the generated code so this is guaranteed to
+ // resolve to the right trait method, even if one of these
+ // fields has an inherent method with a conflicting name as
+ // demonstrated in main.rs.
+ let fnames = fields.named.iter().map(|f| f.ident);
+ quote! {
+ 0 #(
+ + self.#fnames.heap_size_of_children()
+ )*
+ }
+ }
+ Fields::Unnamed(ref fields) => {
+ // Expands to an expression like
+ //
+ // 0 + self.0.heap_size() + self.1.heap_size() + self.2.heap_size()
+ let indices = 0..fields.unnamed.len();
+ quote! {
+ 0 #(
+ + self.#indices.heap_size_of_children()
+ )*
+ }
+ }
+ Fields::Unit => {
+ // Unit structs cannot own more than 0 bytes of heap memory.
+ quote!(0)
+ }
+ }
+ }
+ Data::Enum(_) | Data::Union(_) => unimplemented!(),
+ }
+}