use crate::iter::{self, Iter, IterImpl}; use crate::{Define, Error, Export, ExportArgs, FakeCallSite, Input, Macro, Visibility}; use proc_macro::Delimiter::{Brace, Bracket, Parenthesis}; use proc_macro::{Delimiter, Ident, Span, TokenStream, TokenTree}; pub(crate) fn parse_input(tokens: Iter) -> Result { let attrs = parse_attributes(tokens)?; let vis = parse_visibility(tokens)?; let kw = parse_ident(tokens)?; if kw.to_string() == "use" { parse_export(attrs, vis, tokens).map(Input::Export) } else if kw.to_string() == "fn" { parse_define(attrs, vis, kw.span(), tokens).map(Input::Define) } else { Err(Error::new( kw.span(), "unexpected input to #[proc_macro_hack]", )) } } fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result { let _ = parse_punct(tokens, ':'); let _ = parse_punct(tokens, ':'); let from = parse_ident(tokens)?; parse_punct(tokens, ':')?; parse_punct(tokens, ':')?; let mut macros = Vec::new(); match tokens.peek() { Some(TokenTree::Group(group)) if group.delimiter() == Brace => { let ref mut content = iter::new(group.stream()); loop { macros.push(parse_macro(content)?); if content.peek().is_none() { break; } parse_punct(content, ',')?; if content.peek().is_none() { break; } } tokens.next().unwrap(); } _ => macros.push(parse_macro(tokens)?), } parse_punct(tokens, ';')?; Ok(Export { attrs, vis, from, macros, }) } fn parse_punct(tokens: Iter, ch: char) -> Result<(), Error> { match tokens.peek() { Some(TokenTree::Punct(punct)) if punct.as_char() == ch => { tokens.next().unwrap(); Ok(()) } tt => Err(Error::new( tt.map_or_else(Span::call_site, TokenTree::span), format!("expected `{}`", ch), )), } } fn parse_define( attrs: TokenStream, vis: Visibility, fn_token: Span, tokens: Iter, ) -> Result { if vis.is_none() { return Err(Error::new( fn_token, "functions tagged with `#[proc_macro_hack]` must be `pub`", )); } let name = parse_ident(tokens)?; let body = tokens.collect(); Ok(Define { attrs, name, body }) } fn parse_macro(tokens: Iter) -> Result { let name = parse_ident(tokens)?; let export_as = match tokens.peek() { Some(TokenTree::Ident(ident)) if ident.to_string() == "as" => { tokens.next().unwrap(); parse_ident(tokens)? } _ => name.clone(), }; Ok(Macro { name, export_as }) } fn parse_ident(tokens: Iter) -> Result { match tokens.next() { Some(TokenTree::Ident(ident)) => Ok(ident), tt => Err(Error::new( tt.as_ref().map_or_else(Span::call_site, TokenTree::span), "expected identifier", )), } } fn parse_keyword(tokens: Iter, kw: &'static str) -> Result<(), Error> { match &tokens.next() { Some(TokenTree::Ident(ident)) if ident.to_string() == kw => Ok(()), tt => Err(Error::new( tt.as_ref().map_or_else(Span::call_site, TokenTree::span), format!("expected `{}`", kw), )), } } fn parse_int(tokens: Iter) -> Result { match tokens.next() { Some(TokenTree::Literal(lit)) => lit.to_string().parse().map_err(|_| lit.span()), Some(tt) => Err(tt.span()), None => Err(Span::call_site()), } } fn parse_group(tokens: Iter, delimiter: Delimiter) -> Result { match &tokens.next() { Some(TokenTree::Group(group)) if group.delimiter() == delimiter => { Ok(iter::new(group.stream())) } tt => Err(Error::new( tt.as_ref().map_or_else(Span::call_site, TokenTree::span), "expected delimiter", )), } } fn parse_visibility(tokens: Iter) -> Result { if let Some(TokenTree::Ident(ident)) = tokens.peek() { if ident.to_string() == "pub" { match tokens.next().unwrap() { TokenTree::Ident(vis) => return Ok(Some(vis)), _ => unreachable!(), } } } Ok(None) } fn parse_attributes(tokens: Iter) -> Result { let mut attrs = TokenStream::new(); while let Some(TokenTree::Punct(punct)) = tokens.peek() { if punct.as_char() != '#' { break; } let span = punct.span(); attrs.extend(tokens.next()); match tokens.peek() { Some(TokenTree::Group(group)) if group.delimiter() == Bracket => { attrs.extend(tokens.next()); } _ => return Err(Error::new(span, "unexpected input")), } } Ok(attrs) } pub(crate) fn parse_export_args(tokens: Iter) -> Result { let mut args = ExportArgs { support_nested: false, internal_macro_calls: 0, fake_call_site: false, only_hack_old_rustc: false, }; while let Some(tt) = tokens.next() { match &tt { TokenTree::Ident(ident) if ident.to_string() == "support_nested" => { args.support_nested = true; } TokenTree::Ident(ident) if ident.to_string() == "internal_macro_calls" => { parse_punct(tokens, '=')?; let calls = parse_int(tokens).map_err(|span| { Error::new(span, "expected integer value for internal_macro_calls") })?; args.internal_macro_calls = calls; } TokenTree::Ident(ident) if ident.to_string() == "fake_call_site" => { args.fake_call_site = true; } TokenTree::Ident(ident) if ident.to_string() == "only_hack_old_rustc" => { args.only_hack_old_rustc = true; } _ => { return Err(Error::new( tt.span(), "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`, `only_hack_old_rustc`", )); } } if tokens.peek().is_none() { break; } parse_punct(tokens, ',')?; } Ok(args) } pub(crate) fn parse_define_args(tokens: Iter) -> Result<(), Error> { match tokens.peek() { None => Ok(()), Some(token) => Err(Error::new( token.span(), "unexpected argument to proc_macro_hack macro implementation; args are only accepted on the macro declaration (the `pub use`)", )), } } pub(crate) fn parse_enum_hack(tokens: Iter) -> Result { parse_keyword(tokens, "enum")?; parse_ident(tokens)?; let ref mut braces = parse_group(tokens, Brace)?; parse_ident(braces)?; parse_punct(braces, '=')?; let ref mut parens = parse_group(braces, Parenthesis)?; parse_ident(parens)?; parse_punct(parens, '!')?; let ref mut inner = parse_group(parens, Brace)?; let token_stream = inner.collect(); parse_punct(parens, ',')?; let _ = parens.next(); parse_punct(braces, '.')?; let _ = braces.next(); parse_punct(braces, ',')?; Ok(token_stream) } pub(crate) fn parse_fake_call_site(tokens: Iter) -> Result { parse_punct(tokens, '#')?; let ref mut attr = parse_group(tokens, Bracket)?; parse_keyword(attr, "derive")?; let ref mut path = parse_group(attr, Parenthesis)?; Ok(FakeCallSite { derive: parse_ident(path)?, rest: tokens.collect(), }) }