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