proc_macro_hack/
lib.rs

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 = iter::new(args);
205    let ref mut input = iter::new(input);
206    expand_proc_macro_hack(args, input).unwrap_or_else(compile_error)
207}
208
209fn expand_proc_macro_hack(args: Iter, input: Iter) -> Result<TokenStream, Error> {
210    match parse_input(input)? {
211        Input::Export(export) => {
212            let args = parse_export_args(args)?;
213            Ok(expand_export(export, args))
214        }
215        Input::Define(define) => {
216            parse_define_args(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 = iter::new(input);
226    parse_enum_hack(input).unwrap_or_else(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 = iter::new(args);
238    let ref mut input = iter::new(input);
239    expand_fake_call_site(args, input).unwrap_or_else(compile_error)
240}
241
242fn expand_fake_call_site(args: Iter, input: Iter) -> Result<TokenStream, Error> {
243    let span = match args.next() {
244        Some(token) => token.span(),
245        None => return Ok(input.collect()),
246    };
247
248    let input = parse_fake_call_site(input)?;
249    let mut derive = input.derive;
250    derive.set_span(span);
251    let rest = 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 = export.attrs;
401    let vis = export.vis;
402    let from = export.from;
403    let mut names = TokenStream::new();
404
405    for Macro { name, export_as } in &export.macros {
406        let pub_name = pub_proc_macro_name(name);
407        if !names.is_empty() {
408            names.extend(quote!(,));
409        }
410        names.extend(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::new();
573    let from = unraw(&export.from).to_string();
574    write!(dummy, "_{}{}", from.len(), from).unwrap();
575    for m in &export.macros {
576        let name = 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 = 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::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}