macros/
quote.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2
3use proc_macro::{TokenStream, TokenTree};
4
5pub(crate) trait ToTokens {
6    fn to_tokens(&self, tokens: &mut TokenStream);
7}
8
9impl<T: ToTokens> ToTokens for Option<T> {
10    fn to_tokens(&self, tokens: &mut TokenStream) {
11        if let Some(v) = self {
12            v.to_tokens(tokens);
13        }
14    }
15}
16
17impl ToTokens for proc_macro::Group {
18    fn to_tokens(&self, tokens: &mut TokenStream) {
19        tokens.extend([TokenTree::from(self.clone())]);
20    }
21}
22
23impl ToTokens for proc_macro::Ident {
24    fn to_tokens(&self, tokens: &mut TokenStream) {
25        tokens.extend([TokenTree::from(self.clone())]);
26    }
27}
28
29impl ToTokens for TokenTree {
30    fn to_tokens(&self, tokens: &mut TokenStream) {
31        tokens.extend([self.clone()]);
32    }
33}
34
35impl ToTokens for TokenStream {
36    fn to_tokens(&self, tokens: &mut TokenStream) {
37        tokens.extend(self.clone());
38    }
39}
40
41/// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with
42/// the given span.
43///
44/// This is a similar to the
45/// [`quote_spanned!`](https://docs.rs/quote/latest/quote/macro.quote_spanned.html) macro from the
46/// `quote` crate but provides only just enough functionality needed by the current `macros` crate.
47macro_rules! quote_spanned {
48    ($span:expr => $($tt:tt)*) => {{
49        let mut tokens = ::proc_macro::TokenStream::new();
50        {
51            #[allow(unused_variables)]
52            let span = $span;
53            quote_spanned!(@proc tokens span $($tt)*);
54        }
55        tokens
56    }};
57    (@proc $v:ident $span:ident) => {};
58    (@proc $v:ident $span:ident #$id:ident $($tt:tt)*) => {
59        $crate::quote::ToTokens::to_tokens(&$id, &mut $v);
60        quote_spanned!(@proc $v $span $($tt)*);
61    };
62    (@proc $v:ident $span:ident #(#$id:ident)* $($tt:tt)*) => {
63        for token in $id {
64            $crate::quote::ToTokens::to_tokens(&token, &mut $v);
65        }
66        quote_spanned!(@proc $v $span $($tt)*);
67    };
68    (@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => {
69        #[allow(unused_mut)]
70        let mut tokens = ::proc_macro::TokenStream::new();
71        quote_spanned!(@proc tokens $span $($inner)*);
72        $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
73            ::proc_macro::Delimiter::Parenthesis,
74            tokens,
75        ))]);
76        quote_spanned!(@proc $v $span $($tt)*);
77    };
78    (@proc $v:ident $span:ident [ $($inner:tt)* ] $($tt:tt)*) => {
79        let mut tokens = ::proc_macro::TokenStream::new();
80        quote_spanned!(@proc tokens $span $($inner)*);
81        $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
82            ::proc_macro::Delimiter::Bracket,
83            tokens,
84        ))]);
85        quote_spanned!(@proc $v $span $($tt)*);
86    };
87    (@proc $v:ident $span:ident { $($inner:tt)* } $($tt:tt)*) => {
88        let mut tokens = ::proc_macro::TokenStream::new();
89        quote_spanned!(@proc tokens $span $($inner)*);
90        $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new(
91            ::proc_macro::Delimiter::Brace,
92            tokens,
93        ))]);
94        quote_spanned!(@proc $v $span $($tt)*);
95    };
96    (@proc $v:ident $span:ident :: $($tt:tt)*) => {
97        $v.extend([::proc_macro::Spacing::Joint, ::proc_macro::Spacing::Alone].map(|spacing| {
98            ::proc_macro::TokenTree::Punct(::proc_macro::Punct::new(':', spacing))
99        }));
100        quote_spanned!(@proc $v $span $($tt)*);
101    };
102    (@proc $v:ident $span:ident : $($tt:tt)*) => {
103        $v.extend([::proc_macro::TokenTree::Punct(
104            ::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone),
105        )]);
106        quote_spanned!(@proc $v $span $($tt)*);
107    };
108    (@proc $v:ident $span:ident , $($tt:tt)*) => {
109        $v.extend([::proc_macro::TokenTree::Punct(
110            ::proc_macro::Punct::new(',', ::proc_macro::Spacing::Alone),
111        )]);
112        quote_spanned!(@proc $v $span $($tt)*);
113    };
114    (@proc $v:ident $span:ident @ $($tt:tt)*) => {
115        $v.extend([::proc_macro::TokenTree::Punct(
116            ::proc_macro::Punct::new('@', ::proc_macro::Spacing::Alone),
117        )]);
118        quote_spanned!(@proc $v $span $($tt)*);
119    };
120    (@proc $v:ident $span:ident ! $($tt:tt)*) => {
121        $v.extend([::proc_macro::TokenTree::Punct(
122            ::proc_macro::Punct::new('!', ::proc_macro::Spacing::Alone),
123        )]);
124        quote_spanned!(@proc $v $span $($tt)*);
125    };
126    (@proc $v:ident $span:ident ; $($tt:tt)*) => {
127        $v.extend([::proc_macro::TokenTree::Punct(
128            ::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone),
129        )]);
130        quote_spanned!(@proc $v $span $($tt)*);
131    };
132    (@proc $v:ident $span:ident + $($tt:tt)*) => {
133        $v.extend([::proc_macro::TokenTree::Punct(
134            ::proc_macro::Punct::new('+', ::proc_macro::Spacing::Alone),
135        )]);
136        quote_spanned!(@proc $v $span $($tt)*);
137    };
138    (@proc $v:ident $span:ident = $($tt:tt)*) => {
139        $v.extend([::proc_macro::TokenTree::Punct(
140            ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone),
141        )]);
142        quote_spanned!(@proc $v $span $($tt)*);
143    };
144    (@proc $v:ident $span:ident # $($tt:tt)*) => {
145        $v.extend([::proc_macro::TokenTree::Punct(
146            ::proc_macro::Punct::new('#', ::proc_macro::Spacing::Alone),
147        )]);
148        quote_spanned!(@proc $v $span $($tt)*);
149    };
150    (@proc $v:ident $span:ident & $($tt:tt)*) => {
151        $v.extend([::proc_macro::TokenTree::Punct(
152            ::proc_macro::Punct::new('&', ::proc_macro::Spacing::Alone),
153        )]);
154        quote_spanned!(@proc $v $span $($tt)*);
155    };
156    (@proc $v:ident $span:ident _ $($tt:tt)*) => {
157        $v.extend([::proc_macro::TokenTree::Ident(
158            ::proc_macro::Ident::new("_", $span),
159        )]);
160        quote_spanned!(@proc $v $span $($tt)*);
161    };
162    (@proc $v:ident $span:ident $id:ident $($tt:tt)*) => {
163        $v.extend([::proc_macro::TokenTree::Ident(
164            ::proc_macro::Ident::new(stringify!($id), $span),
165        )]);
166        quote_spanned!(@proc $v $span $($tt)*);
167    };
168}
169
170/// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with
171/// mixed site span ([`Span::mixed_site()`]).
172///
173/// This is a similar to the [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html) macro
174/// from the `quote` crate but provides only just enough functionality needed by the current
175/// `macros` crate.
176///
177/// [`Span::mixed_site()`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.mixed_site
178macro_rules! quote {
179    ($($tt:tt)*) => {
180        quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*)
181    }
182}