Skip to main content

proc_macro_hack/
lib.rs

1//! ## Defining procedural macros
2//!
3//! Two crates are required to define a macro.
4//!
5//! ### The declaration crate
6//!
7//! This crate is allowed to contain other public things if you need, for example
8//! traits or functions or ordinary macros.
9//!
10//! https://github.com/dtolnay/proc-macro-hack/tree/master/demo-hack
11//!
12//! ```rust
13//! #[macro_use]
14//! extern crate proc_macro_hack;
15//!
16//! // This is what allows the users to depend on just your
17//! // declaration crate rather than both crates.
18//! #[allow(unused_imports)]
19//! #[macro_use]
20//! extern crate demo_hack_impl;
21//! #[doc(hidden)]
22//! pub use demo_hack_impl::*;
23//!
24//! proc_macro_expr_decl! {
25//!     /// Add one to an expression.
26//!     add_one! => add_one_impl
27//! }
28//!
29//! proc_macro_item_decl! {
30//!     /// A function that always returns 2.
31//!     two_fn! => two_fn_impl
32//! }
33//! # fn main() {}
34//! ```
35//!
36//! ### The implementation crate
37//!
38//! This crate must contain nothing but procedural macros. Private helper functions
39//! and private modules are fine but nothing can be public.
40//!
41//! https://github.com/dtolnay/proc-macro-hack/tree/master/demo-hack-impl
42//!
43//! ```rust,ignore
44//! #[macro_use]
45//! extern crate proc_macro_hack;
46//!
47//! proc_macro_expr_impl! {
48//!     /// Add one to an expression.
49//!     pub fn add_one_impl(input: &str) -> String {
50//!         format!("1 + {}", input)
51//!     }
52//! }
53//!
54//! proc_macro_item_impl! {
55//!     /// A function that always returns 2.
56//!     pub fn two_fn_impl(input: &str) -> String {
57//!         format!("fn {}() -> u8 {{ 2 }}", input)
58//!     }
59//! }
60//! ```
61//!
62//! Both crates depend on `proc-macro-hack`:
63//!
64//! ```toml
65//! [dependencies]
66//! proc-macro-hack = "0.4"
67//! ```
68//!
69//! Additionally, your implementation crate (but not your declaration crate) is a
70//! proc macro:
71//!
72//! ```toml
73//! [lib]
74//! proc-macro = true
75//! ```
76//!
77//! ## Using procedural macros
78//!
79//! Users of your crate depend on your declaration crate (not your implementation
80//! crate), then use your procedural macros as though it were magic. They even get
81//! reasonable error messages if your procedural macro panics.
82//!
83//! https://github.com/dtolnay/proc-macro-hack/tree/master/example
84//!
85//! ```rust
86//! #[macro_use]
87//! extern crate demo_hack;
88//!
89//! two_fn!(two);
90//!
91//! fn main() {
92//!     let nine = add_one!(two()) + add_one!(2 + 3);
93//!     println!("nine = {}", nine);
94//! }
95//! ```
96//!
97//! ---
98//!
99//! # Expansion of expression macros
100//!
101//! ```rust,ignore
102//! m!(ARGS)
103//! ```
104//!
105//! ... expands to ...
106//!
107//! ```rust,ignore
108//! {
109//!     #[derive(m_impl)]
110//!     #[allow(unused)]
111//!     enum ProcMacroHack {
112//!         Input = (stringify!(ARGS), 0).1,
113//!     }
114//!     proc_macro_call!()
115//! }
116//! ```
117//!
118//! ... expands to ...
119//!
120//! ```rust,ignore
121//! {
122//!     macro_rules! proc_macro_call {
123//!         () => { RESULT }
124//!     }
125//!     proc_macro_call!()
126//! }
127//! ```
128//!
129//! ... expands to ...
130//!
131//! ```rust,ignore
132//! {
133//!     RESULT
134//! }
135//! ```
136//!
137//! # Expansion of item macros
138//!
139//! ```rust,ignore
140//! m!(ARGS);
141//! ```
142//!
143//! ... expands to ...
144//!
145//! ```rust,ignore
146//! #[derive(m_impl)]
147//! #[allow(unused)]
148//! enum ProcMacroHack {
149//!     Input = (stringify!(ARGS), 0).1,
150//! }
151//! ```
152//!
153//! ... expands to ...
154//!
155//! ```rust,ignore
156//! RESULT
157//! ```
158
159#![no_std]
160
161// Allow the "unused" #[macro_use] because there is a different un-ignorable
162// warning otherwise:
163//
164//    proc macro crates and `#[no_link]` crates have no effect without `#[macro_use]`
165#[allow(unused_imports)]
166#[macro_use]
167extern crate proc_macro_hack_impl;
168#[doc(hidden)]
169pub use proc_macro_hack_impl::*;
170
171/// Declare a hacky procedural macro that expands to an expression.
172///
173/// ```rust
174/// # #[macro_use] extern crate proc_macro_hack;
175/// proc_macro_expr_decl! {
176///     /// Add one to an expression.
177///     add_one! => add_one_impl
178/// }
179/// # fn main() {}
180/// ```
181#[macro_export]
182macro_rules! proc_macro_expr_decl {
183    (#[$attr:meta] $($rest:tt)+) => {
184        proc_macro_expr_decl_helper!((#[$attr]) $($rest)+);
185    };
186    ($name:ident ! => $name_impl:ident) => {
187        proc_macro_expr_decl_helper!(() $name ! => $name_impl);
188    };
189}
190
191#[doc(hidden)]
192#[macro_export]
193macro_rules! proc_macro_expr_decl_helper {
194    (($($attrs:tt)*) #[$first:meta] $($rest:tt)+) => {
195        proc_macro_expr_decl_helper!(($($attrs)* #[$first]) $($rest)+);
196    };
197    (($($attrs:tt)*) $name:ident ! => $name_impl:ident) => {
198        #[derive(ProcMacroHackExpr)]
199        #[allow(unused, non_camel_case_types)]
200        $($attrs)*
201        enum $name {
202            $name_impl
203        }
204    };
205    (($($attrs:tt)*) $name:ident ! => $name_impl:ident #[$first:meta] $($rest:tt)+) => {
206        proc_macro_expr_decl_helper!(($($attrs)*) $name ! => $name_impl);
207        proc_macro_expr_decl_helper!((#[$first]) $($rest)+);
208    };
209}
210
211/// Declare a hacky procedural macro that expands to items.
212///
213/// ```rust
214/// # #[macro_use] extern crate proc_macro_hack;
215/// proc_macro_item_decl! {
216///     /// A function that always returns 2.
217///     two_fn! => two_fn_impl
218/// }
219/// # fn main() {}
220/// ```
221#[macro_export]
222macro_rules! proc_macro_item_decl {
223    (#[$attr:meta] $($rest:tt)+) => {
224        proc_macro_item_decl_helper!((#[$attr]) $($rest)+);
225    };
226    ($name:ident ! => $name_impl:ident) => {
227        proc_macro_item_decl_helper!(() $name ! => $name_impl);
228    };
229}
230
231#[doc(hidden)]
232#[macro_export]
233macro_rules! proc_macro_item_decl_helper {
234    (($($attrs:tt)*) #[$first:meta] $($rest:tt)+) => {
235        proc_macro_item_decl_helper!(($($attrs)* #[$first]) $($rest)+);
236    };
237    (($($attrs:tt)*) $name:ident ! => $name_impl:ident) => {
238        #[derive(ProcMacroHackItem)]
239        #[allow(unused, non_camel_case_types)]
240        $($attrs)*
241        enum $name {
242            $name_impl
243        }
244    };
245    (($($attrs:tt)*) $name:ident ! => $name_impl:ident #[$first:meta] $($rest:tt)+) => {
246        proc_macro_item_decl_helper!(($($attrs)*) $name ! => $name_impl);
247        proc_macro_item_decl_helper!((#[$first]) $($rest)+);
248    };
249}
250
251/// Implement a hacky procedural macro that expands to an expression.
252///
253/// ```rust,ignore
254/// proc_macro_expr_impl! {
255///     /// Add one to an expression.
256///     pub fn add_one_impl(input: &str) -> String {
257///         format!("1 + {}", input)
258///     }
259/// }
260/// ```
261#[macro_export]
262macro_rules! proc_macro_expr_impl {
263    ($(
264        $( #[$attr:meta] )*
265        pub fn $func:ident($input:ident: &str) -> String $body:block
266    )+) => {
267        $(
268            mod $func {
269                extern crate proc_macro;
270                pub use self::proc_macro::TokenStream;
271            }
272
273            // Parses an input that looks like:
274            //
275            // ```
276            // #[allow(unused)]
277            // enum ProcMacroHack {
278            //     Input = (stringify!(ARGS), 0).1,
279            // }
280            // ```
281            $( #[$attr] )*
282            #[proc_macro_derive($func)]
283            pub fn $func(input: $func::TokenStream) -> $func::TokenStream {
284                let source = input.to_string();
285                let mut tokens = source.trim();
286
287                for &prefix in &[
288                    "#",
289                    "[",
290                    "allow",
291                    "(",
292                    "unused",
293                    ")",
294                    "]",
295                    "enum",
296                    "ProcMacroHack",
297                    "{",
298                    "Input",
299                    "=",
300                    "(",
301                    "stringify",
302                    "!",
303                    "(",
304                ] {
305                    assert!(tokens.starts_with(prefix));
306                    tokens = &tokens[prefix.len()..].trim();
307                }
308
309                for &suffix in &[
310                    "}",
311                    ",",
312                    "1",
313                    ".",
314                    ")",
315                    "0",
316                    ",",
317                    ")",
318                ] {
319                    if suffix == "," && !tokens.ends_with(suffix) {
320                        continue;
321                    }
322                    assert!(tokens.ends_with(suffix));
323                    tokens = &tokens[..tokens.len() - suffix.len()].trim();
324                }
325
326                fn func($input: &str) -> String $body
327
328                format!("
329                    macro_rules! proc_macro_call {{
330                        () => {{
331                            {}
332                        }}
333                    }}
334                ", func(tokens)).parse().unwrap()
335            }
336        )+
337    };
338}
339
340/// Implement a hacky procedural macro that expands to items.
341///
342/// ```rust,ignore
343/// proc_macro_item_impl! {
344///     /// A function that always returns 2.
345///     pub fn two_fn_impl(input: &str) -> String {
346///         format!("fn {}() -> u8 {{ 2 }}", input)
347///     }
348/// }
349/// ```
350#[macro_export]
351macro_rules! proc_macro_item_impl {
352    ($(
353        $( #[$attr:meta] )*
354        pub fn $func:ident($input:ident: &str) -> String $body:block
355    )+) => {
356        $(
357            mod $func {
358                extern crate proc_macro;
359                pub use self::proc_macro::TokenStream;
360            }
361
362            // Parses an input that looks like:
363            //
364            // ```
365            // #[allow(unused)]
366            // enum ProcMacroHack {
367            //     Input = (stringify!(ARGS), 0).1,
368            // }
369            // ```
370            $( #[$attr] )*
371            #[proc_macro_derive($func)]
372            pub fn $func(input: $func::TokenStream) -> $func::TokenStream {
373                let source = input.to_string();
374                let mut tokens = source.trim();
375
376                for &prefix in &[
377                    "#",
378                    "[",
379                    "allow",
380                    "(",
381                    "unused",
382                    ")",
383                    "]",
384                    "enum",
385                    "ProcMacroHack",
386                    "{",
387                    "Input",
388                    "=",
389                    "(",
390                    "stringify",
391                    "!",
392                    "(",
393                ] {
394                    assert!(tokens.starts_with(prefix));
395                    tokens = &tokens[prefix.len()..].trim();
396                }
397
398                for &suffix in &[
399                    "}",
400                    ",",
401                    "1",
402                    ".",
403                    ")",
404                    "0",
405                    ",",
406                    ")",
407                ] {
408                    if suffix == "," && !tokens.ends_with(suffix) {
409                        continue;
410                    }
411                    assert!(tokens.ends_with(suffix));
412                    tokens = &tokens[..tokens.len() - suffix.len()].trim();
413                }
414
415                fn func($input: &str) -> String $body
416
417                func(tokens).parse().unwrap()
418            }
419        )+
420    };
421}