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)
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&logoColor=white&logo=data:image/svg+xml;base64,PHN2ZyByb2xlPSJpbWciIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgdmlld0JveD0iMCAwIDUxMiA1MTIiPjxwYXRoIGZpbGw9IiNmNWY1ZjUiIGQ9Ik00ODguNiAyNTAuMkwzOTIgMjE0VjEwNS41YzAtMTUtOS4zLTI4LjQtMjMuNC0zMy43bC0xMDAtMzcuNWMtOC4xLTMuMS0xNy4xLTMuMS0yNS4zIDBsLTEwMCAzNy41Yy0xNC4xIDUuMy0yMy40IDE4LjctMjMuNCAzMy43VjIxNGwtOTYuNiAzNi4yQzkuMyAyNTUuNSAwIDI2OC45IDAgMjgzLjlWMzk0YzAgMTMuNiA3LjcgMjYuMSAxOS45IDMyLjJsMTAwIDUwYzEwLjEgNS4xIDIyLjEgNS4xIDMyLjIgMGwxMDMuOS01MiAxMDMuOSA1MmMxMC4xIDUuMSAyMi4xIDUuMSAzMi4yIDBsMTAwLTUwYzEyLjItNi4xIDE5LjktMTguNiAxOS45LTMyLjJWMjgzLjljMC0xNS05LjMtMjguNC0yMy40LTMzLjd6TTM1OCAyMTQuOGwtODUgMzEuOXYtNjguMmw4NS0zN3Y3My4zek0xNTQgMTA0LjFsMTAyLTM4LjIgMTAyIDM4LjJ2LjZsLTEwMiA0MS40LTEwMi00MS40di0uNnptODQgMjkxLjFsLTg1IDQyLjV2LTc5LjFsODUtMzguOHY3NS40em0wLTExMmwtMTAyIDQxLjQtMTAyLTQxLjR2LS42bDEwMi0zOC4yIDEwMiAzOC4ydi42em0yNDAgMTEybC04NSA0Mi41di03OS4xbDg1LTM4Ljh2NzUuNHptMC0xMTJsLTEwMiA0MS40LTEwMi00MS40di0uNmwxMDItMzguMiAxMDIgMzguMnYuNnoiPjwvcGF0aD48L3N2Zz4K
9 //! As of Rust 1.30, the language supports user-defined function-like procedural
10 //! macros. However these can only be invoked in item position, not in
11 //! statements or expressions.
13 //! This crate implements an alternative type of procedural macro that can be
14 //! invoked in statement or expression position.
16 //! # Defining procedural macros
18 //! Two crates are required to define a procedural macro.
20 //! ## The implementation crate
22 //! This crate must contain nothing but procedural macros. Private helper
23 //! functions and private modules are fine but nothing can be public.
25 //! [» example of an implementation crate][demo-hack-impl]
27 //! Just like you would use a #\[proc_macro\] attribute to define a natively
28 //! supported procedural macro, use proc-macro-hack's #\[proc_macro_hack\]
29 //! attribute to define a procedural macro that works in expression position.
30 //! The function signature is the same as for ordinary function-like procedural
34 //! extern crate proc_macro;
36 //! use proc_macro::TokenStream;
37 //! use proc_macro_hack::proc_macro_hack;
39 //! use syn::{parse_macro_input, Expr};
41 //! # const IGNORE: &str = stringify! {
42 //! #[proc_macro_hack]
44 //! pub fn add_one(input: TokenStream) -> TokenStream {
45 //! let expr = parse_macro_input!(input as Expr);
46 //! TokenStream::from(quote! {
54 //! ## The declaration crate
56 //! This crate is allowed to contain other public things if you need, for
57 //! example traits or functions or ordinary macros.
59 //! [» example of a declaration crate][demo-hack]
61 //! Within the declaration crate there needs to be a re-export of your
62 //! procedural macro from the implementation crate. The re-export also carries a
63 //! \#\[proc_macro_hack\] attribute.
66 //! use proc_macro_hack::proc_macro_hack;
68 //! /// Add one to an expression.
70 //! /// (Documentation goes here on the re-export, not in the other crate.)
71 //! #[proc_macro_hack]
72 //! pub use demo_hack_impl::add_one;
77 //! Both crates depend on `proc-macro-hack`:
81 //! proc-macro-hack = "0.5"
84 //! Additionally, your implementation crate (but not your declaration crate) is
85 //! a proc macro crate:
92 //! # Using procedural macros
94 //! Users of your crate depend on your declaration crate (not your
95 //! implementation crate), then use your procedural macros as usual.
97 //! [» example of a downstream crate][example]
100 //! use demo_hack::add_one;
104 //! let nine = add_one!(two) + add_one!(2 + 3);
105 //! println!("nine = {}", nine);
109 //! [demo-hack-impl]: https://github.com/dtolnay/proc-macro-hack/tree/master/demo-hack-impl
110 //! [demo-hack]: https://github.com/dtolnay/proc-macro-hack/tree/master/demo-hack
111 //! [example]: https://github.com/dtolnay/proc-macro-hack/tree/master/example
115 //! - Only proc macros in expression position are supported. Proc macros in
116 //! pattern position ([#20]) are not supported.
118 //! - By default, nested invocations are not supported i.e. the code emitted by
119 //! a proc-macro-hack macro invocation cannot contain recursive calls to the
120 //! same proc-macro-hack macro nor calls to any other proc-macro-hack macros.
121 //! Use [`proc-macro-nested`] if you require support for nested invocations.
123 //! - By default, hygiene is structured such that the expanded code can't refer
124 //! to local variables other than those passed by name somewhere in the macro
125 //! input. If your macro must refer to *local* variables that don't get named
126 //! in the macro input, use `#[proc_macro_hack(fake_call_site)]` on the
127 //! re-export in your declaration crate. *Most macros won't need this.*
129 //! [#10]: https://github.com/dtolnay/proc-macro-hack/issues/10
130 //! [#20]: https://github.com/dtolnay/proc-macro-hack/issues/20
131 //! [`proc-macro-nested`]: https://docs.rs/proc-macro-nested
133 #![recursion_limit = "512"]
134 #![allow(clippy::needless_doctest_main, clippy::toplevel_ref_arg)]
136 extern crate proc_macro
;
145 use crate::error
::{compile_error, Error}
;
146 use crate::iter
::Iter
;
148 parse_define_args
, parse_enum_hack
, parse_export_args
, parse_fake_call_site
, parse_input
,
150 use proc_macro
::{Ident, Punct, Spacing, Span, TokenStream, TokenTree}
;
153 type Visibility
= Option
<Span
>;
160 // pub use demo_hack_impl::{m1, m2 as qrst};
168 // pub fn m1(input: TokenStream) -> TokenStream { ... }
180 #[proc_macro_attribute]
181 pub fn proc_macro_hack(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
182 let ref mut args
= iter
::new(args
);
183 let ref mut input
= iter
::new(input
);
184 expand_proc_macro_hack(args
, input
).unwrap_or_else(compile_error
)
187 fn expand_proc_macro_hack(args
: Iter
, input
: Iter
) -> Result
<TokenStream
, Error
> {
188 match parse_input(input
)?
{
189 Input
::Export(export
) => {
190 let args
= parse_export_args(args
)?
;
191 Ok(expand_export(export
, args
))
193 Input
::Define(define
) => {
194 parse_define_args(args
)?
;
195 Ok(expand_define(define
))
201 #[proc_macro_derive(ProcMacroHack)]
202 pub fn enum_hack(input
: TokenStream
) -> TokenStream
{
203 let ref mut input
= iter
::new(input
);
204 parse_enum_hack(input
).unwrap_or_else(compile_error
)
207 struct FakeCallSite
{
213 #[proc_macro_attribute]
214 pub fn fake_call_site(args
: TokenStream
, input
: TokenStream
) -> TokenStream
{
215 let ref mut args
= iter
::new(args
);
216 let ref mut input
= iter
::new(input
);
217 expand_fake_call_site(args
, input
).unwrap_or_else(compile_error
)
220 fn expand_fake_call_site(args
: Iter
, input
: Iter
) -> Result
<TokenStream
, Error
> {
221 let span
= match args
.next() {
222 Some(token
) => token
.span(),
223 None
=> return Ok(input
.collect()),
226 let input
= parse_fake_call_site(input
)?
;
227 let mut derive
= input
.derive
;
228 derive
.set_span(span
);
229 let rest
= input
.rest
;
238 support_nested
: bool
,
239 internal_macro_calls
: u16,
240 fake_call_site
: bool
,
243 fn expand_export(export
: Export
, args
: ExportArgs
) -> TokenStream
{
244 let dummy
= dummy_name_for_export(&export
);
246 let attrs
= export
.attrs
;
247 let ref vis
= export
.vis
.map(|span
| Ident
::new("pub", span
));
248 let macro_export
= match vis
{
249 Some(_
) => quote
!(#[macro_export]),
252 let crate_prefix
= vis
.as_ref().map(|_
| quote
!($
crate::));
253 let enum_variant
= if args
.support_nested
{
254 if args
.internal_macro_calls
== 0 {
255 Ident
::new("Nested", Span
::call_site())
257 let name
= format
!("Nested{}", args
.internal_macro_calls
);
258 Ident
::new(&name
, Span
::call_site())
261 Ident
::new("Value", Span
::call_site())
264 let from
= export
.from
;
268 .map(|Macro { name, export_as }
| {
269 let actual_name
= actual_proc_macro_name(&name
);
270 let dispatch
= dispatch_macro_name(&name
);
271 let call_site
= call_site_macro_name(&name
);
273 let export_dispatch
= if args
.support_nested
{
276 #vis use proc_macro_nested::dispatch as #dispatch;
282 let proc_macro_call
= if args
.support_nested
{
283 let extra_bangs
= (0..args
.internal_macro_calls
)
284 .map(|_
| TokenTree
::Punct(Punct
::new('
!'
, Spacing
::Alone
)))
285 .collect
::<TokenStream
>();
287 #crate_prefix #dispatch! { ($($proc_macro)*) #extra_bangs }
295 let export_call_site
= if args
.fake_call_site
{
298 #vis use proc_macro_hack::fake_call_site as #call_site;
304 let do_derive
= if !args
.fake_call_site
{
306 #[derive(#crate_prefix #actual_name)]
308 } else if crate_prefix
.is_some() {
310 use #crate_prefix #actual_name;
311 #[#crate_prefix #call_site ($($proc_macro)*)]
312 #[derive(#actual_name)]
316 #[#call_site ($($proc_macro)*)]
317 #[derive(#actual_name)]
323 #vis use #from::#actual_name;
330 macro_rules
! #export_as {
331 ($
($proc_macro
:tt
)*) => {{
335 #enum_variant = (stringify! { $($proc_macro)* }, 0).1,
344 wrap_in_enum_hack(dummy
, rules
)
347 fn expand_define(define
: Define
) -> TokenStream
{
348 let attrs
= define
.attrs
;
349 let name
= define
.name
;
350 let dummy
= actual_proc_macro_name(&name
);
351 let body
= define
.body
;
355 extern crate proc_macro
;
356 pub use self::proc_macro
::*;
360 #[proc_macro_derive(#dummy)]
361 pub fn #dummy(input: #dummy::TokenStream) -> #dummy::TokenStream {
362 use std
::iter
::FromIterator
;
364 let mut iter
= input
.into_iter();
365 iter
.next().unwrap(); // `enum`
366 iter
.next().unwrap(); // `ProcMacroHack`
367 iter
.next().unwrap(); // `#`
368 iter
.next().unwrap(); // `[allow(dead_code)]`
370 let mut braces
= match iter
.next().unwrap() {
371 #dummy::TokenTree::Group(group) => group.stream().into_iter(),
372 _
=> unimplemented
!(),
374 let variant
= braces
.next().unwrap(); // `Value` or `Nested`
375 let varname
= variant
.to_string();
376 let support_nested
= varname
.starts_with("Nested");
377 braces
.next().unwrap(); // `=`
379 let mut parens
= match braces
.next().unwrap() {
380 #dummy::TokenTree::Group(group) => group.stream().into_iter(),
381 _
=> unimplemented
!(),
383 parens
.next().unwrap(); // `stringify`
384 parens
.next().unwrap(); // `!`
386 let inner
= match parens
.next().unwrap() {
387 #dummy::TokenTree::Group(group) => group.stream(),
388 _
=> unimplemented
!(),
391 let output
: #dummy::TokenStream = #name(inner.clone());
393 fn count_bangs(input
: #dummy::TokenStream) -> usize {
397 #dummy::TokenTree::Punct(punct) => {
398 if punct
.as_char() == '
!'
{
402 #dummy::TokenTree::Group(group) => {
403 count
+= count_bangs(group
.stream());
411 // macro_rules! proc_macro_call {
414 #dummy::TokenStream::from_iter(vec![
415 #dummy::TokenTree::Ident(
416 #dummy::Ident::new("macro_rules", #dummy::Span::call_site()),
418 #dummy::TokenTree::Punct(
419 #dummy::Punct::new('!', #dummy::Spacing::Alone),
421 #dummy::TokenTree::Ident(
424 let extra_bangs
= if varname
== "Nested" {
427 varname
["Nested".len()..].parse().unwrap()
429 format
!("proc_macro_call_{}", extra_bangs
+ count_bangs(inner
))
431 String
::from("proc_macro_call")
433 #dummy::Span::call_site(),
436 #dummy::TokenTree::Group(
437 #dummy::Group::new(#dummy::Delimiter::Brace, #dummy::TokenStream::from_iter(vec![
438 #dummy::TokenTree::Group(
439 #dummy::Group::new(#dummy::Delimiter::Parenthesis, #dummy::TokenStream::new()),
441 #dummy::TokenTree::Punct(
442 #dummy::Punct::new('=', #dummy::Spacing::Joint),
444 #dummy::TokenTree::Punct(
445 #dummy::Punct::new('>', #dummy::Spacing::Alone),
447 #dummy::TokenTree::Group(
448 #dummy::Group::new(#dummy::Delimiter::Brace, output),
459 fn actual_proc_macro_name(conceptual
: &Ident
) -> Ident
{
461 &format
!("proc_macro_hack_{}", conceptual
),
466 fn dispatch_macro_name(conceptual
: &Ident
) -> Ident
{
468 &format
!("proc_macro_call_{}", conceptual
),
473 fn call_site_macro_name(conceptual
: &Ident
) -> Ident
{
475 &format
!("proc_macro_fake_call_site_{}", conceptual
),
480 fn dummy_name_for_export(export
: &Export
) -> String
{
481 let mut dummy
= String
::new();
482 let from
= unraw(&export
.from
).to_string();
483 write
!(dummy
, "_{}{}", from
.len(), from
).unwrap();
484 for m
in &export
.macros
{
485 let name
= unraw(&m
.name
).to_string();
486 write
!(dummy
, "_{}{}", name
.len(), name
).unwrap();
491 fn unraw(ident
: &Ident
) -> Ident
{
492 let string
= ident
.to_string();
493 if string
.starts_with("r#") {
494 Ident
::new(&string
[2..], ident
.span())
500 fn wrap_in_enum_hack(dummy
: String
, inner
: TokenStream
) -> TokenStream
{
501 let dummy
= Ident
::new(&dummy
, Span
::call_site());
503 #[derive(proc_macro_hack::ProcMacroHack)]
505 Value
= (stringify
! { #inner }
, 0).1,