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