1#![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
182struct Export {
184 attrs: TokenStream,
185 vis: Visibility,
186 from: Ident,
187 macros: Vec<Macro>,
188}
189
190struct 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(); iter.next().unwrap(); iter.next().unwrap(); iter.next().unwrap(); 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(); let varname = variant.to_string();
454 let support_nested = varname.starts_with("Nested");
455 braces.next().unwrap(); let mut parens = match braces.next().unwrap() {
458 #pub_name::TokenTree::Group(group) => group.stream().into_iter(),
459 _ => unimplemented!(),
460 };
461 parens.next().unwrap(); parens.next().unwrap(); 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 #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}