summaryrefslogtreecommitdiffstats
path: root/vendor/proc-macro-hack/src/parse.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/proc-macro-hack/src/parse.rs')
-rw-r--r--vendor/proc-macro-hack/src/parse.rs254
1 files changed, 254 insertions, 0 deletions
diff --git a/vendor/proc-macro-hack/src/parse.rs b/vendor/proc-macro-hack/src/parse.rs
new file mode 100644
index 000000000..fe074f166
--- /dev/null
+++ b/vendor/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) -> Result<Visibility, Error> {
+ 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<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(),
+ })
+}