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