blob: 6a6741152139e4bd1e4e90a74664e59768063df3 [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 Tolnay941c0922016-12-22 16:06:27 -0500159## Debugging
160
161When developing a procedural macro it can be helpful to look at what the
162generated code looks like. Use `cargo rustc -- -Zunstable-options
163--pretty=expanded` or the
164[`cargo expand`](https://github.com/dtolnay/cargo-expand) subcommand.
165
166To show the expanded code for some crate that uses your procedural macro, run
167`cargo expand` from that crate. To show the expanded code for one of your own
168test cases, run `cargo expand --test the_test_case` where the last argument is
169the name of the test file without the `.rs` extension.
170
David Tolnay686f5042016-10-30 19:24:51 -0700171## Optional features
172
173Syn puts a lot of functionality behind optional features in order to optimize
174compile time for the most common use cases. These are the available features and
175their effect on compile time. Dependencies are included in the compile times.
176
177Features | Compile time | Functionality
178--- | --- | ---
179*(none)* | 1 sec | The data structures representing the AST of Rust structs, enums, and types.
180parsing | 4 sec | Parsing Rust source code containing structs and enums into an AST.
181printing | 2 sec | Printing an AST of structs and enums as Rust source code.
182**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.
183full | 2 sec | The data structures representing the full AST of all possible Rust code.
184full, parsing | 7 sec | Parsing any valid Rust source code to an AST.
185full, printing | 4 sec | Turning an AST into Rust source code.
186full, parsing, printing | 8 sec | Parsing and printing any Rust syntax.
187full, 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.
188full, 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.
189
David Tolnayed7a5082016-10-30 20:06:29 -0700190## Custom derives on stable Rust
191
192Syn supports a way of expanding custom derives from a build script, similar to
193what [Serde is able to do with serde_codegen](https://serde.rs/codegen-stable.html).
194The advantage of using Syn for this purpose rather than Syntex is much faster
195compile time.
196
197Continuing with the `NumFields` example from above, it can be extended to
198support stable Rust like this. One or more custom derives are added to a
199[`Registry`](https://dtolnay.github.io/syn/syn/struct.Registry.html), which is
200then able to expand those derives in a source file at a particular path and
201write the expanded output to a different path. A custom derive is represented by
202the [`CustomDerive`](https://dtolnay.github.io/syn/syn/trait.CustomDerive.html)
203trait which takes a [`MacroInput`](https://dtolnay.github.io/syn/syn/struct.MacroInput.html)
204(either a struct or an enum) and [expands it](https://dtolnay.github.io/syn/syn/struct.Expanded.html)
205into zero or more new items and maybe a modified or unmodified instance of the
206original input.
207
208```rust
209pub fn expand_file<S, D>(src: S, dst: D) -> Result<(), String>
210 where S: AsRef<Path>,
211 D: AsRef<Path>
212{
213 let mut registry = syn::Registry::new();
214 registry.add_derive("NumFields", |input| {
215 let tokens = expand_num_fields(&input);
216 let items = syn::parse_items(&tokens.to_string()).unwrap();
217 Ok(syn::Expanded {
218 new_items: items,
219 original: Some(input),
220 })
221 });
222 registry.expand_file(src, dst)
223}
224```
225
226The codegen can be invoked from a build script as follows.
227
228```rust
229extern crate your_codegen;
230
231use std::env;
232use std::path::Path;
233
234fn main() {
235 let out_dir = env::var_os("OUT_DIR").unwrap();
236
237 let src = Path::new("src/codegen_types.in.rs");
238 let dst = Path::new(&out_dir).join("codegen_types.rs");
239
240 your_codegen::expand_file(&src, &dst).unwrap();
241}
242```
243
David Tolnay35161ff2016-09-03 11:33:15 -0700244## License
245
246Licensed 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
251at your option.
252
253### Contribution
254
255Unless you explicitly state otherwise, any contribution intentionally submitted
256for inclusion in this crate by you, as defined in the Apache-2.0 license, shall
257be dual licensed as above, without any additional terms or conditions.