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 and 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_masquarade;
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
+ //! cssparser-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 the other `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 `ProceduralMasquaradeDummyType`,
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
+ /////
160
+ ///// This indirection is necessary because re-exporting `macro_rules!` macros doesn’t work.
161
+ ///// Without it, when a library `libfoo` defines a macro whose expansion uses directly
162
+ ///// a macro
163
+
164
+ /// This macro wraps `&str -> String` functions
165
+ /// in custom `derive` implementations with `#[proc_macro_derive]`.
166
+ ///
167
+ /// See crate documentation for details.
1
168
#[ macro_export]
2
169
macro_rules! define_proc_macros {
3
170
(
4
171
$(
5
172
$( #[ $attr: meta] ) *
6
- pub fn $func : ident( $input: ident: & str ) -> String
7
- $body: block
173
+ pub fn $proc_macro_name : ident ( $input: ident : & str ) -> String
174
+ $body: block
8
175
) +
9
176
) => {
10
177
$(
11
178
$( #[ $attr] ) *
12
- #[ proc_macro_derive( $func ) ]
13
- pub fn $func ( input: :: proc_macro:: TokenStream ) -> :: proc_macro:: TokenStream {
179
+ #[ proc_macro_derive( $proc_macro_name ) ]
180
+ pub fn $proc_macro_name ( input: :: proc_macro:: TokenStream ) -> :: proc_macro:: TokenStream {
14
181
// Use another function to hide this one’s local variables from $block
15
182
#[ inline]
16
183
fn implementation( $input: & str ) -> String $body
@@ -22,10 +189,10 @@ macro_rules! define_proc_macros {
22
189
normalized. push( ' ' ) ;
23
190
}
24
191
25
- let prefix = "#[allow(unused)] enum Dummy { Input = (0, stringify!(" ;
192
+ let prefix = "#[allow(unused)] enum ProceduralMasqueradeDummyType { Input = (0, stringify!(" ;
26
193
let suffix = ")).0, } " ;
27
- assert!( normalized. starts_with( prefix) , "Unexpected proc_macro_derive input {:?}" , input) ;
28
- assert!( normalized. ends_with( suffix) , "Unexpected proc_macro_derive input {:?}" , input) ;
194
+ assert!( normalized. starts_with( prefix) , "expected prefix not found in {:?}" , input) ;
195
+ assert!( normalized. ends_with( suffix) , "expected suffix not found in {:?}" , input) ;
29
196
30
197
let start = prefix. len( ) ;
31
198
let end = normalized. len( ) - suffix. len( ) ;
@@ -36,6 +203,9 @@ macro_rules! define_proc_macros {
36
203
}
37
204
}
38
205
206
+ /// This macro expands to the definition of another macro (whose name is given as a parameter).
207
+ ///
208
+ /// See crate documentation for details.
39
209
#[ macro_export]
40
210
macro_rules! define_invoke_proc_macro {
41
211
( $macro_name: ident) => {
@@ -44,7 +214,27 @@ macro_rules! define_invoke_proc_macro {
44
214
( $proc_macro_name: ident ! $paren: tt) => {
45
215
#[ derive( $proc_macro_name) ]
46
216
#[ allow( unused) ]
47
- enum Dummy {
217
+ enum ProceduralMasquaradeDummyType {
218
+ // The magic happens here.
219
+ //
220
+ // We use an `enum` with an explicit discriminant
221
+ // because that is the only case where a type definition
222
+ // can contain a (const) expression.
223
+ //
224
+ // `(0, "foo").0` evalutes to 0, with the `"foo"` part ignored.
225
+ //
226
+ // By the time the `#[proc_macro_derive]` function
227
+ // implementing `#[derive($proc_macro_name)]` is called,
228
+ // `$paren` has already been replaced with the input of this inner macro,
229
+ // but `stringify!` has not been expanded yet.
230
+ //
231
+ // This how arbitrary tokens can be inserted
232
+ // in the input to the `#[proc_macro_derive]` function.
233
+ //
234
+ // Later, `stringify!(...)` is expanded into a string literal
235
+ // which is then ignored.
236
+ // Using `stringify!` enables passing arbitrary tokens
237
+ // rather than only what can be parsed as a const expression.
48
238
Input = ( 0 , stringify! $paren ) . 0
49
239
}
50
240
}
0 commit comments