1 // SPDX-License-Identifier: Apache-2.0 OR MIT
2 
3 use proc_macro::{TokenStream, TokenTree};
4 
5 pub(crate) trait ToTokens {
to_tokens(&self, tokens: &mut TokenStream)6     fn to_tokens(&self, tokens: &mut TokenStream);
7 }
8 
9 impl<T: ToTokens> ToTokens for Option<T> {
to_tokens(&self, tokens: &mut TokenStream)10     fn to_tokens(&self, tokens: &mut TokenStream) {
11         if let Some(v) = self {
12             v.to_tokens(tokens);
13         }
14     }
15 }
16 
17 impl ToTokens for proc_macro::Group {
to_tokens(&self, tokens: &mut TokenStream)18     fn to_tokens(&self, tokens: &mut TokenStream) {
19         tokens.extend([TokenTree::from(self.clone())]);
20     }
21 }
22 
23 impl ToTokens for TokenTree {
to_tokens(&self, tokens: &mut TokenStream)24     fn to_tokens(&self, tokens: &mut TokenStream) {
25         tokens.extend([self.clone()]);
26     }
27 }
28 
29 impl ToTokens for TokenStream {
to_tokens(&self, tokens: &mut 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.
41 macro_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
153 macro_rules! quote {
154     ($($tt:tt)*) => {
155         quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*)
156     }
157 }
158