diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /third_party/rust/proc-macro-hack/src/parse.rs | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'third_party/rust/proc-macro-hack/src/parse.rs')
-rw-r--r-- | third_party/rust/proc-macro-hack/src/parse.rs | 254 |
1 files changed, 254 insertions, 0 deletions
diff --git a/third_party/rust/proc-macro-hack/src/parse.rs b/third_party/rust/proc-macro-hack/src/parse.rs new file mode 100644 index 0000000000..f9c3b80290 --- /dev/null +++ b/third_party/rust/proc-macro-hack/src/parse.rs @@ -0,0 +1,254 @@ +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<Input, Error> { + 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<Export, Error> { + 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<Define, Error> { + 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<Macro, Error> { + 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<Ident, Error> { + 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<u16, Span> { + 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<IterImpl, Error> { + 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) -> Visibility { + if let Some(TokenTree::Ident(ident)) = tokens.peek() { + if ident.to_string() == "pub" { + match tokens.next().unwrap() { + TokenTree::Ident(vis) => return Some(vis), + _ => unreachable!(), + } + } + } + None +} + +fn parse_attributes(tokens: Iter) -> Result<TokenStream, Error> { + 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<ExportArgs, Error> { + 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<TokenStream, Error> { + 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<FakeCallSite, Error> { + 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(), + }) +} |