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 proc_macro::Ident { 24 fn to_tokens(&self, tokens: &mut TokenStream) { 25 tokens.extend([TokenTree::from(self.clone())]); 26 } 27 } 28 29 impl ToTokens for TokenTree { 30 fn to_tokens(&self, tokens: &mut TokenStream) { 31 tokens.extend([self.clone()]); 32 } 33 } 34 35 impl 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. 47 macro_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 178 macro_rules! quote { 179 ($($tt:tt)*) => { 180 quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*) 181 } 182 } 183