]> git.lizzy.rs Git - rust.git/blob - crates/proc-macro-srv/src/abis/abi_1_64/proc_macro/quote.rs
Support the 1.64 nightly proc macro ABI
[rust.git] / crates / proc-macro-srv / src / abis / abi_1_64 / proc_macro / quote.rs
1 //! # Quasiquoter
2 //! This file contains the implementation internals of the quasiquoter provided by `quote!`.
3
4 //! This quasiquoter uses macros 2.0 hygiene to reliably access
5 //! items from `proc_macro`, to build a `proc_macro::TokenStream`.
6
7 use super::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
8
9 macro_rules! quote_tt {
10     (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) };
11     ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) };
12     ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
13     (,) => { Punct::new(',', Spacing::Alone) };
14     (.) => { Punct::new('.', Spacing::Alone) };
15     (;) => { Punct::new(';', Spacing::Alone) };
16     (!) => { Punct::new('!', Spacing::Alone) };
17     (<) => { Punct::new('<', Spacing::Alone) };
18     (>) => { Punct::new('>', Spacing::Alone) };
19     (&) => { Punct::new('&', Spacing::Alone) };
20     (=) => { Punct::new('=', Spacing::Alone) };
21     ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
22 }
23
24 macro_rules! quote_ts {
25     ((@ $($t:tt)*)) => { $($t)* };
26     (::) => {
27         [
28             TokenTree::from(Punct::new(':', Spacing::Joint)),
29             TokenTree::from(Punct::new(':', Spacing::Alone)),
30         ].iter()
31             .cloned()
32             .map(|mut x| {
33                 x.set_span(Span::def_site());
34                 x
35             })
36             .collect::<TokenStream>()
37     };
38     ($t:tt) => { TokenTree::from(quote_tt!($t)) };
39 }
40
41 /// Simpler version of the real `quote!` macro, implemented solely
42 /// through `macro_rules`, for bootstrapping the real implementation
43 /// (see the `quote` function), which does not have access to the
44 /// real `quote!` macro due to the `proc_macro` crate not being
45 /// able to depend on itself.
46 ///
47 /// Note: supported tokens are a subset of the real `quote!`, but
48 /// unquoting is different: instead of `$x`, this uses `(@ expr)`.
49 macro_rules! quote {
50     () => { TokenStream::new() };
51     ($($t:tt)*) => {
52         [
53             $(TokenStream::from(quote_ts!($t)),)*
54         ].iter().cloned().collect::<TokenStream>()
55     };
56 }
57
58 /// Quote a `TokenStream` into a `TokenStream`.
59 /// This is the actual implementation of the `quote!()` proc macro.
60 ///
61 /// It is loaded by the compiler in `register_builtin_macros`.
62 pub fn quote(stream: TokenStream) -> TokenStream {
63     if stream.is_empty() {
64         return quote!(super::TokenStream::new());
65     }
66     let proc_macro_crate = quote!(crate);
67     let mut after_dollar = false;
68     let tokens = stream
69         .into_iter()
70         .filter_map(|tree| {
71             if after_dollar {
72                 after_dollar = false;
73                 match tree {
74                     TokenTree::Ident(_) => {
75                         return Some(quote!(Into::<super::TokenStream>::into(
76                         Clone::clone(&(@ tree))),));
77                     }
78                     TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
79                     _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
80                 }
81             } else if let TokenTree::Punct(ref tt) = tree {
82                 if tt.as_char() == '$' {
83                     after_dollar = true;
84                     return None;
85                 }
86             }
87
88             Some(quote!(super::TokenStream::from((@ match tree {
89                 TokenTree::Punct(tt) => quote!(super::TokenTree::Punct(super::Punct::new(
90                     (@ TokenTree::from(Literal::character(tt.as_char()))),
91                     (@ match tt.spacing() {
92                         Spacing::Alone => quote!(super::Spacing::Alone),
93                         Spacing::Joint => quote!(super::Spacing::Joint),
94                     }),
95                 ))),
96                 TokenTree::Group(tt) => quote!(super::TokenTree::Group(super::Group::new(
97                     (@ match tt.delimiter() {
98                         Delimiter::Parenthesis => quote!(super::Delimiter::Parenthesis),
99                         Delimiter::Brace => quote!(super::Delimiter::Brace),
100                         Delimiter::Bracket => quote!(super::Delimiter::Bracket),
101                         Delimiter::None => quote!(super::Delimiter::None),
102                     }),
103                     (@ quote(tt.stream())),
104                 ))),
105                 TokenTree::Ident(tt) => quote!(super::TokenTree::Ident(super::Ident::new(
106                     (@ TokenTree::from(Literal::string(&tt.to_string()))),
107                     (@ quote_span(proc_macro_crate.clone(), tt.span())),
108                 ))),
109                 TokenTree::Literal(tt) => quote!(super::TokenTree::Literal({
110                     let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
111                         .parse::<super::TokenStream>()
112                         .unwrap()
113                         .into_iter();
114                     if let (Some(super::TokenTree::Literal(mut lit)), None) =
115                         (iter.next(), iter.next())
116                     {
117                         lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span())));
118                         lit
119                     } else {
120                         unreachable!()
121                     }
122                 }))
123             })),))
124         })
125         .collect::<TokenStream>();
126
127     if after_dollar {
128         panic!("unexpected trailing `$` in `quote!`");
129     }
130
131     quote!([(@ tokens)].iter().cloned().collect::<super::TokenStream>())
132 }
133
134 /// Quote a `Span` into a `TokenStream`.
135 /// This is needed to implement a custom quoter.
136 pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream {
137     let id = span.save_span();
138     quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
139 }