David Tolnay | 6eacaff | 2016-10-23 15:20:23 -0700 | [diff] [blame] | 1 | Nom parser for Rust source code |
| 2 | =============================== |
David Tolnay | 35161ff | 2016-09-03 11:33:15 -0700 | [diff] [blame] | 3 | |
David Tolnay | ac9953b | 2016-09-07 08:37:12 -0700 | [diff] [blame] | 4 | [](https://travis-ci.org/dtolnay/syn) |
| 5 | [](https://crates.io/crates/syn) |
David Tolnay | 552ff33 | 2018-03-31 23:04:48 +0200 | [diff] [blame] | 6 | [](https://docs.rs/syn/0.13/syn/) |
David Tolnay | 8659a23 | 2018-03-31 22:54:03 +0200 | [diff] [blame] | 7 | [](https://blog.rust-lang.org/2017/02/02/Rust-1.15.html) |
David Tolnay | ac9953b | 2016-09-07 08:37:12 -0700 | [diff] [blame] | 8 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 9 | Syn is a parsing library for parsing a stream of Rust tokens into a syntax tree |
| 10 | of Rust source code. |
David Tolnay | 35161ff | 2016-09-03 11:33:15 -0700 | [diff] [blame] | 11 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 12 | Currently this library is geared toward the [custom derive] use case but |
| 13 | contains some APIs that may be useful for Rust procedural macros more generally. |
David Tolnay | f939f35 | 2016-09-11 18:00:09 -0700 | [diff] [blame] | 14 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 15 | [custom derive]: https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md |
David Tolnay | f939f35 | 2016-09-11 18:00:09 -0700 | [diff] [blame] | 16 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 17 | - **Data structures** — Syn provides a complete syntax tree that can represent |
| 18 | any valid Rust source code. The syntax tree is rooted at [`syn::File`] which |
| 19 | represents a full source file, but there are other entry points that may be |
| 20 | useful to procedural macros including [`syn::Item`], [`syn::Expr`] and |
| 21 | [`syn::Type`]. |
David Tolnay | fb9f704 | 2016-12-22 12:31:39 -0500 | [diff] [blame] | 22 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 23 | - **Custom derives** — Of particular interest to custom derives is |
| 24 | [`syn::DeriveInput`] which is any of the three legal input items to a derive |
| 25 | macro. An example below shows using this type in a library that can derive |
| 26 | implementations of a trait of your own. |
| 27 | |
| 28 | - **Parser combinators** — Parsing in Syn is built on a suite of public parser |
| 29 | combinator macros that you can use for parsing any token-based syntax you |
| 30 | dream up within a `functionlike!(...)` procedural macro. Every syntax tree |
| 31 | node defined by Syn is individually parsable and may be used as a building |
| 32 | block for custom syntaxes, or you may do it all yourself working from the most |
| 33 | primitive tokens. |
| 34 | |
| 35 | - **Location information** — Every token parsed by Syn is associated with a |
| 36 | `Span` that tracks line and column information back to the source of that |
| 37 | token. These spans allow a procedural macro to display detailed error messages |
| 38 | pointing to all the right places in the user's code. There is an example of |
| 39 | this below. |
| 40 | |
| 41 | - **Feature flags** — Functionality is aggressively feature gated so your |
| 42 | procedural macros enable only what they need, and do not pay in compile time |
| 43 | for all the rest. |
| 44 | |
David Tolnay | 552ff33 | 2018-03-31 23:04:48 +0200 | [diff] [blame] | 45 | [`syn::File`]: https://docs.rs/syn/0.13/syn/struct.File.html |
| 46 | [`syn::Item`]: https://docs.rs/syn/0.13/syn/enum.Item.html |
| 47 | [`syn::Expr`]: https://docs.rs/syn/0.13/syn/enum.Expr.html |
| 48 | [`syn::Type`]: https://docs.rs/syn/0.13/syn/enum.Type.html |
| 49 | [`syn::DeriveInput`]: https://docs.rs/syn/0.13/syn/struct.DeriveInput.html |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 50 | |
| 51 | If you get stuck with anything involving procedural macros in Rust I am happy to |
| 52 | provide help even if the issue is not related to Syn. Please file a ticket in |
| 53 | this repo. |
| 54 | |
| 55 | *Version requirement: Syn supports any compiler version back to Rust's very |
| 56 | first support for procedural macros in Rust 1.15.0. Some features especially |
| 57 | around error reporting are only available in newer compilers or on the nightly |
| 58 | channel.* |
| 59 | |
| 60 | ## Example of a custom derive |
| 61 | |
| 62 | The canonical custom derive using Syn looks like this. We write an ordinary Rust |
| 63 | function tagged with a `proc_macro_derive` attribute and the name of the trait |
| 64 | we are deriving. Any time that derive appears in the user's code, the Rust |
| 65 | compiler passes their data structure as tokens into our macro. We get to execute |
| 66 | arbitrary Rust code to figure out what to do with those tokens, then hand some |
| 67 | tokens back to the compiler to compile into the user's crate. |
| 68 | |
| 69 | [`TokenStream`]: https://doc.rust-lang.org/proc_macro/struct.TokenStream.html |
David Tolnay | 6c9f5b6 | 2016-09-13 15:19:22 -0700 | [diff] [blame] | 70 | |
David Tolnay | 69b538e | 2016-09-23 19:59:48 -0700 | [diff] [blame] | 71 | ```toml |
| 72 | [dependencies] |
David Tolnay | 552ff33 | 2018-03-31 23:04:48 +0200 | [diff] [blame] | 73 | syn = "0.13" |
| 74 | quote = "0.5" |
David Tolnay | 69b538e | 2016-09-23 19:59:48 -0700 | [diff] [blame] | 75 | |
| 76 | [lib] |
David Tolnay | 45cc849 | 2016-10-08 20:52:03 -0700 | [diff] [blame] | 77 | proc-macro = true |
David Tolnay | 69b538e | 2016-09-23 19:59:48 -0700 | [diff] [blame] | 78 | ``` |
| 79 | |
White-Oak | 82d0db7 | 2016-09-13 21:45:58 +0300 | [diff] [blame] | 80 | ```rust |
David Tolnay | 45cc849 | 2016-10-08 20:52:03 -0700 | [diff] [blame] | 81 | extern crate proc_macro; |
David Tolnay | b4c6326 | 2016-09-23 20:03:06 -0700 | [diff] [blame] | 82 | extern crate syn; |
David Tolnay | 6c9f5b6 | 2016-09-13 15:19:22 -0700 | [diff] [blame] | 83 | |
White-Oak | 82d0db7 | 2016-09-13 21:45:58 +0300 | [diff] [blame] | 84 | #[macro_use] |
| 85 | extern crate quote; |
White-Oak | 82d0db7 | 2016-09-13 21:45:58 +0300 | [diff] [blame] | 86 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 87 | use proc_macro::TokenStream; |
| 88 | use syn::DeriveInput; |
| 89 | |
David Tolnay | 45cc849 | 2016-10-08 20:52:03 -0700 | [diff] [blame] | 90 | #[proc_macro_derive(MyMacro)] |
David Tolnay | b4c6326 | 2016-09-23 20:03:06 -0700 | [diff] [blame] | 91 | pub fn my_macro(input: TokenStream) -> TokenStream { |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 92 | // Parse the input tokens into a syntax tree |
| 93 | let input: DeriveInput = syn::parse(input).unwrap(); |
White-Oak | 82d0db7 | 2016-09-13 21:45:58 +0300 | [diff] [blame] | 94 | |
David Tolnay | 6c9f5b6 | 2016-09-13 15:19:22 -0700 | [diff] [blame] | 95 | // Build the output, possibly using quasi-quotation |
| 96 | let expanded = quote! { |
| 97 | // ... |
| 98 | }; |
| 99 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 100 | // Hand the output tokens back to the compiler |
| 101 | expanded.into() |
White-Oak | 82d0db7 | 2016-09-13 21:45:58 +0300 | [diff] [blame] | 102 | } |
| 103 | ``` |
David Tolnay | 6c9f5b6 | 2016-09-13 15:19:22 -0700 | [diff] [blame] | 104 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 105 | The [`heapsize`] example directory shows a complete working Macros 1.1 |
| 106 | implementation of a custom derive. It works on any Rust compiler \>=1.15.0. The |
| 107 | example derives a `HeapSize` trait which computes an estimate of the amount of |
| 108 | heap memory owned by a value. |
David Tolnay | b988b6d | 2016-10-05 00:12:37 -0700 | [diff] [blame] | 109 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 110 | [`heapsize`]: examples/heapsize |
David Tolnay | b988b6d | 2016-10-05 00:12:37 -0700 | [diff] [blame] | 111 | |
| 112 | ```rust |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 113 | pub trait HeapSize { |
| 114 | /// Total number of bytes of heap memory owned by `self`. |
| 115 | fn heap_size_of_children(&self) -> usize; |
David Tolnay | b988b6d | 2016-10-05 00:12:37 -0700 | [diff] [blame] | 116 | } |
| 117 | ``` |
| 118 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 119 | The custom derive allows users to write `#[derive(HeapSize)]` on data structures |
| 120 | in their program. |
David Tolnay | b988b6d | 2016-10-05 00:12:37 -0700 | [diff] [blame] | 121 | |
| 122 | ```rust |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 123 | #[derive(HeapSize)] |
| 124 | struct Demo<'a, T: ?Sized> { |
| 125 | a: Box<T>, |
| 126 | b: u8, |
| 127 | c: &'a str, |
| 128 | d: String, |
David Tolnay | b988b6d | 2016-10-05 00:12:37 -0700 | [diff] [blame] | 129 | } |
| 130 | ``` |
| 131 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 132 | ## Spans and error reporting |
David Tolnay | c2263f3 | 2017-03-09 19:20:52 -0800 | [diff] [blame] | 133 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 134 | The [`heapsize2`] example directory is an extension of the `heapsize` example |
| 135 | that demonstrates some of the hygiene and error reporting properties of Macros |
| 136 | 2.0. This example currently requires a nightly Rust compiler \>=1.24.0-nightly |
| 137 | but we are working to stabilize all of the APIs involved. |
David Tolnay | c2263f3 | 2017-03-09 19:20:52 -0800 | [diff] [blame] | 138 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 139 | [`heapsize2`]: examples/heapsize2 |
David Tolnay | 736829a | 2016-12-22 15:55:53 -0500 | [diff] [blame] | 140 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 141 | The token-based procedural macro API provides great control over where the |
| 142 | compiler's error messages are displayed in user code. Consider the error the |
| 143 | user sees if one of their field types does not implement `HeapSize`. |
David Tolnay | 736829a | 2016-12-22 15:55:53 -0500 | [diff] [blame] | 144 | |
| 145 | ```rust |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 146 | #[derive(HeapSize)] |
| 147 | struct Broken { |
| 148 | ok: String, |
| 149 | bad: std::thread::Thread, |
David Tolnay | 736829a | 2016-12-22 15:55:53 -0500 | [diff] [blame] | 150 | } |
| 151 | ``` |
| 152 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 153 | In the Macros 1.1 string-based procedural macro world, the resulting error would |
| 154 | point unhelpfully to the invocation of the derive macro and not to the actual |
| 155 | problematic field. |
| 156 | |
| 157 | ``` |
| 158 | error[E0599]: no method named `heap_size_of_children` found for type `std::thread::Thread` in the current scope |
| 159 | --> src/main.rs:4:10 |
| 160 | | |
| 161 | 4 | #[derive(HeapSize)] |
| 162 | | ^^^^^^^^ |
| 163 | ``` |
| 164 | |
| 165 | By tracking span information all the way through the expansion of a procedural |
| 166 | macro as shown in the `heapsize2` example, token-based macros in Syn are able to |
| 167 | trigger errors that directly pinpoint the source of the problem. |
| 168 | |
| 169 | ``` |
| 170 | error[E0277]: the trait bound `std::thread::Thread: HeapSize` is not satisfied |
| 171 | --> src/main.rs:7:5 |
| 172 | | |
| 173 | 7 | bad: std::thread::Thread, |
| 174 | | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `HeapSize` is not implemented for `std::thread::Thread` |
| 175 | ``` |
| 176 | |
| 177 | ## Parsing a custom syntax using combinators |
| 178 | |
| 179 | The [`lazy-static`] example directory shows the implementation of a |
| 180 | `functionlike!(...)` procedural macro in which the input tokens are parsed using |
| 181 | [`nom`]-style parser combinators. |
| 182 | |
| 183 | [`lazy-static`]: examples/lazy-static |
| 184 | [`nom`]: https://github.com/Geal/nom |
| 185 | |
| 186 | The example reimplements the popular `lazy_static` crate from crates.io as a |
| 187 | procedural macro. |
| 188 | |
| 189 | ``` |
| 190 | lazy_static! { |
| 191 | static ref USERNAME: Regex = Regex::new("^[a-z0-9_-]{3,16}$").unwrap(); |
| 192 | } |
| 193 | ``` |
| 194 | |
| 195 | The implementation shows how to trigger custom warnings and error messages on |
| 196 | the macro input. |
| 197 | |
| 198 | ``` |
| 199 | warning: come on, pick a more creative name |
| 200 | --> src/main.rs:10:16 |
| 201 | | |
| 202 | 10 | static ref FOO: String = "lazy_static".to_owned(); |
| 203 | | ^^^ |
| 204 | ``` |
| 205 | |
David Tolnay | 941c092 | 2016-12-22 16:06:27 -0500 | [diff] [blame] | 206 | ## Debugging |
| 207 | |
| 208 | When developing a procedural macro it can be helpful to look at what the |
| 209 | generated code looks like. Use `cargo rustc -- -Zunstable-options |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 210 | --pretty=expanded` or the [`cargo expand`] subcommand. |
| 211 | |
| 212 | [`cargo expand`]: https://github.com/dtolnay/cargo-expand |
David Tolnay | 941c092 | 2016-12-22 16:06:27 -0500 | [diff] [blame] | 213 | |
| 214 | To show the expanded code for some crate that uses your procedural macro, run |
| 215 | `cargo expand` from that crate. To show the expanded code for one of your own |
| 216 | test cases, run `cargo expand --test the_test_case` where the last argument is |
| 217 | the name of the test file without the `.rs` extension. |
| 218 | |
David Tolnay | 3bfbd54 | 2017-01-16 14:57:53 -0800 | [diff] [blame] | 219 | This write-up by Brandon W Maister discusses debugging in more detail: |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 220 | [Debugging Rust's new Custom Derive system][debugging]. |
| 221 | |
| 222 | [debugging]: https://quodlibetor.github.io/posts/debugging-rusts-new-custom-derive-system/ |
David Tolnay | 3bfbd54 | 2017-01-16 14:57:53 -0800 | [diff] [blame] | 223 | |
David Tolnay | 686f504 | 2016-10-30 19:24:51 -0700 | [diff] [blame] | 224 | ## Optional features |
| 225 | |
| 226 | Syn puts a lot of functionality behind optional features in order to optimize |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 227 | compile time for the most common use cases. The following features are |
| 228 | available. |
David Tolnay | 686f504 | 2016-10-30 19:24:51 -0700 | [diff] [blame] | 229 | |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 230 | - **`derive`** *(enabled by default)* — Data structures for representing the |
| 231 | possible input to a custom derive, including structs and enums and types. |
| 232 | - **`full`** — Data structures for representing the syntax tree of all valid |
| 233 | Rust source code, including items and expressions. |
| 234 | - **`parsing`** *(enabled by default)* — Ability to parse input tokens into a |
| 235 | syntax tree node of a chosen type. |
| 236 | - **`printing`** *(enabled by default)* — Ability to print a syntax tree node as |
| 237 | tokens of Rust source code. |
| 238 | - **`visit`** — Trait for traversing a syntax tree. |
| 239 | - **`visit-mut`** — Trait for traversing and mutating in place a syntax tree. |
| 240 | - **`fold`** — Trait for transforming an owned syntax tree. |
| 241 | - **`clone-impls`** *(enabled by default)* — Clone impls for all syntax tree |
| 242 | types. |
| 243 | - **`extra-traits`** — Debug, Eq, PartialEq, Hash impls for all syntax tree |
| 244 | types. |
David Tolnay | d236b55 | 2018-04-08 09:02:55 -0700 | [diff] [blame^] | 245 | - **`proc-macro`** *(enabled by default)* — Runtime dependency on the dynamic |
| 246 | library libproc_macro from rustc toolchain. |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 247 | |
| 248 | ## Nightly features |
| 249 | |
| 250 | By default Syn uses the [`proc-macro2`] crate to emulate the nightly compiler's |
| 251 | procedural macro API in a stable way that works all the way back to Rust 1.15.0. |
| 252 | This shim makes it possible to write code without regard for whether the current |
| 253 | compiler version supports the features we use. |
| 254 | |
| 255 | [`proc-macro2`]: https://github.com/alexcrichton/proc-macro2 |
| 256 | |
| 257 | On a nightly compiler, to eliminate the stable shim and use the compiler's |
| 258 | `proc-macro` directly, add `proc-macro2` to your Cargo.toml and set its |
| 259 | `"nightly"` feature which bypasses the stable shim. |
| 260 | |
| 261 | ```toml |
| 262 | [dependencies] |
David Tolnay | 552ff33 | 2018-03-31 23:04:48 +0200 | [diff] [blame] | 263 | syn = "0.13" |
| 264 | proc-macro2 = { version = "0.3", features = ["nightly"] } |
David Tolnay | c088adb | 2018-01-01 00:26:05 -0500 | [diff] [blame] | 265 | ``` |
David Tolnay | ed7a508 | 2016-10-30 20:06:29 -0700 | [diff] [blame] | 266 | |
David Tolnay | 35161ff | 2016-09-03 11:33:15 -0700 | [diff] [blame] | 267 | ## License |
| 268 | |
| 269 | Licensed under either of |
| 270 | |
| 271 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) |
| 272 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) |
| 273 | |
| 274 | at your option. |
| 275 | |
| 276 | ### Contribution |
| 277 | |
| 278 | Unless you explicitly state otherwise, any contribution intentionally submitted |
| 279 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall |
| 280 | be dual licensed as above, without any additional terms or conditions. |