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.
1168#[ macro_export]
2169macro_rules! define_proc_macros {
3170 (
4171 $(
5172 $( #[ $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
8175 ) +
9176 ) => {
10177 $(
11178 $( #[ $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 {
14181 // Use another function to hide this one’s local variables from $block
15182 #[ inline]
16183 fn implementation( $input: & str ) -> String $body
@@ -22,10 +189,10 @@ macro_rules! define_proc_macros {
22189 normalized. push( ' ' ) ;
23190 }
24191
25- let prefix = "#[allow(unused)] enum Dummy { Input = (0, stringify!(" ;
192+ let prefix = "#[allow(unused)] enum ProceduralMasqueradeDummyType { Input = (0, stringify!(" ;
26193 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) ;
29196
30197 let start = prefix. len( ) ;
31198 let end = normalized. len( ) - suffix. len( ) ;
@@ -36,6 +203,9 @@ macro_rules! define_proc_macros {
36203 }
37204}
38205
206+ /// This macro expands to the definition of another macro (whose name is given as a parameter).
207+ ///
208+ /// See crate documentation for details.
39209#[ macro_export]
40210macro_rules! define_invoke_proc_macro {
41211 ( $macro_name: ident) => {
@@ -44,7 +214,27 @@ macro_rules! define_invoke_proc_macro {
44214 ( $proc_macro_name: ident ! $paren: tt) => {
45215 #[ derive( $proc_macro_name) ]
46216 #[ 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.
48238 Input = ( 0 , stringify! $paren ) . 0
49239 }
50240 }
0 commit comments