procedural_masquerade/lib.rs
1//! # Custom `derive` pretending to be functional procedural macros on Rust 1.15
2//!
3//! This crate enables creating function-like macros (invoked as `foo!(...)`)
4//! with a procedural component,
5//! based on both custom `derive` (a.k.a. *Macros 1.1*) and `macro_rules!`.
6//!
7//! This convoluted mechanism enables such macros to run on stable Rust 1.15,
8//! even though functional procedural macros (a.k.a. *Macros 2.0*) are not available yet.
9//!
10//! A library defining such a macro needs two crates: a “normal” one, and a `proc-macro` one.
11//! In the example below we’ll call them `libfoo` and `libfoo-macros`, respectively.
12//!
13//! # Credits
14//!
15//! The trick that makes this crate work
16//! is based on an idea from [David Tolnay](https://github.com/dtolnay).
17//! Many thanks!
18//!
19//! # Example
20//!
21//! As a simple example, we’re going to re-implement the `stringify!` macro.
22//! This is useless since `stringify!` already exists in the standard library,
23//! and a bit absurd since this crate uses `stringify!` internally.
24//!
25//! Nevertheless, it serves as a simple example to demonstrate the use of this crate.
26//!
27//! ## The `proc-macro` crate
28//!
29//! The minimal `Cargo.toml` file is typical for Macros 1.1:
30//!
31//! ```toml
32//! [package]
33//! name = "libfoo-macros"
34//! version = "1.0.0"
35//!
36//! [lib]
37//! proc-macro = true
38//! ```
39//!
40//! In the code, we define the procedural part of our macro in a function.
41//! This function will not be used directly by end users,
42//! but it still needs to be re-exported to them
43//! (because of limitations in `macro_rules!`).
44//!
45//! To avoid name collisions, we include a long and explicit prefix in the function’s name.
46//!
47//! The function takes a string containing arbitrary Rust tokens,
48//! and returns a string that is parsed as *items*.
49//! The returned string can contain constants, statics, functions, `impl`s, etc.,
50//! but not expressions directly.
51//!
52//! ```rust
53//! #[macro_use] extern crate procedural_masquerade;
54//! extern crate proc_macro;
55//!
56//! define_proc_macros! {
57//! #[allow(non_snake_case)]
58//! pub fn foo_internal__stringify_const(input: &str) -> String {
59//! format!("const STRINGIFIED: &'static str = {:?};", input)
60//! }
61//! }
62//! ```
63//!
64//! A less trivial macro would probably use
65//! the [`syn`](https://github.com/dtolnay/syn/) crate to parse its input
66//! and the [`quote`](https://github.com/dtolnay/quote) crate to generate its output.
67//!
68//! ## The library crate
69//!
70//! ```toml
71//! [package]
72//! name = "libfoo"
73//! version = "1.0.0"
74//!
75//! [dependencies]
76//! libfoo-macros = {path = "./macros", version = "1.0"}
77//! ```
78//!
79//! ```rust
80//! #[macro_use] extern crate libfoo_macros; // (1)
81//!
82//! pub use libfoo_macros::*; // (2)
83//!
84//! define_invoke_proc_macro!(libfoo__invoke_proc_macro); // (3)
85//!
86//! #[macro_export]
87//! macro_rules! foo_stringify { // (4)
88//! ( $( $tts: tt ) ) => {
89//! { // (5)
90//! libfoo__invoke_proc_macro! { // (6)
91//! foo_internal__stringify_const!( $( $tts ) ) // (7)
92//! }
93//! STRINGIFIED // (8)
94//! }
95//! }
96//! }
97//! ```
98//!
99//! Let’s go trough the numbered lines one by one:
100//!
101//! 1. `libfoo` depends on `libfoo-macros`, and imports its macros.
102//! 2. Everything exported by `libfoo-macros` (which is one custom `derive`)
103//! is re-exported to users of `libfoo`.
104//! They’re not expected to use it directly,
105//! but expansion of the `foo_stringify` macro needs it.
106//! 3. This macro invocation defines yet another macro, called `libfoo__invoke_proc_macro`,
107//! which is also exported.
108//! This indirection is necessary
109//! because re-exporting `macro_rules!` macros doesn’t work currently,
110//! and once again it is used by the expansion of `foo_stringify`.
111//! Again, we use a long prefix to avoid name collisions.
112//! 4. Finally, we define the macro that we really want.
113//! This one has a name that users will use.
114//! 5. The expansion of this macro will define some items,
115//! whose names are not hygienic in `macro_rules`.
116//! So we wrap everything in an extra `{…}` block to prevent these names for leaking.
117//! 6. Here we use the macro defined in (3),
118//! which allows us to write something that look like invoking a functional procedural macro,
119//! but really uses a custom `derive`.
120//! This will define a type called `ProceduralMasqueradeDummyType`,
121//! as a placeholder to use `derive`.
122//! If `libfoo__invoke_proc_macro!` is to be used more than once,
123//! each use needs to be nested in another block
124//! so that the names of multiple dummy types don’t collide.
125//! 7. In addition to the dummy type,
126//! the items returned by our procedural component are inserted here.
127//! (In this case the `STRINGIFIED` constant.)
128//! 8. Finally, we write the expression that we want the macro to evaluate to.
129//! This expression can use parts of `foo_stringify`’s input,
130//! it can contain control-flow statements like `return` or `continue`,
131//! and of course refer to procedurally-defined items.
132//!
133//! This macro can be used in an expression context.
134//! It expands to a block-expression that contains some items (as an implementation detail)
135//! and ends with another expression.
136//!
137//! ## For users
138//!
139//! Users of `libfoo` don’t need to worry about any of these implementation details.
140//! They can use the `foo_stringify` macro as if it were a simle `macro_rules` macro:
141//!
142//! ```rust
143//! #[macro_use] extern crate libfoo;
144//!
145//! fn main() {
146//! do_something(foo_stringify!(1 + 2));
147//! }
148//!
149//! fn do_something(_: &str) { /* ... */ }
150//! ```
151//!
152//! # More
153//!
154//! To see a more complex example, look at
155//! [`cssparser`’s `src/macros.rs`](https://github.com/servo/rust-cssparser/blob/master/src/macros.rs)
156//! and
157//! [`cssparser-macros`’s `macros/lib.rs`](https://github.com/servo/rust-cssparser/blob/master/macros/lib.rs).
158
159/// This macro wraps `&str -> String` functions
160/// in custom `derive` implementations with `#[proc_macro_derive]`.
161///
162/// See crate documentation for details.
163#[macro_export]
164macro_rules! define_proc_macros {
165 (
166 $(
167 $( #[$attr:meta] )*
168 pub fn $proc_macro_name: ident ($input: ident : &str) -> String
169 $body: block
170 )+
171 ) => {
172 $(
173 $( #[$attr] )*
174 #[proc_macro_derive($proc_macro_name)]
175 pub fn $proc_macro_name(derive_input: ::proc_macro::TokenStream)
176 -> ::proc_macro::TokenStream {
177 fn wrapped($input: &str) -> String {
178 $body
179 }
180
181 // syn uses a huge amount of stack in debug mode.
182 let derive_input_string = derive_input.to_string();
183 let handle =
184 ::std::thread::Builder::new().stack_size(128 * 1024 * 1024).spawn(move || {
185 wrapped($crate::_extract_input(&derive_input_string))
186 }).unwrap();
187
188 handle.join().unwrap().parse().unwrap()
189 }
190 )+
191 }
192}
193
194/// Implementation detail of `define_proc_macros!`.
195///
196/// **This function is not part of the public API. It can change or be removed between any versions.**
197#[doc(hidden)]
198pub fn _extract_input(derive_input: &str) -> &str {
199 let mut input = derive_input;
200
201 for expected in &[
202 "#",
203 "[",
204 "allow",
205 "(",
206 "unused",
207 ")",
208 "]",
209 "enum",
210 "ProceduralMasqueradeDummyType",
211 "{",
212 "Input",
213 "=",
214 "(",
215 "0",
216 ",",
217 "stringify",
218 "!",
219 "(",
220 ] {
221 input = input.trim_start();
222 assert!(
223 input.starts_with(expected),
224 "expected prefix {:?} not found in {:?}",
225 expected,
226 derive_input
227 );
228 input = &input[expected.len()..];
229 }
230
231 for expected in [")", ")", ".", "0", ",", "}"].iter().rev() {
232 input = input.trim_end();
233 assert!(
234 input.ends_with(expected),
235 "expected suffix {:?} not found in {:?}",
236 expected,
237 derive_input
238 );
239 let end = input.len() - expected.len();
240 input = &input[..end];
241 }
242
243 input
244}
245
246/// This macro expands to the definition of another macro (whose name is given as a parameter).
247///
248/// See crate documentation for details.
249#[macro_export]
250macro_rules! define_invoke_proc_macro {
251 ($macro_name: ident) => {
252 /// Implementation detail of other macros in this crate.
253 #[doc(hidden)]
254 #[macro_export]
255 macro_rules! $macro_name {
256 ($proc_macro_name: ident ! $paren: tt) => {
257 #[derive($proc_macro_name)]
258 #[allow(unused)]
259 enum ProceduralMasqueradeDummyType {
260 // The magic happens here.
261 //
262 // We use an `enum` with an explicit discriminant
263 // because that is the only case where a type definition
264 // can contain a (const) expression.
265 //
266 // `(0, "foo").0` evalutes to 0, with the `"foo"` part ignored.
267 //
268 // By the time the `#[proc_macro_derive]` function
269 // implementing `#[derive($proc_macro_name)]` is called,
270 // `$paren` has already been replaced with the input of this inner macro,
271 // but `stringify!` has not been expanded yet.
272 //
273 // This how arbitrary tokens can be inserted
274 // in the input to the `#[proc_macro_derive]` function.
275 //
276 // Later, `stringify!(...)` is expanded into a string literal
277 // which is then ignored.
278 // Using `stringify!` enables passing arbitrary tokens
279 // rather than only what can be parsed as a const expression.
280 Input = (0, stringify! $paren ).0,
281 }
282 }
283 }
284 };
285}