diff options
Diffstat (limited to 'syn/examples')
24 files changed, 0 insertions, 1019 deletions
diff --git a/syn/examples/README.md b/syn/examples/README.md deleted file mode 100644 index fdd69d6..0000000 --- a/syn/examples/README.md +++ /dev/null @@ -1,19 +0,0 @@ -### [`dump-syntax`](dump-syntax) - -Little utility to parse a Rust source file into a `syn::File` and print out a -debug representation of the syntax tree. - -### [`heapsize`](heapsize) - -An example implementation of a derive macro that generates trait impls. - -### [`lazy-static`](lazy-static) - -An example of parsing a custom syntax within a `functionlike!(...)` procedural -macro. Demonstrates how to trigger custom warnings and error messages on -individual tokens of the input. - -### [`trace-var`](trace-var) - -An attribute procedural macro that uses a syntax tree traversal to transform -certain syntax tree nodes in a function body. diff --git a/syn/examples/dump-syntax/Cargo.toml b/syn/examples/dump-syntax/Cargo.toml deleted file mode 100644 index 0bc9f62..0000000 --- a/syn/examples/dump-syntax/Cargo.toml +++ /dev/null @@ -1,17 +0,0 @@ -[package] -name = "dump-syntax" -version = "0.0.0" -authors = ["David Tolnay <dtolnay@gmail.com>"] -edition = "2018" -publish = false - -[dependencies] -colored = "1.7" -proc-macro2 = { version = "1.0", features = ["span-locations"] } - -[dependencies.syn] -path = "../.." -default-features = false -features = ["parsing", "full", "extra-traits"] - -[workspace] diff --git a/syn/examples/dump-syntax/README.md b/syn/examples/dump-syntax/README.md deleted file mode 100644 index 37c84d8..0000000 --- a/syn/examples/dump-syntax/README.md +++ /dev/null @@ -1,28 +0,0 @@ -Parse a Rust source file into a `syn::File` and print out a debug representation -of the syntax tree. - -Use the following command from this directory to test this program by running it -on its own source code: - -``` -cargo run -- src/main.rs -``` - -The output will begin with: - -``` -File { - shebang: None, - attrs: [ - Attribute { - pound_token: Pound, - style: Inner( - Bang - ), - bracket_token: Bracket, - path: Path { - leading_colon: None, - segments: [ - ... -} -``` diff --git a/syn/examples/dump-syntax/src/main.rs b/syn/examples/dump-syntax/src/main.rs deleted file mode 100644 index 240b7a2..0000000 --- a/syn/examples/dump-syntax/src/main.rs +++ /dev/null @@ -1,149 +0,0 @@ -//! Parse a Rust source file into a `syn::File` and print out a debug -//! representation of the syntax tree. -//! -//! Use the following command from this directory to test this program by -//! running it on its own source code: -//! -//! cargo run -- src/main.rs -//! -//! The output will begin with: -//! -//! File { -//! shebang: None, -//! attrs: [ -//! Attribute { -//! pound_token: Pound, -//! style: Inner( -//! ... -//! } - -use std::borrow::Cow; -use std::env; -use std::ffi::OsStr; -use std::fmt::{self, Display}; -use std::fs; -use std::io::{self, Write}; -use std::path::{Path, PathBuf}; -use std::process; - -use colored::Colorize; - -enum Error { - IncorrectUsage, - ReadFile(io::Error), - ParseFile { - error: syn::Error, - filepath: PathBuf, - source_code: String, - }, -} - -impl Display for Error { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - use self::Error::*; - - match self { - IncorrectUsage => write!(f, "Usage: dump-syntax path/to/filename.rs"), - ReadFile(error) => write!(f, "Unable to read file: {}", error), - ParseFile { - error, - filepath, - source_code, - } => render_location(f, error, filepath, source_code), - } - } -} - -fn main() { - if let Err(error) = try_main() { - let _ = writeln!(io::stderr(), "{}", error); - process::exit(1); - } -} - -fn try_main() -> Result<(), Error> { - let mut args = env::args_os(); - let _ = args.next(); // executable name - - let filepath = match (args.next(), args.next()) { - (Some(arg), None) => PathBuf::from(arg), - _ => return Err(Error::IncorrectUsage), - }; - - let code = fs::read_to_string(&filepath).map_err(Error::ReadFile)?; - let syntax = syn::parse_file(&code).map_err({ - |error| Error::ParseFile { - error, - filepath, - source_code: code, - } - })?; - println!("{:#?}", syntax); - - Ok(()) -} - -// Render a rustc-style error message, including colors. -// -// error: Syn unable to parse file -// --> main.rs:40:17 -// | -// 40 | fn fmt(&self formatter: &mut fmt::Formatter) -> fmt::Result { -// | ^^^^^^^^^ expected `,` -// -fn render_location( - formatter: &mut fmt::Formatter, - err: &syn::Error, - filepath: &Path, - code: &str, -) -> fmt::Result { - let start = err.span().start(); - let mut end = err.span().end(); - - if start.line == end.line && start.column == end.column { - return render_fallback(formatter, err); - } - - let code_line = match code.lines().nth(start.line - 1) { - Some(line) => line, - None => return render_fallback(formatter, err), - }; - - if end.line > start.line { - end.line = start.line; - end.column = code_line.len(); - } - - let filename = filepath - .file_name() - .map(OsStr::to_string_lossy) - .unwrap_or(Cow::Borrowed("main.rs")); - - write!( - formatter, - "\n\ - {error}{header}\n\ - {indent}{arrow} {filename}:{linenum}:{colnum}\n\ - {indent} {pipe}\n\ - {label} {pipe} {code}\n\ - {indent} {pipe} {offset}{underline} {message}\n\ - ", - error = "error".red().bold(), - header = ": Syn unable to parse file".bold(), - indent = " ".repeat(start.line.to_string().len()), - arrow = "-->".blue().bold(), - filename = filename, - linenum = start.line, - colnum = start.column, - pipe = "|".blue().bold(), - label = start.line.to_string().blue().bold(), - code = code_line.trim_end(), - offset = " ".repeat(start.column), - underline = "^".repeat(end.column - start.column).red().bold(), - message = err.to_string().red(), - ) -} - -fn render_fallback(formatter: &mut fmt::Formatter, err: &syn::Error) -> fmt::Result { - write!(formatter, "Unable to parse file: {}", err) -} diff --git a/syn/examples/heapsize/Cargo.toml b/syn/examples/heapsize/Cargo.toml deleted file mode 100644 index 9b19214..0000000 --- a/syn/examples/heapsize/Cargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[workspace] -members = ["example", "heapsize", "heapsize_derive"] diff --git a/syn/examples/heapsize/README.md b/syn/examples/heapsize/README.md deleted file mode 100644 index e789559..0000000 --- a/syn/examples/heapsize/README.md +++ /dev/null @@ -1,72 +0,0 @@ -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<T>, - 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 deleted file mode 100644 index 85c7699..0000000 --- a/syn/examples/heapsize/example/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "heapsize_example" -version = "0.0.0" -authors = ["David Tolnay <dtolnay@gmail.com>"] -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 deleted file mode 100644 index 9332b11..0000000 --- a/syn/examples/heapsize/example/src/main.rs +++ /dev/null @@ -1,28 +0,0 @@ -use heapsize::HeapSize; - -#[derive(HeapSize)] -struct Demo<'a, T: ?Sized> { - a: Box<T>, - 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 deleted file mode 100644 index 27bb954..0000000 --- a/syn/examples/heapsize/heapsize/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "heapsize" -version = "0.0.0" -authors = ["David Tolnay <dtolnay@gmail.com>"] -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 deleted file mode 100644 index 30bb6d6..0000000 --- a/syn/examples/heapsize/heapsize/src/lib.rs +++ /dev/null @@ -1,64 +0,0 @@ -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<T> HeapSize for Box<T> -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<T> 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 deleted file mode 100644 index f4357b9..0000000 --- a/syn/examples/heapsize/heapsize_derive/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[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 deleted file mode 100644 index 9176b29..0000000 --- a/syn/examples/heapsize/heapsize_derive/src/lib.rs +++ /dev/null @@ -1,96 +0,0 @@ -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!(), - } -} diff --git a/syn/examples/lazy-static/Cargo.toml b/syn/examples/lazy-static/Cargo.toml deleted file mode 100644 index 586e547..0000000 --- a/syn/examples/lazy-static/Cargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[workspace] -members = ["example", "lazy-static"] diff --git a/syn/examples/lazy-static/README.md b/syn/examples/lazy-static/README.md deleted file mode 100644 index bc64585..0000000 --- a/syn/examples/lazy-static/README.md +++ /dev/null @@ -1,42 +0,0 @@ -An example of parsing a custom syntax within a `functionlike!(...)` procedural -macro. Demonstrates how to trigger custom warnings and error messages on -individual tokens of the input. - -- [`lazy-static/src/lib.rs`](lazy-static/src/lib.rs) -- [`example/src/main.rs`](example/src/main.rs) - -The library implements a `lazy_static!` macro similar to the one from the real -[`lazy_static`](https://docs.rs/lazy_static/1.0.0/lazy_static/) crate on -crates.io. - -```rust -lazy_static! { - static ref USERNAME: Regex = Regex::new("^[a-z0-9_-]{3,16}$").unwrap(); -} -``` - -Compile and run the example by doing `cargo run` in the directory of the -`example` crate. - -The implementation shows how to trigger custom warnings and error messages on -the macro input. For example if you try adding an uncreatively named `FOO` lazy -static, the macro will scold you with the following warning. - -``` -warning: come on, pick a more creative name - --> src/main.rs:10:16 - | -10 | static ref FOO: String = "lazy_static".to_owned(); - | ^^^ -``` - -And if you try to lazily initialize `() = ()`, the macro will outright refuse to -compile it for you. - -``` -error: I can't think of a legitimate use for lazily initializing the value `()` - --> src/main.rs:10:27 - | -10 | static ref UNIT: () = (); - | ^^ -``` diff --git a/syn/examples/lazy-static/example/Cargo.toml b/syn/examples/lazy-static/example/Cargo.toml deleted file mode 100644 index 716b08c..0000000 --- a/syn/examples/lazy-static/example/Cargo.toml +++ /dev/null @@ -1,10 +0,0 @@ -[package] -name = "example" -version = "0.0.0" -authors = ["David Tolnay <dtolnay@gmail.com>"] -edition = "2018" -publish = false - -[dependencies] -lazy_static = { path = "../lazy-static" } -regex = "0.2" diff --git a/syn/examples/lazy-static/example/src/main.rs b/syn/examples/lazy-static/example/src/main.rs deleted file mode 100644 index c4f64af..0000000 --- a/syn/examples/lazy-static/example/src/main.rs +++ /dev/null @@ -1,20 +0,0 @@ -use lazy_static::lazy_static; -use regex::Regex; - -lazy_static! { - static ref USERNAME: Regex = { - println!("Compiling username regex..."); - Regex::new("^[a-z0-9_-]{3,16}$").unwrap() - }; -} - -fn main() { - println!("Let's validate some usernames."); - validate("fergie"); - validate("will.i.am"); -} - -fn validate(name: &str) { - // The USERNAME regex is compiled lazily the first time its value is accessed. - println!("is_match({:?}): {}", name, USERNAME.is_match(name)); -} diff --git a/syn/examples/lazy-static/lazy-static/Cargo.toml b/syn/examples/lazy-static/lazy-static/Cargo.toml deleted file mode 100644 index bf65787..0000000 --- a/syn/examples/lazy-static/lazy-static/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "lazy_static" -version = "0.0.0" -authors = ["David Tolnay <dtolnay@gmail.com>"] -edition = "2018" -publish = false - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = { version = "1.0", features = ["nightly"] } -quote = "1.0" -syn = { path = "../../../", features = ["full"] } diff --git a/syn/examples/lazy-static/lazy-static/src/lib.rs b/syn/examples/lazy-static/lazy-static/src/lib.rs deleted file mode 100644 index 254ca72..0000000 --- a/syn/examples/lazy-static/lazy-static/src/lib.rs +++ /dev/null @@ -1,143 +0,0 @@ -#![recursion_limit = "128"] -#![feature(proc_macro_diagnostic)] - -extern crate proc_macro; -use self::proc_macro::TokenStream; - -use quote::{quote, quote_spanned}; -use syn::parse::{Parse, ParseStream, Result}; -use syn::spanned::Spanned; -use syn::{parse_macro_input, Expr, Ident, Token, Type, Visibility}; - -/// Parses the following syntax, which aligns with the input of the real -/// `lazy_static` crate. -/// -/// lazy_static! { -/// $VISIBILITY static ref $NAME: $TYPE = $EXPR; -/// } -/// -/// For example: -/// -/// lazy_static! { -/// static ref USERNAME: Regex = Regex::new("^[a-z0-9_-]{3,16}$").unwrap(); -/// } -struct LazyStatic { - visibility: Visibility, - name: Ident, - ty: Type, - init: Expr, -} - -impl Parse for LazyStatic { - fn parse(input: ParseStream) -> Result<Self> { - let visibility: Visibility = input.parse()?; - input.parse::<Token![static]>()?; - input.parse::<Token![ref]>()?; - let name: Ident = input.parse()?; - input.parse::<Token![:]>()?; - let ty: Type = input.parse()?; - input.parse::<Token![=]>()?; - let init: Expr = input.parse()?; - input.parse::<Token![;]>()?; - Ok(LazyStatic { - visibility, - name, - ty, - init, - }) - } -} - -#[proc_macro] -pub fn lazy_static(input: TokenStream) -> TokenStream { - let LazyStatic { - visibility, - name, - ty, - init, - } = parse_macro_input!(input as LazyStatic); - - // The warning looks like this. - // - // warning: come on, pick a more creative name - // --> src/main.rs:10:16 - // | - // 10 | static ref FOO: String = "lazy_static".to_owned(); - // | ^^^ - if name == "FOO" { - name.span() - .unwrap() - .warning("come on, pick a more creative name") - .emit(); - } - - // The error looks like this. - // - // error: I can't think of a legitimate use for lazily initializing the value `()` - // --> src/main.rs:10:27 - // | - // 10 | static ref UNIT: () = (); - // | ^^ - if let Expr::Tuple(ref init) = init { - if init.elems.is_empty() { - init.span() - .unwrap() - .error("I can't think of a legitimate use for lazily initializing the value `()`") - .emit(); - return TokenStream::new(); - } - } - - // Assert that the static type implements Sync. If not, user sees an error - // message like the following. We span this assertion with the field type's - // line/column so that the error message appears in the correct place. - // - // error[E0277]: the trait bound `*const (): std::marker::Sync` is not satisfied - // --> src/main.rs:10:21 - // | - // 10 | static ref PTR: *const () = &(); - // | ^^^^^^^^^ `*const ()` cannot be shared between threads safely - let assert_sync = quote_spanned! {ty.span()=> - struct _AssertSync where #ty: std::marker::Sync; - }; - - // Check for Sized. Not vital to check here, but the error message is less - // confusing this way than if they get a Sized error in one of our - // implementation details where it assumes Sized. - // - // error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied - // --> src/main.rs:10:19 - // | - // 10 | static ref A: str = ""; - // | ^^^ `str` does not have a constant size known at compile-time - let assert_sized = quote_spanned! {ty.span()=> - struct _AssertSized where #ty: std::marker::Sized; - }; - - let init_ptr = quote_spanned! {init.span()=> - Box::into_raw(Box::new(#init)) - }; - - let expanded = quote! { - #visibility struct #name; - - impl std::ops::Deref for #name { - type Target = #ty; - - fn deref(&self) -> &#ty { - #assert_sync - #assert_sized - - static ONCE: std::sync::Once = std::sync::Once::new(); - static mut VALUE: *mut #ty = 0 as *mut #ty; - - unsafe { - ONCE.call_once(|| VALUE = #init_ptr); - &*VALUE - } - } - } - }; - - TokenStream::from(expanded) -} diff --git a/syn/examples/trace-var/Cargo.toml b/syn/examples/trace-var/Cargo.toml deleted file mode 100644 index b54454d..0000000 --- a/syn/examples/trace-var/Cargo.toml +++ /dev/null @@ -1,2 +0,0 @@ -[workspace] -members = ["example", "trace-var"] diff --git a/syn/examples/trace-var/README.md b/syn/examples/trace-var/README.md deleted file mode 100644 index b93fae2..0000000 --- a/syn/examples/trace-var/README.md +++ /dev/null @@ -1,61 +0,0 @@ -An example of an attribute procedural macro. The `#[trace_var(...)]` attribute -prints the value of the given variables each time they are reassigned. - -- [`trace-var/src/lib.rs`](trace-var/src/lib.rs) -- [`example/src/main.rs`](example/src/main.rs) - -Consider the following factorial implementation. - -```rust -#[trace_var(p, n)] -fn factorial(mut n: u64) -> u64 { - let mut p = 1; - while n > 1 { - p *= n; - n -= 1; - } - p -} -``` - -Invoking this with `factorial(8)` prints all the values of `p` and `n` during -the execution of the function. - -``` -p = 1 -p = 8 -n = 7 -p = 56 -n = 6 -p = 336 -n = 5 -p = 1680 -n = 4 -p = 6720 -n = 3 -p = 20160 -n = 2 -p = 40320 -n = 1 -``` - -The procedural macro uses a syntax tree [`Fold`] to rewrite every `let` -statement and assignment expression in the following way: - -[`Fold`]: https://docs.rs/syn/1.0/syn/fold/trait.Fold.html - -```rust -// Before -let VAR = INIT; - -// After -let VAR = { let VAR = INIT; println!("VAR = {:?}", VAR); VAR }; -``` - -```rust -// Before -VAR = INIT - -// After -{ VAR = INIT; println!("VAR = {:?}", VAR); } -``` diff --git a/syn/examples/trace-var/example/Cargo.toml b/syn/examples/trace-var/example/Cargo.toml deleted file mode 100644 index d2ad650..0000000 --- a/syn/examples/trace-var/example/Cargo.toml +++ /dev/null @@ -1,9 +0,0 @@ -[package] -name = "example" -version = "0.0.0" -authors = ["David Tolnay <dtolnay@gmail.com>"] -edition = "2018" -publish = false - -[dependencies] -trace-var = { path = "../trace-var" } diff --git a/syn/examples/trace-var/example/src/main.rs b/syn/examples/trace-var/example/src/main.rs deleted file mode 100644 index da2c10b..0000000 --- a/syn/examples/trace-var/example/src/main.rs +++ /dev/null @@ -1,15 +0,0 @@ -use trace_var::trace_var; - -fn main() { - println!("{}", factorial(8)); -} - -#[trace_var(p, n)] -fn factorial(mut n: u64) -> u64 { - let mut p = 1; - while n > 1 { - p *= n; - n -= 1; - } - p -} diff --git a/syn/examples/trace-var/trace-var/Cargo.toml b/syn/examples/trace-var/trace-var/Cargo.toml deleted file mode 100644 index 72f56e9..0000000 --- a/syn/examples/trace-var/trace-var/Cargo.toml +++ /dev/null @@ -1,14 +0,0 @@ -[package] -name = "trace-var" -version = "0.0.0" -authors = ["David Tolnay <dtolnay@gmail.com>"] -edition = "2018" -publish = false - -[lib] -proc-macro = true - -[dependencies] -proc-macro2 = { version = "1.0", features = ["nightly"] } -quote = "1.0" -syn = { path = "../../../", features = ["full", "fold"] } diff --git a/syn/examples/trace-var/trace-var/src/lib.rs b/syn/examples/trace-var/trace-var/src/lib.rs deleted file mode 100644 index 0ecfb47..0000000 --- a/syn/examples/trace-var/trace-var/src/lib.rs +++ /dev/null @@ -1,180 +0,0 @@ -extern crate proc_macro; -use self::proc_macro::TokenStream; - -use quote::{quote, ToTokens}; -use std::collections::HashSet as Set; -use syn::fold::{self, Fold}; -use syn::parse::{Parse, ParseStream, Result}; -use syn::punctuated::Punctuated; -use syn::{parse_macro_input, parse_quote, Expr, Ident, ItemFn, Local, Pat, Stmt, Token}; - -/// Parses a list of variable names separated by commas. -/// -/// a, b, c -/// -/// This is how the compiler passes in arguments to our attribute -- it is -/// everything inside the delimiters after the attribute name. -/// -/// #[trace_var(a, b, c)] -/// ^^^^^^^ -struct Args { - vars: Set<Ident>, -} - -impl Parse for Args { - fn parse(input: ParseStream) -> Result<Self> { - let vars = Punctuated::<Ident, Token![,]>::parse_terminated(input)?; - Ok(Args { - vars: vars.into_iter().collect(), - }) - } -} - -impl Args { - /// Determines whether the given `Expr` is a path referring to one of the - /// variables we intend to print. Expressions are used as the left-hand side - /// of the assignment operator. - fn should_print_expr(&self, e: &Expr) -> bool { - match *e { - Expr::Path(ref e) => { - if e.path.leading_colon.is_some() { - false - } else if e.path.segments.len() != 1 { - false - } else { - let first = e.path.segments.first().unwrap(); - self.vars.contains(&first.ident) && first.arguments.is_empty() - } - } - _ => false, - } - } - - /// Determines whether the given `Pat` is an identifier equal to one of the - /// variables we intend to print. Patterns are used as the left-hand side of - /// a `let` binding. - fn should_print_pat(&self, p: &Pat) -> bool { - match p { - Pat::Ident(ref p) => self.vars.contains(&p.ident), - _ => false, - } - } - - /// Produces an expression that assigns the right-hand side to the left-hand - /// side and then prints the value. - /// - /// // Before - /// VAR = INIT - /// - /// // After - /// { VAR = INIT; println!("VAR = {:?}", VAR); } - fn assign_and_print(&mut self, left: Expr, op: &dyn ToTokens, right: Expr) -> Expr { - let right = fold::fold_expr(self, right); - parse_quote!({ - #left #op #right; - println!(concat!(stringify!(#left), " = {:?}"), #left); - }) - } - - /// Produces a let-binding that assigns the right-hand side to the left-hand - /// side and then prints the value. - /// - /// // Before - /// let VAR = INIT; - /// - /// // After - /// let VAR = { let VAR = INIT; println!("VAR = {:?}", VAR); VAR }; - fn let_and_print(&mut self, local: Local) -> Stmt { - let Local { pat, init, .. } = local; - let init = self.fold_expr(*init.unwrap().1); - let ident = match pat { - Pat::Ident(ref p) => &p.ident, - _ => unreachable!(), - }; - parse_quote! { - let #pat = { - #[allow(unused_mut)] - let #pat = #init; - println!(concat!(stringify!(#ident), " = {:?}"), #ident); - #ident - }; - } - } -} - -/// The `Fold` trait is a way to traverse an owned syntax tree and replace some -/// of its nodes. -/// -/// Syn provides two other syntax tree traversal traits: `Visit` which walks a -/// shared borrow of a syntax tree, and `VisitMut` which walks an exclusive -/// borrow of a syntax tree and can mutate it in place. -/// -/// All three traits have a method corresponding to each type of node in Syn's -/// syntax tree. All of these methods have default no-op implementations that -/// simply recurse on any child nodes. We can override only those methods for -/// which we want non-default behavior. In this case the traversal needs to -/// transform `Expr` and `Stmt` nodes. -impl Fold for Args { - fn fold_expr(&mut self, e: Expr) -> Expr { - match e { - Expr::Assign(e) => { - if self.should_print_expr(&e.left) { - self.assign_and_print(*e.left, &e.eq_token, *e.right) - } else { - Expr::Assign(fold::fold_expr_assign(self, e)) - } - } - Expr::AssignOp(e) => { - if self.should_print_expr(&e.left) { - self.assign_and_print(*e.left, &e.op, *e.right) - } else { - Expr::AssignOp(fold::fold_expr_assign_op(self, e)) - } - } - _ => fold::fold_expr(self, e), - } - } - - fn fold_stmt(&mut self, s: Stmt) -> Stmt { - match s { - Stmt::Local(s) => { - if s.init.is_some() && self.should_print_pat(&s.pat) { - self.let_and_print(s) - } else { - Stmt::Local(fold::fold_local(self, s)) - } - } - _ => fold::fold_stmt(self, s), - } - } -} - -/// Attribute to print the value of the given variables each time they are -/// reassigned. -/// -/// # Example -/// -/// ``` -/// #[trace_var(p, n)] -/// fn factorial(mut n: u64) -> u64 { -/// let mut p = 1; -/// while n > 1 { -/// p *= n; -/// n -= 1; -/// } -/// p -/// } -/// ``` -#[proc_macro_attribute] -pub fn trace_var(args: TokenStream, input: TokenStream) -> TokenStream { - let input = parse_macro_input!(input as ItemFn); - - // Parse the list of variables the user wanted to print. - let mut args = parse_macro_input!(args as Args); - - // Use a syntax tree traversal to transform the function body. - let output = args.fold_item_fn(input); - - // Hand the resulting function body back to the compiler. - TokenStream::from(quote!(#output)) -} |