blob: 217adb6ecab86da4a501de666bd09d6efa014609 [file] [log] [blame] [view]
David Tolnay6eacaff2016-10-23 15:20:23 -07001Nom parser for Rust source code
2===============================
David Tolnay35161ff2016-09-03 11:33:15 -07003
David Tolnayac9953b2016-09-07 08:37:12 -07004[![Build Status](https://api.travis-ci.org/dtolnay/syn.svg?branch=master)](https://travis-ci.org/dtolnay/syn)
5[![Latest Version](https://img.shields.io/crates/v/syn.svg)](https://crates.io/crates/syn)
David Tolnay50e62202016-09-12 09:49:49 -07006[![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://dtolnay.github.io/syn/syn/)
David Tolnayac9953b2016-09-07 08:37:12 -07007
David Tolnay35161ff2016-09-03 11:33:15 -07008Parse Rust structs and enums without a Syntex dependency, intended for use with
9[Macros 1.1](https://github.com/rust-lang/rfcs/blob/master/text/1681-macros-1.1.md).
10
David Tolnayf939f352016-09-11 18:00:09 -070011Designed for fast compile time.
12
David Tolnayb5a7b142016-09-13 22:46:39 -070013- Compile time for `syn` (from scratch including all dependencies): **4 seconds**
David Tolnayf939f352016-09-11 18:00:09 -070014- Compile time for the `syntex`/`quasi`/`aster` stack: **60+ seconds**
15
David Tolnayfb9f7042016-12-22 12:31:39 -050016If you get stuck with Macros 1.1 I am happy to provide help even if the issue is
17not related to syn. Please file a ticket in this repo.
18
David Tolnayed8e23e2016-09-27 08:18:22 -070019## Usage with Macros 1.1
David Tolnay6c9f5b62016-09-13 15:19:22 -070020
David Tolnay69b538e2016-09-23 19:59:48 -070021```toml
22[dependencies]
David Tolnay3c0c8c52016-10-30 20:20:46 -070023syn = "0.10"
David Tolnayf8f43072016-10-08 14:58:05 -070024quote = "0.3"
David Tolnay69b538e2016-09-23 19:59:48 -070025
26[lib]
David Tolnay45cc8492016-10-08 20:52:03 -070027proc-macro = true
David Tolnay69b538e2016-09-23 19:59:48 -070028```
29
White-Oak82d0db72016-09-13 21:45:58 +030030```rust
David Tolnay45cc8492016-10-08 20:52:03 -070031#![feature(proc_macro, proc_macro_lib)]
White-Oak82d0db72016-09-13 21:45:58 +030032
David Tolnay45cc8492016-10-08 20:52:03 -070033extern crate proc_macro;
34use proc_macro::TokenStream;
David Tolnayb4c63262016-09-23 20:03:06 -070035
36extern crate syn;
David Tolnay6c9f5b62016-09-13 15:19:22 -070037
White-Oak82d0db72016-09-13 21:45:58 +030038#[macro_use]
39extern crate quote;
White-Oak82d0db72016-09-13 21:45:58 +030040
David Tolnay45cc8492016-10-08 20:52:03 -070041#[proc_macro_derive(MyMacro)]
David Tolnayb4c63262016-09-23 20:03:06 -070042pub fn my_macro(input: TokenStream) -> TokenStream {
White-Oak82d0db72016-09-13 21:45:58 +030043 let source = input.to_string();
44
David Tolnayb988b6d2016-10-05 00:12:37 -070045 // Parse the string representation to an AST
David Tolnayf38cdf62016-09-23 19:07:09 -070046 let ast = syn::parse_macro_input(&source).unwrap();
White-Oak82d0db72016-09-13 21:45:58 +030047
David Tolnay6c9f5b62016-09-13 15:19:22 -070048 // Build the output, possibly using quasi-quotation
49 let expanded = quote! {
50 // ...
51 };
52
David Tolnayb988b6d2016-10-05 00:12:37 -070053 // Parse back to a token stream and return it
David Tolnay62463ba2016-11-24 13:06:01 -080054 expanded.parse().unwrap()
White-Oak82d0db72016-09-13 21:45:58 +030055}
56```
David Tolnay6c9f5b62016-09-13 15:19:22 -070057
David Tolnayb988b6d2016-10-05 00:12:37 -070058## Complete example
59
60Suppose we have the following simple trait which returns the number of fields in
61a struct:
62
63```rust
64trait NumFields {
65 fn num_fields() -> usize;
66}
67```
68
69A complete Macros 1.1 implementation of `#[derive(NumFields)]` based on `syn`
70and [`quote`](https://github.com/dtolnay/quote) looks like this:
71
72```rust
David Tolnay45cc8492016-10-08 20:52:03 -070073#![feature(proc_macro, proc_macro_lib)]
David Tolnayb988b6d2016-10-05 00:12:37 -070074
David Tolnay45cc8492016-10-08 20:52:03 -070075extern crate proc_macro;
76use proc_macro::TokenStream;
David Tolnayb988b6d2016-10-05 00:12:37 -070077
78extern crate syn;
79
80#[macro_use]
81extern crate quote;
82
David Tolnay45cc8492016-10-08 20:52:03 -070083#[proc_macro_derive(NumFields)]
David Tolnayb988b6d2016-10-05 00:12:37 -070084pub fn num_fields(input: TokenStream) -> TokenStream {
85 let source = input.to_string();
86
87 // Parse the string representation to an AST
88 let ast = syn::parse_macro_input(&source).unwrap();
89
90 // Build the output
David Tolnayed7a5082016-10-30 20:06:29 -070091 let expanded = expand_num_fields(&ast);
David Tolnayb988b6d2016-10-05 00:12:37 -070092
David Tolnay7c509bb2016-11-19 13:00:12 -080093 // Return the generated impl as a TokenStream
David Tolnay62463ba2016-11-24 13:06:01 -080094 expanded.parse().unwrap()
David Tolnayb988b6d2016-10-05 00:12:37 -070095}
96
David Tolnayed7a5082016-10-30 20:06:29 -070097fn expand_num_fields(ast: &syn::MacroInput) -> quote::Tokens {
David Tolnayb988b6d2016-10-05 00:12:37 -070098 let n = match ast.body {
99 syn::Body::Struct(ref data) => data.fields().len(),
100 syn::Body::Enum(_) => panic!("#[derive(NumFields)] can only be used with structs"),
101 };
102
103 // Used in the quasi-quotation below as `#name`
104 let name = &ast.ident;
105
106 // Helper is provided for handling complex generic types correctly and effortlessly
107 let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl();
108
109 quote! {
David Tolnayb988b6d2016-10-05 00:12:37 -0700110 // The generated impl
111 impl #impl_generics ::mycrate::NumFields for #name #ty_generics #where_clause {
112 fn num_fields() -> usize {
113 #n
114 }
115 }
116 }
117}
118```
119
David Tolnay736829a2016-12-22 15:55:53 -0500120## Testing
121
122Macros 1.1 has a restriction that your proc-macro crate must export nothing but
123`proc_macro_derive` functions, and also `proc_macro_derive` procedural macros
124cannot be used from the same crate in which they are defined. These restrictions
125may be lifted in the future but for now they make writing tests a bit trickier
126than for other types of code.
127
128In particular, you will not be able to write test functions like `#[test] fn
129it_works() { ... }` in line with your code. Instead, either put tests in a
130[`tests` directory](https://doc.rust-lang.org/book/testing.html#the-tests-directory)
131or in a separate crate entirely.
132
133Additionally, if your procedural macro implements a particular trait, that trait
134must be defined in a separate crate from the procedural macro.
135
136As a concrete example, suppose your procedural macro crate is called `my_derive`
137and it implements a trait called `my_crate::MyTrait`. Your unit tests for the
138procedural macro can go in `my_derive/tests/test.rs` or into a separate crate
139`my_tests/tests/test.rs`. Either way the test would look something like this:
140
141```rust
142#![feature(proc_macro)]
143
144#[macro_use]
145extern crate my_derive;
146
147extern crate my_crate;
148use my_crate::MyTrait;
149
150#[test]
151fn it_works() {
152 #[derive(MyTrait)]
153 struct S { /* ... */ }
154
155 /* test the thing */
156}
157```
158
David Tolnay686f5042016-10-30 19:24:51 -0700159## Optional features
160
161Syn puts a lot of functionality behind optional features in order to optimize
162compile time for the most common use cases. These are the available features and
163their effect on compile time. Dependencies are included in the compile times.
164
165Features | Compile time | Functionality
166--- | --- | ---
167*(none)* | 1 sec | The data structures representing the AST of Rust structs, enums, and types.
168parsing | 4 sec | Parsing Rust source code containing structs and enums into an AST.
169printing | 2 sec | Printing an AST of structs and enums as Rust source code.
170**parsing, printing** | **4 sec** | **This is the default.** Parsing and printing of Rust structs and enums. This is typically what you want for implementing Macros 1.1 custom derives.
171full | 2 sec | The data structures representing the full AST of all possible Rust code.
172full, parsing | 7 sec | Parsing any valid Rust source code to an AST.
173full, printing | 4 sec | Turning an AST into Rust source code.
174full, parsing, printing | 8 sec | Parsing and printing any Rust syntax.
175full, parsing, printing, expand | 9 sec | Expansion of custom derives in a file of Rust code. This is typically what you want for expanding custom derives on stable Rust using a build script.
176full, parsing, printing, expand, pretty | 60 sec | Expansion of custom derives with pretty-printed output. This is what you want when iterating on or debugging a custom derive, but the pretty printing should be disabled once you get everything working.
177
David Tolnayed7a5082016-10-30 20:06:29 -0700178## Custom derives on stable Rust
179
180Syn supports a way of expanding custom derives from a build script, similar to
181what [Serde is able to do with serde_codegen](https://serde.rs/codegen-stable.html).
182The advantage of using Syn for this purpose rather than Syntex is much faster
183compile time.
184
185Continuing with the `NumFields` example from above, it can be extended to
186support stable Rust like this. One or more custom derives are added to a
187[`Registry`](https://dtolnay.github.io/syn/syn/struct.Registry.html), which is
188then able to expand those derives in a source file at a particular path and
189write the expanded output to a different path. A custom derive is represented by
190the [`CustomDerive`](https://dtolnay.github.io/syn/syn/trait.CustomDerive.html)
191trait which takes a [`MacroInput`](https://dtolnay.github.io/syn/syn/struct.MacroInput.html)
192(either a struct or an enum) and [expands it](https://dtolnay.github.io/syn/syn/struct.Expanded.html)
193into zero or more new items and maybe a modified or unmodified instance of the
194original input.
195
196```rust
197pub fn expand_file<S, D>(src: S, dst: D) -> Result<(), String>
198 where S: AsRef<Path>,
199 D: AsRef<Path>
200{
201 let mut registry = syn::Registry::new();
202 registry.add_derive("NumFields", |input| {
203 let tokens = expand_num_fields(&input);
204 let items = syn::parse_items(&tokens.to_string()).unwrap();
205 Ok(syn::Expanded {
206 new_items: items,
207 original: Some(input),
208 })
209 });
210 registry.expand_file(src, dst)
211}
212```
213
214The codegen can be invoked from a build script as follows.
215
216```rust
217extern crate your_codegen;
218
219use std::env;
220use std::path::Path;
221
222fn main() {
223 let out_dir = env::var_os("OUT_DIR").unwrap();
224
225 let src = Path::new("src/codegen_types.in.rs");
226 let dst = Path::new(&out_dir).join("codegen_types.rs");
227
228 your_codegen::expand_file(&src, &dst).unwrap();
229}
230```
231
David Tolnay35161ff2016-09-03 11:33:15 -0700232## License
233
234Licensed under either of
235
236 * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
237 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
238
239at your option.
240
241### Contribution
242
243Unless you explicitly state otherwise, any contribution intentionally submitted
244for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
245be dual licensed as above, without any additional terms or conditions.