From 5e20a29b4fdc8a2d442d1093681b396dcb4b816b Mon Sep 17 00:00:00 2001 From: Robin Krahl Date: Tue, 7 Jan 2020 11:18:04 +0000 Subject: Add structopt dependency in version 0.3.7 This patch series replaces argparse with structopt in the argument handling code. As a first step, we need structopt as a dependency. Import subrepo structopt/:structopt at efbdda4753592e27bc430fb01f7b9650b2f3174d Import subrepo bitflags/:bitflags at 30668016aca6bd3b02c766e8347e0b4080d4c296 Import subrepo clap/:clap at 784524f7eb193e35f81082cc69454c8c21b948f7 Import subrepo heck/:heck at 093d56fbf001e1506e56dbfa38631d99b1066df1 Import subrepo proc-macro-error/:proc-macro-error at 6c4cfe79a622c5de8ae68557993542be46eacae2 Import subrepo proc-macro2/:proc-macro2 at d5d48eddca4566e5438e8a2cbed4a74e049544de Import subrepo quote/:quote at 727436c6c137b20f0f34dde5d8fda2679b9747ad Import subrepo rustversion/:rustversion at 0c5663313516263059ce9059ef81fc7a1cf655ca Import subrepo syn-mid/:syn-mid at 5d3d85414a9e6674e1857ec22a87b96e04a6851a Import subrepo syn/:syn at e87c27e87f6f4ef8919d0372bdb056d53ef0d8f3 Import subrepo textwrap/:textwrap at abcd618beae3f74841032aa5b53c1086b0a57ca2 Import subrepo unicode-segmentation/:unicode-segmentation at 637c9874c4fe0c205ff27787faf150a40295c6c3 Import subrepo unicode-width/:unicode-width at 3033826f8bf05e82724140a981d5941e48fce393 Import subrepo unicode-xid/:unicode-xid at 4baae9fffb156ba229665b972a9cd5991787ceb7 --- syn/examples/heapsize/Cargo.toml | 2 + syn/examples/heapsize/README.md | 72 ++++++++++++++++++ syn/examples/heapsize/example/Cargo.toml | 9 +++ syn/examples/heapsize/example/src/main.rs | 28 +++++++ syn/examples/heapsize/heapsize/Cargo.toml | 9 +++ syn/examples/heapsize/heapsize/src/lib.rs | 64 ++++++++++++++++ syn/examples/heapsize/heapsize_derive/Cargo.toml | 14 ++++ syn/examples/heapsize/heapsize_derive/src/lib.rs | 96 ++++++++++++++++++++++++ 8 files changed, 294 insertions(+) create mode 100644 syn/examples/heapsize/Cargo.toml create mode 100644 syn/examples/heapsize/README.md create mode 100644 syn/examples/heapsize/example/Cargo.toml create mode 100644 syn/examples/heapsize/example/src/main.rs create mode 100644 syn/examples/heapsize/heapsize/Cargo.toml create mode 100644 syn/examples/heapsize/heapsize/src/lib.rs create mode 100644 syn/examples/heapsize/heapsize_derive/Cargo.toml create mode 100644 syn/examples/heapsize/heapsize_derive/src/lib.rs (limited to 'syn/examples/heapsize') diff --git a/syn/examples/heapsize/Cargo.toml b/syn/examples/heapsize/Cargo.toml new file mode 100644 index 0000000..9b19214 --- /dev/null +++ b/syn/examples/heapsize/Cargo.toml @@ -0,0 +1,2 @@ +[workspace] +members = ["example", "heapsize", "heapsize_derive"] diff --git a/syn/examples/heapsize/README.md b/syn/examples/heapsize/README.md new file mode 100644 index 0000000..e789559 --- /dev/null +++ b/syn/examples/heapsize/README.md @@ -0,0 +1,72 @@ +A derive macro that generates trait impls. + +- [`heapsize/src/lib.rs`](heapsize/src/lib.rs) +- [`heapsize_derive/src/lib.rs`](heapsize_derive/src/lib.rs) +- [`example/src/main.rs`](example/src/main.rs) + +We are deriving the `HeapSize` trait which computes an estimate of the amount of +heap memory owned by a value. + +```rust +pub trait HeapSize { + /// Total number of bytes of heap memory owned by `self`. + fn heap_size_of_children(&self) -> usize; +} +``` + +The derive macro allows users to write `#[derive(HeapSize)]` on data structures +in their program. + +```rust +#[derive(HeapSize)] +struct Demo<'a, T: ?Sized> { + a: Box, + b: u8, + c: &'a str, + d: String, +} +``` + +The trait impl generated by the derive macro here would look like: + +```rust +impl<'a, T: ?Sized + heapsize::HeapSize> heapsize::HeapSize for Demo<'a, T> { + fn heap_size_of_children(&self) -> usize { + 0 + heapsize::HeapSize::heap_size_of_children(&self.a) + + heapsize::HeapSize::heap_size_of_children(&self.b) + + heapsize::HeapSize::heap_size_of_children(&self.c) + + heapsize::HeapSize::heap_size_of_children(&self.d) + } +} +``` + +The implementation of `heapsize_derive` demonstrates some attention to "spans" +of error messages. For each subexpression in the generated code we apply the +span of the input fragment under which we would want to trigger a compiler error +if the subexpression fails to compile. In this example, each recursive call to +`heap_size_of_children` is associated with the span of the corresponding struct +field. Thus we get errors in the right place if any of the field types do not +implement the `HeapSize` trait. + +``` +error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied + --> src/main.rs:7:5 + | +7 | bad: std::thread::Thread, + | ^^^ the trait `HeapSize` is not implemented for `std::thread::Thread` +``` + +Some unstable APIs in the `proc-macro2` crate let us improve this further by +joining together the span of the field name and the field type. There is no +difference in our code — everything is as shown in this directory — +but building the example crate with `cargo build` shows errors like the one +above and building with `RUSTFLAGS='--cfg procmacro2_semver_exempt' cargo build` +is able to show errors like the following. + +``` +error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied + --> src/main.rs:7:5 + | +7 | bad: std::thread::Thread, + | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HeapSize` is not implemented for `std::thread::Thread` +``` diff --git a/syn/examples/heapsize/example/Cargo.toml b/syn/examples/heapsize/example/Cargo.toml new file mode 100644 index 0000000..85c7699 --- /dev/null +++ b/syn/examples/heapsize/example/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "heapsize_example" +version = "0.0.0" +authors = ["David Tolnay "] +edition = "2018" +publish = false + +[dependencies] +heapsize = { path = "../heapsize" } diff --git a/syn/examples/heapsize/example/src/main.rs b/syn/examples/heapsize/example/src/main.rs new file mode 100644 index 0000000..9332b11 --- /dev/null +++ b/syn/examples/heapsize/example/src/main.rs @@ -0,0 +1,28 @@ +use heapsize::HeapSize; + +#[derive(HeapSize)] +struct Demo<'a, T: ?Sized> { + a: Box, + b: u8, + c: &'a str, + d: String, +} + +fn main() { + let demo = Demo { + a: b"bytestring".to_vec().into_boxed_slice(), + b: 255, + c: "&'static str", + d: "String".to_owned(), + }; + + // 10 + 0 + 0 + 6 = 16 + println!( + "heap size = {} + {} + {} + {} = {}", + demo.a.heap_size_of_children(), + demo.b.heap_size_of_children(), + demo.c.heap_size_of_children(), + demo.d.heap_size_of_children(), + demo.heap_size_of_children() + ); +} diff --git a/syn/examples/heapsize/heapsize/Cargo.toml b/syn/examples/heapsize/heapsize/Cargo.toml new file mode 100644 index 0000000..27bb954 --- /dev/null +++ b/syn/examples/heapsize/heapsize/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "heapsize" +version = "0.0.0" +authors = ["David Tolnay "] +edition = "2018" +publish = false + +[dependencies] +heapsize_derive = { path = "../heapsize_derive" } diff --git a/syn/examples/heapsize/heapsize/src/lib.rs b/syn/examples/heapsize/heapsize/src/lib.rs new file mode 100644 index 0000000..30bb6d6 --- /dev/null +++ b/syn/examples/heapsize/heapsize/src/lib.rs @@ -0,0 +1,64 @@ +use std::mem; + +pub use heapsize_derive::*; + +pub trait HeapSize { + /// Total number of bytes of heap memory owned by `self`. + /// + /// Does not include the size of `self` itself, which may or may not be on + /// the heap. Includes only children of `self`, meaning things pointed to by + /// `self`. + fn heap_size_of_children(&self) -> usize; +} + +// +// In a real version of this library there would be lots more impls here, but +// here are some interesting ones. +// + +impl HeapSize for u8 { + /// A `u8` does not own any heap memory. + fn heap_size_of_children(&self) -> usize { + 0 + } +} + +impl HeapSize for String { + /// A `String` owns enough heap memory to hold its reserved capacity. + fn heap_size_of_children(&self) -> usize { + self.capacity() + } +} + +impl HeapSize for Box +where + T: ?Sized + HeapSize, +{ + /// A `Box` owns however much heap memory was allocated to hold the value of + /// type `T` that we placed on the heap, plus transitively however much `T` + /// itself owns. + fn heap_size_of_children(&self) -> usize { + mem::size_of_val(&**self) + (**self).heap_size_of_children() + } +} + +impl HeapSize for [T] +where + T: HeapSize, +{ + /// Sum of heap memory owned by each element of a dynamically sized slice of + /// `T`. + fn heap_size_of_children(&self) -> usize { + self.iter().map(HeapSize::heap_size_of_children).sum() + } +} + +impl<'a, T> HeapSize for &'a T +where + T: ?Sized, +{ + /// A shared reference does not own heap memory. + fn heap_size_of_children(&self) -> usize { + 0 + } +} 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 "] +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!(), + } +} -- cgit v1.2.1