]>
Commit | Line | Data |
---|---|---|
83c7162d XL |
1 | Nom parser for Rust source code |
2 | =============================== | |
3 | ||
4 | [![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) | |
6 | [![Rust Documentation](https://img.shields.io/badge/api-rustdoc-blue.svg)](https://dtolnay.github.io/syn/syn/) | |
7 | ||
8 | Parse Rust source code 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 | ||
11 | Designed for fast compile time. | |
12 | ||
13 | - Compile time for `syn` (from scratch including all dependencies): **6 seconds** | |
14 | - Compile time for the `syntex`/`quasi`/`aster` stack: **60+ seconds** | |
15 | ||
16 | If you get stuck with Macros 1.1 I am happy to provide help even if the issue is | |
17 | not related to syn. Please file a ticket in this repo. | |
18 | ||
19 | ## Usage with Macros 1.1 | |
20 | ||
21 | ```toml | |
22 | [dependencies] | |
23 | syn = "0.11" | |
24 | quote = "0.3" | |
25 | ||
26 | [lib] | |
27 | proc-macro = true | |
28 | ``` | |
29 | ||
30 | ```rust | |
31 | extern crate proc_macro; | |
32 | use proc_macro::TokenStream; | |
33 | ||
34 | extern crate syn; | |
35 | ||
36 | #[macro_use] | |
37 | extern crate quote; | |
38 | ||
39 | #[proc_macro_derive(MyMacro)] | |
40 | pub fn my_macro(input: TokenStream) -> TokenStream { | |
41 | let source = input.to_string(); | |
42 | ||
43 | // Parse the string representation into a syntax tree | |
44 | let ast = syn::parse_derive_input(&source).unwrap(); | |
45 | ||
46 | // Build the output, possibly using quasi-quotation | |
47 | let expanded = quote! { | |
48 | // ... | |
49 | }; | |
50 | ||
51 | // Parse back to a token stream and return it | |
52 | expanded.parse().unwrap() | |
53 | } | |
54 | ``` | |
55 | ||
56 | ## Complete example | |
57 | ||
58 | Suppose we have the following simple trait which returns the number of fields in | |
59 | a struct: | |
60 | ||
61 | ```rust | |
62 | trait NumFields { | |
63 | fn num_fields() -> usize; | |
64 | } | |
65 | ``` | |
66 | ||
67 | A complete Macros 1.1 implementation of `#[derive(NumFields)]` based on `syn` | |
68 | and [`quote`](https://github.com/dtolnay/quote) looks like this: | |
69 | ||
70 | ```rust | |
71 | extern crate proc_macro; | |
72 | use proc_macro::TokenStream; | |
73 | ||
74 | extern crate syn; | |
75 | ||
76 | #[macro_use] | |
77 | extern crate quote; | |
78 | ||
79 | #[proc_macro_derive(NumFields)] | |
80 | pub fn num_fields(input: TokenStream) -> TokenStream { | |
81 | let source = input.to_string(); | |
82 | ||
83 | // Parse the string representation into a syntax tree | |
84 | let ast = syn::parse_derive_input(&source).unwrap(); | |
85 | ||
86 | // Build the output | |
87 | let expanded = expand_num_fields(&ast); | |
88 | ||
89 | // Return the generated impl as a TokenStream | |
90 | expanded.parse().unwrap() | |
91 | } | |
92 | ||
93 | fn expand_num_fields(ast: &syn::DeriveInput) -> quote::Tokens { | |
94 | let n = match ast.body { | |
95 | syn::Body::Struct(ref data) => data.fields().len(), | |
96 | syn::Body::Enum(_) => panic!("#[derive(NumFields)] can only be used with structs"), | |
97 | }; | |
98 | ||
99 | // Used in the quasi-quotation below as `#name` | |
100 | let name = &ast.ident; | |
101 | ||
102 | // Helper is provided for handling complex generic types correctly and effortlessly | |
103 | let (impl_generics, ty_generics, where_clause) = ast.generics.split_for_impl(); | |
104 | ||
105 | quote! { | |
106 | // The generated impl | |
107 | impl #impl_generics ::mycrate::NumFields for #name #ty_generics #where_clause { | |
108 | fn num_fields() -> usize { | |
109 | #n | |
110 | } | |
111 | } | |
112 | } | |
113 | } | |
114 | ``` | |
115 | ||
116 | For a more elaborate example that involves trait bounds, enums, and different | |
117 | kinds of structs, check out [`DeepClone`] and [`deep-clone-derive`]. | |
118 | ||
119 | [`DeepClone`]: https://github.com/asajeffrey/deep-clone | |
120 | [`deep-clone-derive`]: https://github.com/asajeffrey/deep-clone/blob/master/deep-clone-derive/lib.rs | |
121 | ||
122 | ## Testing | |
123 | ||
124 | Macros 1.1 has a restriction that your proc-macro crate must export nothing but | |
125 | `proc_macro_derive` functions, and also `proc_macro_derive` procedural macros | |
126 | cannot be used from the same crate in which they are defined. These restrictions | |
127 | may be lifted in the future but for now they make writing tests a bit trickier | |
128 | than for other types of code. | |
129 | ||
130 | In particular, you will not be able to write test functions like `#[test] fn | |
131 | it_works() { ... }` in line with your code. Instead, either put tests in a | |
132 | [`tests` directory](https://doc.rust-lang.org/book/testing.html#the-tests-directory) | |
133 | or in a separate crate entirely. | |
134 | ||
135 | Additionally, if your procedural macro implements a particular trait, that trait | |
136 | must be defined in a separate crate from the procedural macro. | |
137 | ||
138 | As a concrete example, suppose your procedural macro crate is called `my_derive` | |
139 | and it implements a trait called `my_crate::MyTrait`. Your unit tests for the | |
140 | procedural macro can go in `my_derive/tests/test.rs` or into a separate crate | |
141 | `my_tests/tests/test.rs`. Either way the test would look something like this: | |
142 | ||
143 | ```rust | |
144 | #[macro_use] | |
145 | extern crate my_derive; | |
146 | ||
147 | extern crate my_crate; | |
148 | use my_crate::MyTrait; | |
149 | ||
150 | #[test] | |
151 | fn it_works() { | |
152 | #[derive(MyTrait)] | |
153 | struct S { /* ... */ } | |
154 | ||
155 | /* test the thing */ | |
156 | } | |
157 | ``` | |
158 | ||
159 | ## Debugging | |
160 | ||
161 | When developing a procedural macro it can be helpful to look at what the | |
162 | generated code looks like. Use `cargo rustc -- -Zunstable-options | |
163 | --pretty=expanded` or the | |
164 | [`cargo expand`](https://github.com/dtolnay/cargo-expand) subcommand. | |
165 | ||
166 | To 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 | |
168 | test cases, run `cargo expand --test the_test_case` where the last argument is | |
169 | the name of the test file without the `.rs` extension. | |
170 | ||
171 | This write-up by Brandon W Maister discusses debugging in more detail: | |
172 | [Debugging Rust's new Custom Derive | |
173 | system](https://quodlibetor.github.io/posts/debugging-rusts-new-custom-derive-system/). | |
174 | ||
175 | ## Optional features | |
176 | ||
177 | Syn puts a lot of functionality behind optional features in order to optimize | |
178 | compile time for the most common use cases. These are the available features and | |
179 | their effect on compile time. Dependencies are included in the compile times. | |
180 | ||
181 | Features | Compile time | Functionality | |
182 | --- | --- | --- | |
183 | *(none)* | 3 sec | The data structures representing the AST of Rust structs, enums, and types. | |
184 | parsing | 6 sec | Parsing Rust source code containing structs and enums into an AST. | |
185 | printing | 4 sec | Printing an AST of structs and enums as Rust source code. | |
186 | **parsing, printing** | **6 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. | |
187 | full | 4 sec | The data structures representing the full AST of all possible Rust code. | |
188 | full, parsing | 9 sec | Parsing any valid Rust source code to an AST. | |
189 | full, printing | 6 sec | Turning an AST into Rust source code. | |
190 | full, parsing, printing | 11 sec | Parsing and printing any Rust syntax. | |
191 | ||
192 | ## License | |
193 | ||
194 | Licensed under either of | |
195 | ||
196 | * Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0) | |
197 | * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT) | |
198 | ||
199 | at your option. | |
200 | ||
201 | ### Contribution | |
202 | ||
203 | Unless you explicitly state otherwise, any contribution intentionally submitted | |
204 | for inclusion in this crate by you, as defined in the Apache-2.0 license, shall | |
205 | be dual licensed as above, without any additional terms or conditions. |