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