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 TokenTree {
24    fn to_tokens(&self, tokens: &mut TokenStream) {
25        tokens.extend([self.clone()]);
26    }
27}
28
29impl ToTokens for TokenStream {
30    fn to_tokens(&self, tokens: &mut TokenStream) {
31        tokens.extend(self.clone());
32    }
33}
34
35/// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with
36/// the given span.
37///
38/// This is a similar to the
39/// [`quote_spanned!`](https://docs.rs/quote/latest/quote/macro.quote_spanned.html) macro from the
40/// `quote` crate but provides only just enough functionality needed by the current `macros` crate.
41macro_rules! quote_spanned {
42    ($span:expr => $($tt:tt)*) => {{
43        let mut tokens;
44        #[allow(clippy::vec_init_then_push)]
45        {
46            tokens = ::std::vec::Vec::new();
47            let span = $span;
48            quote_spanned!(@proc tokens span $($tt)*);
49        }
50        ::proc_macro::TokenStream::from_iter(tokens)
51    }};
52    (@proc $v:ident $span:ident) => {};
53    (@proc $v:ident $span:ident #$id:ident $($tt:tt)*) => {
54        let mut ts = ::proc_macro::TokenStream::new();
55        $crate::quote::ToTokens::to_tokens(&$id, &mut ts);
56        $v.extend(ts);
57        quote_spanned!(@proc $v $span $($tt)*);
58    };
59    (@proc $v:ident $span:ident #(#$id:ident)* $($tt:tt)*) => {
60        for token in $id {
61            let mut ts = ::proc_macro::TokenStream::new();
62            $crate::quote::ToTokens::to_tokens(&token, &mut ts);
63            $v.extend(ts);
64        }
65        quote_spanned!(@proc $v $span $($tt)*);
66    };
67    (@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => {
68        let mut tokens = ::std::vec::Vec::new();
69        quote_spanned!(@proc tokens $span $($inner)*);
70        $v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new(
71            ::proc_macro::Delimiter::Parenthesis,
72            ::proc_macro::TokenStream::from_iter(tokens)
73        )));
74        quote_spanned!(@proc $v $span $($tt)*);
75    };
76    (@proc $v:ident $span:ident [ $($inner:tt)* ] $($tt:tt)*) => {
77        let mut tokens = ::std::vec::Vec::new();
78        quote_spanned!(@proc tokens $span $($inner)*);
79        $v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new(
80            ::proc_macro::Delimiter::Bracket,
81            ::proc_macro::TokenStream::from_iter(tokens)
82        )));
83        quote_spanned!(@proc $v $span $($tt)*);
84    };
85    (@proc $v:ident $span:ident { $($inner:tt)* } $($tt:tt)*) => {
86        let mut tokens = ::std::vec::Vec::new();
87        quote_spanned!(@proc tokens $span $($inner)*);
88        $v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new(
89            ::proc_macro::Delimiter::Brace,
90            ::proc_macro::TokenStream::from_iter(tokens)
91        )));
92        quote_spanned!(@proc $v $span $($tt)*);
93    };
94    (@proc $v:ident $span:ident :: $($tt:tt)*) => {
95        $v.push(::proc_macro::TokenTree::Punct(
96                ::proc_macro::Punct::new(':', ::proc_macro::Spacing::Joint)
97        ));
98        $v.push(::proc_macro::TokenTree::Punct(
99                ::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone)
100        ));
101        quote_spanned!(@proc $v $span $($tt)*);
102    };
103    (@proc $v:ident $span:ident : $($tt:tt)*) => {
104        $v.push(::proc_macro::TokenTree::Punct(
105                ::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone)
106        ));
107        quote_spanned!(@proc $v $span $($tt)*);
108    };
109    (@proc $v:ident $span:ident , $($tt:tt)*) => {
110        $v.push(::proc_macro::TokenTree::Punct(
111                ::proc_macro::Punct::new(',', ::proc_macro::Spacing::Alone)
112        ));
113        quote_spanned!(@proc $v $span $($tt)*);
114    };
115    (@proc $v:ident $span:ident @ $($tt:tt)*) => {
116        $v.push(::proc_macro::TokenTree::Punct(
117                ::proc_macro::Punct::new('@', ::proc_macro::Spacing::Alone)
118        ));
119        quote_spanned!(@proc $v $span $($tt)*);
120    };
121    (@proc $v:ident $span:ident ! $($tt:tt)*) => {
122        $v.push(::proc_macro::TokenTree::Punct(
123                ::proc_macro::Punct::new('!', ::proc_macro::Spacing::Alone)
124        ));
125        quote_spanned!(@proc $v $span $($tt)*);
126    };
127    (@proc $v:ident $span:ident ; $($tt:tt)*) => {
128        $v.push(::proc_macro::TokenTree::Punct(
129                ::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone)
130        ));
131        quote_spanned!(@proc $v $span $($tt)*);
132    };
133    (@proc $v:ident $span:ident + $($tt:tt)*) => {
134        $v.push(::proc_macro::TokenTree::Punct(
135                ::proc_macro::Punct::new('+', ::proc_macro::Spacing::Alone)
136        ));
137        quote_spanned!(@proc $v $span $($tt)*);
138    };
139    (@proc $v:ident $span:ident $id:ident $($tt:tt)*) => {
140        $v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new(stringify!($id), $span)));
141        quote_spanned!(@proc $v $span $($tt)*);
142    };
143}
144
145/// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with
146/// mixed site span ([`Span::mixed_site()`]).
147///
148/// This is a similar to the [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html) macro
149/// from the `quote` crate but provides only just enough functionality needed by the current
150/// `macros` crate.
151///
152/// [`Span::mixed_site()`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.mixed_site
153macro_rules! quote {
154    ($($tt:tt)*) => {
155        quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*)
156    }
157}