1//! [![github]](https://github.com/dtolnay/proc-macro-hack) [![crates-io]](https://crates.io/crates/proc-macro-hack) [![docs-rs]](https://docs.rs/proc-macro-hack)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! <table><tr><td><hr>
10//! <b>Note:</b> <i>As of Rust 1.45 this crate is superseded by native support
11//! for #[proc_macro] in expression position. Only consider using this crate if
12//! you care about supporting compilers between 1.31 and 1.45.</i>
13//! <hr></td></tr></table>
14//!
15//! Since Rust 1.30, the language supports user-defined function-like procedural
16//! macros. However these can only be invoked in item position, not in
17//! statements or expressions.
18//!
19//! This crate implements an alternative type of procedural macro that can be
20//! invoked in statement or expression position.
21//!
22//! # Defining procedural macros
23//!
24//! Two crates are required to define a procedural macro.
25//!
26//! ## The implementation crate
27//!
28//! This crate must contain nothing but procedural macros. Private helper
29//! functions and private modules are fine but nothing can be public.
30//!
31//! [&raquo; example of an implementation crate][demo-hack-impl]
32//!
33//! Just like you would use a #\[proc_macro\] attribute to define a natively
34//! supported procedural macro, use proc-macro-hack's #\[proc_macro_hack\]
35//! attribute to define a procedural macro that works in expression position.
36//! The function signature is the same as for ordinary function-like procedural
37//! macros.
38//!
39//! ```
40//! # extern crate proc_macro;
41//! #
42//! use proc_macro::TokenStream;
43//! use proc_macro_hack::proc_macro_hack;
44//! use quote::quote;
45//! use syn::{parse_macro_input, Expr};
46//!
47//! # const IGNORE: &str = stringify! {
48//! #[proc_macro_hack]
49//! # };
50//! pub fn add_one(input: TokenStream) -> TokenStream {
51//! let expr = parse_macro_input!(input as Expr);
52//! TokenStream::from(quote! {
53//! 1 + (#expr)
54//! })
55//! }
56//! #
57//! # fn main() {}
58//! ```
59//!
60//! ## The declaration crate
61//!
62//! This crate is allowed to contain other public things if you need, for
63//! example traits or functions or ordinary macros.
64//!
65//! [&raquo; example of a declaration crate][demo-hack]
66//!
67//! Within the declaration crate there needs to be a re-export of your
68//! procedural macro from the implementation crate. The re-export also carries a
69//! \#\[proc_macro_hack\] attribute.
70//!
71//! ```
72//! use proc_macro_hack::proc_macro_hack;
73//!
74//! /// Add one to an expression.
75//! ///
76//! /// (Documentation goes here on the re-export, not in the other crate.)
77//! #[proc_macro_hack]
78//! pub use demo_hack_impl::add_one;
79//! #
80//! # fn main() {}
81//! ```
82//!
83//! Both crates depend on `proc-macro-hack`:
84//!
85//! ```toml
86//! [dependencies]
87//! proc-macro-hack = "0.5"
88//! ```
89//!
90//! Additionally, your implementation crate (but not your declaration crate) is
91//! a proc macro crate:
92//!
93//! ```toml
94//! [lib]
95//! proc-macro = true
96//! ```
97//!
98//! # Using procedural macros
99//!
100//! Users of your crate depend on your declaration crate (not your
101//! implementation crate), then use your procedural macros as usual.
102//!
103//! [&raquo; example of a downstream crate][example]
104//!
105//! ```
106//! use demo_hack::add_one;
107//!
108//! fn main() {
109//! let two = 2;
110//! let nine = add_one!(two) + add_one!(2 + 3);
111//! println!("nine = {}", nine);
112//! }
113//! ```
114//!
115//! [demo-hack-impl]: https://github.com/dtolnay/proc-macro-hack/tree/master/demo-hack-impl
116//! [demo-hack]: https://github.com/dtolnay/proc-macro-hack/tree/master/demo-hack
117//! [example]: https://github.com/dtolnay/proc-macro-hack/tree/master/example
118//!
119//! # Limitations
120//!
121//! - Only proc macros in expression position are supported. Proc macros in
122//! pattern position ([#20]) are not supported.
123//!
124//! - By default, nested invocations are not supported i.e. the code emitted by
125//! a proc-macro-hack macro invocation cannot contain recursive calls to the
126//! same proc-macro-hack macro nor calls to any other proc-macro-hack macros.
127//! Use [`proc-macro-nested`] if you require support for nested invocations.
128//!
129//! - By default, hygiene is structured such that the expanded code can't refer
130//! to local variables other than those passed by name somewhere in the macro
131//! input. If your macro must refer to *local* variables that don't get named
132//! in the macro input, use `#[proc_macro_hack(fake_call_site)]` on the
133//! re-export in your declaration crate. *Most macros won't need this.*
134//!
135//! - On compilers that are new enough to natively support proc macros in
136//! expression position, proc-macro-hack does not automatically use that
137//! support, since the hygiene can be subtly different between the two
138//! implementations. To opt in to compiling your macro to native
139//! `#[proc_macro]` on sufficiently new compilers, use
140//! `#[proc_macro_hack(only_hack_old_rustc)]` on the re-export in your
141//! declaration crate.
142//!
143//! [#10]: https://github.com/dtolnay/proc-macro-hack/issues/10
144//! [#20]: https://github.com/dtolnay/proc-macro-hack/issues/20
145//! [`proc-macro-nested`]: https://docs.rs/proc-macro-nested
146
147#![recursion_limit = "512"]
148#![allow(
149 clippy::doc_markdown,
150 clippy::manual_strip,
151 clippy::module_name_repetitions,
152 clippy::needless_doctest_main,
153 clippy::needless_pass_by_value,
154 clippy::too_many_lines,
155 clippy::toplevel_ref_arg
156)]
157
158extern crate proc_macro;
159
160#[macro_use]
161mod quote;
162
163mod error;
164mod iter;
165mod parse;
166
167use crate::error::{compile_error, Error};
168use crate::iter::Iter;
169use crate::parse::{
170 parse_define_args, parse_enum_hack, parse_export_args, parse_fake_call_site, parse_input,
171};
172use proc_macro::{Ident, Punct, Spacing, Span, TokenStream, TokenTree};
173use std::fmt::Write;
174
175type Visibility = Option<Ident>;
176
177enum Input {
178 Export(Export),
179 Define(Define),
180}
181
182// pub use demo_hack_impl::{m1, m2 as qrst};
183struct Export {
184 attrs: TokenStream,
185 vis: Visibility,
186 from: Ident,
187 macros: Vec<Macro>,
188}
189
190// pub fn m1(input: TokenStream) -> TokenStream { ... }
191struct Define {
192 attrs: TokenStream,
193 name: Ident,
194 body: TokenStream,
195}
196
197struct Macro {
198 name: Ident,
199 export_as: Ident,
200}
201
202#[proc_macro_attribute]
203pub fn proc_macro_hack(args: TokenStream, input: TokenStream) -> TokenStream {
204 let ref mut args: &mut IterImpl = iter::new(tokens:args);
205 let ref mut input: &mut IterImpl = iter::new(tokens:input);
206 expand_proc_macro_hack(args, input).unwrap_or_else(op:compile_error)
207}
208
209fn expand_proc_macro_hack(args: Iter, input: Iter) -> Result<TokenStream, Error> {
210 match parse_input(tokens:input)? {
211 Input::Export(export: Export) => {
212 let args: ExportArgs = parse_export_args(tokens:args)?;
213 Ok(expand_export(export, args))
214 }
215 Input::Define(define: Define) => {
216 parse_define_args(tokens:args)?;
217 Ok(expand_define(define))
218 }
219 }
220}
221
222#[doc(hidden)]
223#[proc_macro_derive(ProcMacroHack)]
224pub fn enum_hack(input: TokenStream) -> TokenStream {
225 let ref mut input: &mut IterImpl = iter::new(tokens:input);
226 parse_enum_hack(input).unwrap_or_else(op:compile_error)
227}
228
229struct FakeCallSite {
230 derive: Ident,
231 rest: TokenStream,
232}
233
234#[doc(hidden)]
235#[proc_macro_attribute]
236pub fn fake_call_site(args: TokenStream, input: TokenStream) -> TokenStream {
237 let ref mut args: &mut IterImpl = iter::new(tokens:args);
238 let ref mut input: &mut IterImpl = iter::new(tokens:input);
239 expand_fake_call_site(args, input).unwrap_or_else(op:compile_error)
240}
241
242fn expand_fake_call_site(args: Iter, input: Iter) -> Result<TokenStream, Error> {
243 let span: Span = match args.next() {
244 Some(token: TokenTree) => token.span(),
245 None => return Ok(input.collect()),
246 };
247
248 let input: FakeCallSite = parse_fake_call_site(tokens:input)?;
249 let mut derive: Ident = input.derive;
250 derive.set_span(span);
251 let rest: TokenStream = input.rest;
252
253 Ok(quote! {
254 #[derive(#derive)]
255 #rest
256 })
257}
258
259struct ExportArgs {
260 support_nested: bool,
261 internal_macro_calls: u16,
262 fake_call_site: bool,
263 only_hack_old_rustc: bool,
264}
265
266fn expand_export(export: Export, args: ExportArgs) -> TokenStream {
267 if args.only_hack_old_rustc && cfg!(not(need_proc_macro_hack)) {
268 return expand_export_nohack(export);
269 }
270
271 let dummy = dummy_name_for_export(&export);
272
273 let attrs = export.attrs;
274 let vis = export.vis;
275 let macro_export = match vis {
276 Some(_) => quote!(#[macro_export]),
277 None => quote!(),
278 };
279 let crate_prefix = vis.as_ref().map(|_| quote!($crate::));
280 let enum_variant = if args.support_nested {
281 if args.internal_macro_calls == 0 {
282 Ident::new("Nested", Span::call_site())
283 } else {
284 let name = format!("Nested{}", args.internal_macro_calls);
285 Ident::new(&name, Span::call_site())
286 }
287 } else {
288 Ident::new("Value", Span::call_site())
289 };
290
291 let from = export.from;
292 let mut actual_names = TokenStream::new();
293 let mut export_dispatch = TokenStream::new();
294 let mut export_call_site = TokenStream::new();
295 let mut macro_rules = TokenStream::new();
296 for Macro { name, export_as } in &export.macros {
297 let hacked = hacked_proc_macro_name(name);
298 let dispatch = dispatch_macro_name(name);
299 let call_site = call_site_macro_name(name);
300
301 if !actual_names.is_empty() {
302 actual_names.extend(quote!(,));
303 }
304 actual_names.extend(quote!(#hacked));
305
306 if !export_dispatch.is_empty() {
307 export_dispatch.extend(quote!(,));
308 }
309 export_dispatch.extend(quote!(dispatch as #dispatch));
310
311 if !export_call_site.is_empty() {
312 export_call_site.extend(quote!(,));
313 }
314 export_call_site.extend(quote!(fake_call_site as #call_site));
315
316 let do_derive = if !args.fake_call_site {
317 quote! {
318 #[derive(#crate_prefix #hacked)]
319 }
320 } else if crate_prefix.is_some() {
321 quote! {
322 use #crate_prefix #hacked;
323 #[#crate_prefix #call_site ($($proc_macro)*)]
324 #[derive(#hacked)]
325 }
326 } else {
327 quote! {
328 #[#call_site ($($proc_macro)*)]
329 #[derive(#hacked)]
330 }
331 };
332
333 let proc_macro_call = if args.support_nested {
334 let extra_bangs = (0..args.internal_macro_calls)
335 .map(|_| TokenTree::Punct(Punct::new('!', Spacing::Alone)))
336 .collect::<TokenStream>();
337 quote! {
338 #crate_prefix #dispatch! { ($($proc_macro)*) #extra_bangs }
339 }
340 } else {
341 quote! {
342 proc_macro_call!()
343 }
344 };
345
346 macro_rules.extend(quote! {
347 #attrs
348 #macro_export
349 macro_rules! #export_as {
350 ($($proc_macro:tt)*) => {{
351 #do_derive
352 #[allow(dead_code)]
353 enum ProcMacroHack {
354 #enum_variant = (stringify! { $($proc_macro)* }, 0).1,
355 }
356 #proc_macro_call
357 }};
358 }
359 });
360 }
361
362 if export.macros.len() != 1 {
363 export_dispatch = quote!({#export_dispatch});
364 export_call_site = quote!({#export_call_site});
365 actual_names = quote!({#actual_names});
366 }
367
368 let export_dispatch = if args.support_nested {
369 quote! {
370 #[doc(hidden)]
371 #vis use proc_macro_nested::#export_dispatch;
372 }
373 } else {
374 quote!()
375 };
376
377 let export_call_site = if args.fake_call_site {
378 quote! {
379 #[doc(hidden)]
380 #vis use proc_macro_hack::#export_call_site;
381 }
382 } else {
383 quote!()
384 };
385
386 let expanded = quote! {
387 #[doc(hidden)]
388 #vis use #from::#actual_names;
389
390 #export_dispatch
391 #export_call_site
392
393 #macro_rules
394 };
395
396 wrap_in_enum_hack(dummy, expanded)
397}
398
399fn expand_export_nohack(export: Export) -> TokenStream {
400 let attrs: TokenStream = export.attrs;
401 let vis: Option = export.vis;
402 let from: Ident = export.from;
403 let mut names: TokenStream = TokenStream::new();
404
405 for Macro { name: &Ident, export_as: &Ident } in &export.macros {
406 let pub_name: Ident = pub_proc_macro_name(conceptual:name);
407 if !names.is_empty() {
408 names.extend(iter:quote!(,));
409 }
410 names.extend(iter:quote!(#pub_name as #export_as));
411 }
412
413 if export.macros.len() != 1 {
414 names = quote!({#names});
415 }
416
417 quote! {
418 #attrs
419 #vis use #from::#names;
420 }
421}
422
423fn expand_define(define: Define) -> TokenStream {
424 let attrs = define.attrs;
425 let name = define.name;
426 let pub_name = pub_proc_macro_name(&name);
427 let hacked = hacked_proc_macro_name(&name);
428 let body = define.body;
429
430 quote! {
431 mod #pub_name {
432 extern crate proc_macro;
433 pub use self::proc_macro::*;
434 }
435
436 #attrs
437 #[doc(hidden)]
438 #[proc_macro_derive(#hacked)]
439 pub fn #hacked(input: #pub_name::TokenStream) -> #pub_name::TokenStream {
440 use std::iter::FromIterator;
441
442 let mut iter = input.into_iter();
443 iter.next().unwrap(); // `enum`
444 iter.next().unwrap(); // `ProcMacroHack`
445 iter.next().unwrap(); // `#`
446 iter.next().unwrap(); // `[allow(dead_code)]`
447
448 let mut braces = match iter.next().unwrap() {
449 #pub_name::TokenTree::Group(group) => group.stream().into_iter(),
450 _ => unimplemented!(),
451 };
452 let variant = braces.next().unwrap(); // `Value` or `Nested`
453 let varname = variant.to_string();
454 let support_nested = varname.starts_with("Nested");
455 braces.next().unwrap(); // `=`
456
457 let mut parens = match braces.next().unwrap() {
458 #pub_name::TokenTree::Group(group) => group.stream().into_iter(),
459 _ => unimplemented!(),
460 };
461 parens.next().unwrap(); // `stringify`
462 parens.next().unwrap(); // `!`
463
464 let inner = match parens.next().unwrap() {
465 #pub_name::TokenTree::Group(group) => group.stream(),
466 _ => unimplemented!(),
467 };
468
469 let output: #pub_name::TokenStream = #name(inner.clone());
470
471 fn count_bangs(input: #pub_name::TokenStream) -> usize {
472 let mut count = 0;
473 for token in input {
474 match token {
475 #pub_name::TokenTree::Punct(punct) => {
476 if punct.as_char() == '!' {
477 count += 1;
478 }
479 }
480 #pub_name::TokenTree::Group(group) => {
481 count += count_bangs(group.stream());
482 }
483 _ => {}
484 }
485 }
486 count
487 }
488
489 // macro_rules! proc_macro_call {
490 // () => { #output }
491 // }
492 #pub_name::TokenStream::from_iter(vec![
493 #pub_name::TokenTree::Ident(
494 #pub_name::Ident::new("macro_rules", #pub_name::Span::call_site()),
495 ),
496 #pub_name::TokenTree::Punct(
497 #pub_name::Punct::new('!', #pub_name::Spacing::Alone),
498 ),
499 #pub_name::TokenTree::Ident(
500 #pub_name::Ident::new(
501 &if support_nested {
502 let extra_bangs = if varname == "Nested" {
503 0
504 } else {
505 varname["Nested".len()..].parse().unwrap()
506 };
507 format!("proc_macro_call_{}", extra_bangs + count_bangs(inner))
508 } else {
509 String::from("proc_macro_call")
510 },
511 #pub_name::Span::call_site(),
512 ),
513 ),
514 #pub_name::TokenTree::Group(
515 #pub_name::Group::new(#pub_name::Delimiter::Brace, #pub_name::TokenStream::from_iter(vec![
516 #pub_name::TokenTree::Group(
517 #pub_name::Group::new(#pub_name::Delimiter::Parenthesis, #pub_name::TokenStream::new()),
518 ),
519 #pub_name::TokenTree::Punct(
520 #pub_name::Punct::new('=', #pub_name::Spacing::Joint),
521 ),
522 #pub_name::TokenTree::Punct(
523 #pub_name::Punct::new('>', #pub_name::Spacing::Alone),
524 ),
525 #pub_name::TokenTree::Group(
526 #pub_name::Group::new(#pub_name::Delimiter::Brace, output),
527 ),
528 ])),
529 ),
530 ])
531 }
532
533 #attrs
534 #[proc_macro]
535 pub fn #pub_name(input: #pub_name::TokenStream) -> #pub_name::TokenStream {
536 #name(input)
537 }
538
539 fn #name #body
540 }
541}
542
543fn pub_proc_macro_name(conceptual: &Ident) -> Ident {
544 Ident::new(
545 &format!("proc_macro_hack_{}", conceptual),
546 conceptual.span(),
547 )
548}
549
550fn hacked_proc_macro_name(conceptual: &Ident) -> Ident {
551 Ident::new(
552 &format!("_proc_macro_hack_{}", conceptual),
553 conceptual.span(),
554 )
555}
556
557fn dispatch_macro_name(conceptual: &Ident) -> Ident {
558 Ident::new(
559 &format!("proc_macro_call_{}", conceptual),
560 conceptual.span(),
561 )
562}
563
564fn call_site_macro_name(conceptual: &Ident) -> Ident {
565 Ident::new(
566 &format!("proc_macro_fake_call_site_{}", conceptual),
567 conceptual.span(),
568 )
569}
570
571fn dummy_name_for_export(export: &Export) -> String {
572 let mut dummy: String = String::new();
573 let from: String = unraw(&export.from).to_string();
574 write!(dummy, "_{}{}", from.len(), from).unwrap();
575 for m: &Macro in &export.macros {
576 let name: String = unraw(&m.name).to_string();
577 write!(dummy, "_{}{}", name.len(), name).unwrap();
578 }
579 dummy
580}
581
582fn unraw(ident: &Ident) -> Ident {
583 let string: String = ident.to_string();
584 if string.starts_with("r#") {
585 Ident::new(&string[2..], ident.span())
586 } else {
587 ident.clone()
588 }
589}
590
591fn wrap_in_enum_hack(dummy: String, inner: TokenStream) -> TokenStream {
592 let dummy: Ident = Ident::new(&dummy, Span::call_site());
593 quote! {
594 #[derive(proc_macro_hack::ProcMacroHack)]
595 enum #dummy {
596 Value = (stringify! { #inner }, 0).1,
597 }
598 }
599}
600