proc_macro_hack/
parse.rs

1use crate::iter::{self, Iter, IterImpl};
2use crate::{Define, Error, Export, ExportArgs, FakeCallSite, Input, Macro, Visibility};
3use proc_macro::Delimiter::{Brace, Bracket, Parenthesis};
4use proc_macro::{Delimiter, Ident, Span, TokenStream, TokenTree};
5
6pub(crate) fn parse_input(tokens: Iter) -> Result<Input, Error> {
7    let attrs = parse_attributes(tokens)?;
8    let vis = parse_visibility(tokens);
9    let kw = parse_ident(tokens)?;
10    if kw.to_string() == "use" {
11        parse_export(attrs, vis, tokens).map(Input::Export)
12    } else if kw.to_string() == "fn" {
13        parse_define(attrs, vis, kw.span(), tokens).map(Input::Define)
14    } else {
15        Err(Error::new(
16            kw.span(),
17            "unexpected input to #[proc_macro_hack]",
18        ))
19    }
20}
21
22fn parse_export(attrs: TokenStream, vis: Visibility, tokens: Iter) -> Result<Export, Error> {
23    let _ = parse_punct(tokens, ':');
24    let _ = parse_punct(tokens, ':');
25    let from = parse_ident(tokens)?;
26    parse_punct(tokens, ':')?;
27    parse_punct(tokens, ':')?;
28
29    let mut macros = Vec::new();
30    match tokens.peek() {
31        Some(TokenTree::Group(group)) if group.delimiter() == Brace => {
32            let ref mut content = iter::new(group.stream());
33            loop {
34                macros.push(parse_macro(content)?);
35                if content.peek().is_none() {
36                    break;
37                }
38                parse_punct(content, ',')?;
39                if content.peek().is_none() {
40                    break;
41                }
42            }
43            tokens.next().unwrap();
44        }
45        _ => macros.push(parse_macro(tokens)?),
46    }
47
48    parse_punct(tokens, ';')?;
49    Ok(Export {
50        attrs,
51        vis,
52        from,
53        macros,
54    })
55}
56
57fn parse_punct(tokens: Iter, ch: char) -> Result<(), Error> {
58    match tokens.peek() {
59        Some(TokenTree::Punct(punct)) if punct.as_char() == ch => {
60            tokens.next().unwrap();
61            Ok(())
62        }
63        tt => Err(Error::new(
64            tt.map_or_else(Span::call_site, TokenTree::span),
65            format!("expected `{}`", ch),
66        )),
67    }
68}
69
70fn parse_define(
71    attrs: TokenStream,
72    vis: Visibility,
73    fn_token: Span,
74    tokens: Iter,
75) -> Result<Define, Error> {
76    if vis.is_none() {
77        return Err(Error::new(
78            fn_token,
79            "functions tagged with `#[proc_macro_hack]` must be `pub`",
80        ));
81    }
82    let name = parse_ident(tokens)?;
83    let body = tokens.collect();
84    Ok(Define { attrs, name, body })
85}
86
87fn parse_macro(tokens: Iter) -> Result<Macro, Error> {
88    let name = parse_ident(tokens)?;
89    let export_as = match tokens.peek() {
90        Some(TokenTree::Ident(ident)) if ident.to_string() == "as" => {
91            tokens.next().unwrap();
92            parse_ident(tokens)?
93        }
94        _ => name.clone(),
95    };
96    Ok(Macro { name, export_as })
97}
98
99fn parse_ident(tokens: Iter) -> Result<Ident, Error> {
100    match tokens.next() {
101        Some(TokenTree::Ident(ident)) => Ok(ident),
102        tt => Err(Error::new(
103            tt.as_ref().map_or_else(Span::call_site, TokenTree::span),
104            "expected identifier",
105        )),
106    }
107}
108
109fn parse_keyword(tokens: Iter, kw: &'static str) -> Result<(), Error> {
110    match &tokens.next() {
111        Some(TokenTree::Ident(ident)) if ident.to_string() == kw => Ok(()),
112        tt => Err(Error::new(
113            tt.as_ref().map_or_else(Span::call_site, TokenTree::span),
114            format!("expected `{}`", kw),
115        )),
116    }
117}
118
119fn parse_int(tokens: Iter) -> Result<u16, Span> {
120    match tokens.next() {
121        Some(TokenTree::Literal(lit)) => lit.to_string().parse().map_err(|_| lit.span()),
122        Some(tt) => Err(tt.span()),
123        None => Err(Span::call_site()),
124    }
125}
126
127fn parse_group(tokens: Iter, delimiter: Delimiter) -> Result<IterImpl, Error> {
128    match &tokens.next() {
129        Some(TokenTree::Group(group)) if group.delimiter() == delimiter => {
130            Ok(iter::new(group.stream()))
131        }
132        tt => Err(Error::new(
133            tt.as_ref().map_or_else(Span::call_site, TokenTree::span),
134            "expected delimiter",
135        )),
136    }
137}
138
139fn parse_visibility(tokens: Iter) -> Visibility {
140    if let Some(TokenTree::Ident(ident)) = tokens.peek() {
141        if ident.to_string() == "pub" {
142            match tokens.next().unwrap() {
143                TokenTree::Ident(vis) => return Some(vis),
144                _ => unreachable!(),
145            }
146        }
147    }
148    None
149}
150
151fn parse_attributes(tokens: Iter) -> Result<TokenStream, Error> {
152    let mut attrs = TokenStream::new();
153    while let Some(TokenTree::Punct(punct)) = tokens.peek() {
154        if punct.as_char() != '#' {
155            break;
156        }
157        let span = punct.span();
158        attrs.extend(tokens.next());
159        match tokens.peek() {
160            Some(TokenTree::Group(group)) if group.delimiter() == Bracket => {
161                attrs.extend(tokens.next());
162            }
163            _ => return Err(Error::new(span, "unexpected input")),
164        }
165    }
166    Ok(attrs)
167}
168
169pub(crate) fn parse_export_args(tokens: Iter) -> Result<ExportArgs, Error> {
170    let mut args = ExportArgs {
171        support_nested: false,
172        internal_macro_calls: 0,
173        fake_call_site: false,
174        only_hack_old_rustc: false,
175    };
176
177    while let Some(tt) = tokens.next() {
178        match &tt {
179            TokenTree::Ident(ident) if ident.to_string() == "support_nested" => {
180                args.support_nested = true;
181            }
182            TokenTree::Ident(ident) if ident.to_string() == "internal_macro_calls" => {
183                parse_punct(tokens, '=')?;
184                let calls = parse_int(tokens).map_err(|span| {
185                    Error::new(span, "expected integer value for internal_macro_calls")
186                })?;
187                args.internal_macro_calls = calls;
188            }
189            TokenTree::Ident(ident) if ident.to_string() == "fake_call_site" => {
190                args.fake_call_site = true;
191            }
192            TokenTree::Ident(ident) if ident.to_string() == "only_hack_old_rustc" => {
193                args.only_hack_old_rustc = true;
194            }
195            _ => {
196                return Err(Error::new(
197                    tt.span(),
198                    "expected one of: `support_nested`, `internal_macro_calls`, `fake_call_site`, `only_hack_old_rustc`",
199                ));
200            }
201        }
202        if tokens.peek().is_none() {
203            break;
204        }
205        parse_punct(tokens, ',')?;
206    }
207
208    Ok(args)
209}
210
211pub(crate) fn parse_define_args(tokens: Iter) -> Result<(), Error> {
212    match tokens.peek() {
213        None => Ok(()),
214        Some(token) => Err(Error::new(
215            token.span(),
216            "unexpected argument to proc_macro_hack macro implementation; args are only accepted on the macro declaration (the `pub use`)",
217        )),
218    }
219}
220
221pub(crate) fn parse_enum_hack(tokens: Iter) -> Result<TokenStream, Error> {
222    parse_keyword(tokens, "enum")?;
223    parse_ident(tokens)?;
224
225    let ref mut braces = parse_group(tokens, Brace)?;
226    parse_ident(braces)?;
227    parse_punct(braces, '=')?;
228
229    let ref mut parens = parse_group(braces, Parenthesis)?;
230    parse_ident(parens)?;
231    parse_punct(parens, '!')?;
232
233    let ref mut inner = parse_group(parens, Brace)?;
234    let token_stream = inner.collect();
235
236    parse_punct(parens, ',')?;
237    let _ = parens.next();
238    parse_punct(braces, '.')?;
239    let _ = braces.next();
240    parse_punct(braces, ',')?;
241
242    Ok(token_stream)
243}
244
245pub(crate) fn parse_fake_call_site(tokens: Iter) -> Result<FakeCallSite, Error> {
246    parse_punct(tokens, '#')?;
247    let ref mut attr = parse_group(tokens, Bracket)?;
248    parse_keyword(attr, "derive")?;
249    let ref mut path = parse_group(attr, Parenthesis)?;
250    Ok(FakeCallSite {
251        derive: parse_ident(path)?,
252        rest: tokens.collect(),
253    })
254}