1use proc_macro::TokenStream as TokenStream1;
119use proc_macro2::{Delimiter, Group, Ident, Punct, Span, TokenStream, TokenTree};
120use quote::{format_ident, quote, quote_spanned};
121use std::{
122 collections::hash_map::RandomState,
123 convert::identity,
124 hash::{BuildHasher, Hasher},
125};
126
127#[proc_macro_attribute]
190pub fn macro_vis(attr: TokenStream1, item: TokenStream1) -> TokenStream1 {
191 macro_vis_inner(attr.into(), item.into())
192 .unwrap_or_else(identity)
193 .into()
194}
195
196fn macro_vis_inner(attr: TokenStream, item: TokenStream) -> Result<TokenStream, TokenStream> {
197 let vis = parse_vis(attr)?;
198 let Macro {
199 attrs,
200 macro_rules,
201 bang,
202 name,
203 arms,
204 rules,
205 semi,
206 } = parse_macro(item)?;
207
208 let real_name = format_ident!("__{}_{}", name, RandomState::new().build_hasher().finish());
209
210 Ok(match vis {
211 Vis::Local { pub_token, scope } => {
212 quote! {
213 #attrs
214 #macro_rules #bang #real_name #arms #semi
215 #pub_token #scope use #real_name as #name;
216 }
217 }
218 Vis::Public { pub_token } => {
219 let macro_token = Ident::new("macro", macro_rules.span());
220 let mut arms_2_0 = Group::new(Delimiter::Brace, macro_2_0_arms(&rules));
221 arms_2_0.set_span(arms.span());
222
223 let display_name = format_ident!("{}ǃ", name);
225
226 quote! {
227 #[cfg(not(doc_nightly))]
228 #[doc(hidden)]
229 #[macro_export]
230 #macro_rules #bang #real_name #arms #semi
231
232 #[cfg(not(doc_nightly))]
233 #[doc(hidden)]
234 #pub_token use #real_name as #name;
235
236 #[cfg(all(doc, not(doc_nightly)))]
237 #[doc = "<sup>**\\[macro\\]**</sup>"]
238 #attrs
239 #pub_token fn #display_name() {}
240
241 #[cfg(doc_nightly)]
242 #[rustc_macro_transparency = "semitransparent"]
245 #attrs
246 #pub_token #macro_token #name #arms_2_0
247 }
248 }
249 })
250}
251
252#[derive(Debug)]
253enum Vis {
254 Public {
255 pub_token: Ident,
256 },
257 Local {
258 pub_token: Option<Ident>,
259 scope: Option<Group>,
260 },
261}
262
263#[derive(Debug)]
264struct Macro {
265 attrs: TokenStream,
266 macro_rules: Ident,
267 bang: Punct,
268 name: Ident,
269 arms: Group,
270 rules: Vec<MacroRule>,
271 semi: Option<Punct>,
272}
273
274#[derive(Debug)]
275struct MacroRule {
276 matcher: Group,
277 equals: Punct,
278 greater_than: Punct,
279 transcriber: Group,
280 semi: Option<Punct>,
281}
282
283fn parse_vis(vis: TokenStream) -> Result<Vis, TokenStream> {
284 let mut vis = vis.into_iter();
285
286 let pub_token = match vis.next() {
287 Some(TokenTree::Ident(pub_token)) if pub_token == "pub" => pub_token,
288 Some(token) => {
289 return Err(error(token.span(), "expected visibility"));
290 }
291 None => {
292 return Ok(Vis::Local {
293 pub_token: None,
294 scope: None,
295 })
296 }
297 };
298
299 let scope = match vis.next() {
300 Some(TokenTree::Group(scope)) if scope.delimiter() == Delimiter::Parenthesis => scope,
301 Some(token) => {
302 return Err(error(token.span(), "expected parenthesis"));
303 }
304 None => return Ok(Vis::Public { pub_token }),
305 };
306
307 if let Some(trailing) = vis.next() {
308 return Err(error(trailing.span(), "trailing tokens"));
309 }
310
311 Ok(Vis::Local {
312 pub_token: Some(pub_token),
313 scope: Some(scope),
314 })
315}
316
317fn parse_macro(item: TokenStream) -> Result<Macro, TokenStream> {
318 let mut item = item.into_iter();
319
320 let mut attrs = TokenStream::new();
321
322 let macro_rules = loop {
323 match item.next() {
324 Some(TokenTree::Punct(punct)) if punct.as_char() == '#' => {
325 let next = item.next().expect("unexpected EOF in attribute");
326 if !matches!(&next, TokenTree::Group(group) if group.delimiter() == Delimiter::Bracket)
327 {
328 unreachable!("attribute without square brackets");
329 }
330 attrs.extend([TokenTree::Punct(punct), next]);
331 }
332 Some(TokenTree::Ident(macro_rules)) if macro_rules == "macro_rules" => {
333 break macro_rules;
334 }
335 token => {
336 return Err(error(opt_span(&token), "expected macro_rules! macro"));
337 }
338 }
339 };
340
341 let bang = match item.next() {
342 Some(TokenTree::Punct(p)) if p.as_char() == '!' => p,
343 token => {
344 return Err(error(opt_span(&token), "expected exclamation mark"));
345 }
346 };
347
348 let name = match item.next() {
349 Some(TokenTree::Ident(ident)) => ident,
350 token => {
351 return Err(error(opt_span(&token), "expected identifier"));
352 }
353 };
354
355 let arms = match item.next() {
356 Some(TokenTree::Group(group)) => group,
357 token => {
358 return Err(error(opt_span(&token), "expected macro arms"));
359 }
360 };
361
362 let mut rule_tokens = arms.stream().into_iter();
363 let mut rules = Vec::new();
364
365 loop {
369 let matcher = match rule_tokens.next() {
370 Some(TokenTree::Group(group)) => group,
371 Some(token) => {
372 return Err(error(token.span(), "expected macro matcher"));
373 }
374 None if rules.is_empty() => {
375 return Err(error(arms.span(), "expected macro rules"));
376 }
377 None => break,
378 };
379 let equals = match rule_tokens.next() {
380 Some(TokenTree::Punct(equals)) if equals.as_char() == '=' => equals,
381 token => return Err(error(opt_span(&token), "expected =>")),
382 };
383 let greater_than = match rule_tokens.next() {
384 Some(TokenTree::Punct(greater_than)) if greater_than.as_char() == '>' => greater_than,
385 _ => return Err(error(equals.span(), "expected =>")),
386 };
387 let transcriber = match rule_tokens.next() {
388 Some(TokenTree::Group(group)) => group,
389 token => return Err(error(opt_span(&token), "expected macro transcriber")),
390 };
391 let mut rule = MacroRule {
392 matcher,
393 equals,
394 greater_than,
395 transcriber,
396 semi: None,
397 };
398 match rule_tokens.next() {
399 Some(TokenTree::Punct(semi)) if semi.as_char() == ';' => {
400 rule.semi = Some(semi);
401 rules.push(rule);
402 }
403 None => {
404 rules.push(rule);
405 break;
406 }
407 Some(token) => {
408 return Err(error(token.span(), "expected semicolon"));
409 }
410 }
411 }
412
413 let semi = if arms.delimiter() != Delimiter::Brace {
414 Some(match item.next() {
415 Some(TokenTree::Punct(semi)) if semi.as_char() == ';' => semi,
416 _ => unreachable!("no semicolon after () or []-delimited macro"),
417 })
418 } else {
419 None
420 };
421
422 if item.next().is_some() {
423 unreachable!("trailing tokens after macro_rules! macro");
424 }
425
426 Ok(Macro {
427 attrs,
428 macro_rules,
429 bang,
430 name,
431 arms,
432 rules,
433 semi,
434 })
435}
436
437fn opt_span(token: &Option<TokenTree>) -> Span {
438 token
439 .as_ref()
440 .map(|token| token.span())
441 .unwrap_or_else(Span::call_site)
442}
443
444fn macro_2_0_arms(rules: &[MacroRule]) -> TokenStream {
445 rules
446 .iter()
447 .map(
448 |MacroRule {
449 matcher,
450 equals,
451 greater_than,
452 transcriber,
453 semi,
454 }| {
455 let comma = semi.as_ref().map(|semi| {
456 let mut comma = Punct::new(',', semi.spacing());
457 comma.set_span(semi.span());
458 comma
459 });
460 quote!(#matcher #equals #greater_than #transcriber #comma)
461 },
462 )
463 .collect()
464}
465
466fn error(span: Span, msg: &str) -> TokenStream {
467 quote_spanned!(span=> ::core::compile_error!(#msg))
468}
469
470#[cfg(test)]
471mod tests {
472 use crate::{parse_macro, parse_vis, Macro, Vis};
473 use proc_macro2::TokenStream;
474 use quote::quote;
475
476 #[test]
477 fn vis_parse() {
478 assert!(matches!(
479 parse_vis(TokenStream::new()),
480 Ok(Vis::Local {
481 pub_token: None,
482 scope: None
483 })
484 ));
485 assert!(matches!(
486 parse_vis(quote!(pub)),
487 Ok(Vis::Public { pub_token }) if pub_token == "pub"
488 ));
489 assert!(matches!(
490 parse_vis(quote!(pub(crate))),
491 Ok(Vis::Local { pub_token: Some(pub_token), scope: Some(scope) })
492 if pub_token == "pub" && scope.to_string() == quote!((crate)).to_string()
493 ));
494 assert!(matches!(
495 parse_vis(quote!(pub(foo bar))),
496 Ok(Vis::Local { pub_token: Some(pub_token), scope: Some(scope) })
497 if pub_token == "pub" && scope.to_string() == quote!((foo bar)).to_string()
498 ));
499 }
500
501 #[test]
502 fn vis_error() {
503 macro_rules! assert_err {
504 (($($input:tt)*) -> $e:literal) => {
505 assert_eq!(
506 parse_vis(quote!($($input)*)).unwrap_err().to_string(),
507 quote!(::core::compile_error!($e)).to_string(),
508 );
509 };
510 }
511 assert_err!((priv) -> "expected visibility");
512 assert_err!((pub[crate]) -> "expected parenthesis");
513 assert_err!((pub() trailing) -> "trailing tokens");
514 }
515
516 #[test]
517 fn macro_parse() {
518 assert!(matches!(
519 parse_macro(quote!(macro_rules! foo { (m) => { t } })),
520 Ok(Macro { attrs, macro_rules, bang, name, arms, rules, semi: None })
521 if attrs.is_empty()
522 && macro_rules == "macro_rules"
523 && bang.as_char() == '!'
524 && name == "foo"
525 && arms.to_string() == quote!({ (m) => { t } }).to_string()
526 && rules.len() == 1
527 && rules[0].matcher.to_string() == quote!((m)).to_string()
528 && rules[0].equals.as_char() == '='
529 && rules[0].greater_than.as_char() == '>'
530 && rules[0].transcriber.to_string() == quote!({ t }).to_string()
531 && rules[0].semi.is_none()
532 ));
533 assert!(matches!(
534 parse_macro(quote! {
535 #[attr1]
536 #[attr2 = "foo"]
537 macro_rules! foo [
538 {} => ();
539 [$] => [[]];
540 ];
541 }),
542 Ok(Macro { attrs, arms, rules, semi: Some(semi), .. })
543 if attrs.to_string() == quote!(#[attr1] #[attr2 = "foo"]).to_string()
544 && arms.to_string() == quote!([{} => (); [$] => [[]];]).to_string()
545 && semi.as_char() == ';'
546 && rules.len() == 2
547 && rules[0].matcher.to_string() == quote!({}).to_string()
548 && rules[0].transcriber.to_string() == quote!(()).to_string()
549 && rules[0].semi.as_ref().map_or(false, |semi| semi.as_char() == ';')
550 && rules[1].matcher.to_string() == quote!([$]).to_string()
551 && rules[1].transcriber.to_string() == quote!([[]]).to_string()
552 && rules[1].semi.as_ref().map_or(false, |semi| semi.as_char() == ';')
553 ));
554 }
555
556 #[test]
557 fn macro_error() {
558 macro_rules! assert_err {
559 (($($input:tt)*) -> $e:literal) => {
560 assert_eq!(
561 parse_macro(quote!($($input)*)).unwrap_err().to_string(),
562 quote!(::core::compile_error!($e)).to_string(),
563 );
564 }
565 }
566 assert_err!(() -> "expected macro_rules! macro");
567 assert_err!((const _: () = {};) -> "expected macro_rules! macro");
568 assert_err!((macro_rules x {}) -> "expected exclamation mark");
569 assert_err!((macro_rules! { () => {} }) -> "expected identifier");
570 assert_err!((macro_rules! foo) -> "expected macro arms");
571 assert_err!((macro_rules! foo { }) -> "expected macro rules");
572 assert_err!((macro_rules! foo { # }) -> "expected macro matcher");
573 assert_err!((macro_rules! foo { () }) -> "expected =>");
574 assert_err!((macro_rules! foo { () = }) -> "expected =>");
575 assert_err!((macro_rules! foo { () => }) -> "expected macro transcriber");
576 assert_err!((macro_rules! foo { () => {} () => {} }) -> "expected semicolon");
577 }
578}