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}