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: ::std::vec::Vec<::proc_macro::TokenTree>; 50 #[allow(clippy::vec_init_then_push)] 51 { 52 tokens = ::std::vec::Vec::new(); 53 let span = $span; 54 quote_spanned!(@proc tokens span $($tt)*); 55 } 56 ::proc_macro::TokenStream::from_iter(tokens) 57 }}; 58 (@proc $v:ident $span:ident) => {}; 59 (@proc $v:ident $span:ident #$id:ident $($tt:tt)*) => { 60 let mut ts = ::proc_macro::TokenStream::new(); 61 $crate::quote::ToTokens::to_tokens(&$id, &mut ts); 62 $v.extend(ts); 63 quote_spanned!(@proc $v $span $($tt)*); 64 }; 65 (@proc $v:ident $span:ident #(#$id:ident)* $($tt:tt)*) => { 66 for token in $id { 67 let mut ts = ::proc_macro::TokenStream::new(); 68 $crate::quote::ToTokens::to_tokens(&token, &mut ts); 69 $v.extend(ts); 70 } 71 quote_spanned!(@proc $v $span $($tt)*); 72 }; 73 (@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => { 74 #[allow(unused_mut)] 75 let mut tokens = ::std::vec::Vec::<::proc_macro::TokenTree>::new(); 76 quote_spanned!(@proc tokens $span $($inner)*); 77 $v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new( 78 ::proc_macro::Delimiter::Parenthesis, 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::Bracket, 88 ::proc_macro::TokenStream::from_iter(tokens) 89 ))); 90 quote_spanned!(@proc $v $span $($tt)*); 91 }; 92 (@proc $v:ident $span:ident { $($inner:tt)* } $($tt:tt)*) => { 93 let mut tokens = ::std::vec::Vec::new(); 94 quote_spanned!(@proc tokens $span $($inner)*); 95 $v.push(::proc_macro::TokenTree::Group(::proc_macro::Group::new( 96 ::proc_macro::Delimiter::Brace, 97 ::proc_macro::TokenStream::from_iter(tokens) 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::Joint) 104 )); 105 $v.push(::proc_macro::TokenTree::Punct( 106 ::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone) 107 )); 108 quote_spanned!(@proc $v $span $($tt)*); 109 }; 110 (@proc $v:ident $span:ident : $($tt:tt)*) => { 111 $v.push(::proc_macro::TokenTree::Punct( 112 ::proc_macro::Punct::new(':', ::proc_macro::Spacing::Alone) 113 )); 114 quote_spanned!(@proc $v $span $($tt)*); 115 }; 116 (@proc $v:ident $span:ident , $($tt:tt)*) => { 117 $v.push(::proc_macro::TokenTree::Punct( 118 ::proc_macro::Punct::new(',', ::proc_macro::Spacing::Alone) 119 )); 120 quote_spanned!(@proc $v $span $($tt)*); 121 }; 122 (@proc $v:ident $span:ident @ $($tt:tt)*) => { 123 $v.push(::proc_macro::TokenTree::Punct( 124 ::proc_macro::Punct::new('@', ::proc_macro::Spacing::Alone) 125 )); 126 quote_spanned!(@proc $v $span $($tt)*); 127 }; 128 (@proc $v:ident $span:ident ! $($tt:tt)*) => { 129 $v.push(::proc_macro::TokenTree::Punct( 130 ::proc_macro::Punct::new('!', ::proc_macro::Spacing::Alone) 131 )); 132 quote_spanned!(@proc $v $span $($tt)*); 133 }; 134 (@proc $v:ident $span:ident ; $($tt:tt)*) => { 135 $v.push(::proc_macro::TokenTree::Punct( 136 ::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone) 137 )); 138 quote_spanned!(@proc $v $span $($tt)*); 139 }; 140 (@proc $v:ident $span:ident + $($tt:tt)*) => { 141 $v.push(::proc_macro::TokenTree::Punct( 142 ::proc_macro::Punct::new('+', ::proc_macro::Spacing::Alone) 143 )); 144 quote_spanned!(@proc $v $span $($tt)*); 145 }; 146 (@proc $v:ident $span:ident = $($tt:tt)*) => { 147 $v.push(::proc_macro::TokenTree::Punct( 148 ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone) 149 )); 150 quote_spanned!(@proc $v $span $($tt)*); 151 }; 152 (@proc $v:ident $span:ident # $($tt:tt)*) => { 153 $v.push(::proc_macro::TokenTree::Punct( 154 ::proc_macro::Punct::new('#', ::proc_macro::Spacing::Alone) 155 )); 156 quote_spanned!(@proc $v $span $($tt)*); 157 }; 158 (@proc $v:ident $span:ident _ $($tt:tt)*) => { 159 $v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new("_", $span))); 160 quote_spanned!(@proc $v $span $($tt)*); 161 }; 162 (@proc $v:ident $span:ident $id:ident $($tt:tt)*) => { 163 $v.push(::proc_macro::TokenTree::Ident(::proc_macro::Ident::new(stringify!($id), $span))); 164 quote_spanned!(@proc $v $span $($tt)*); 165 }; 166 } 167 168 /// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with 169 /// mixed site span ([`Span::mixed_site()`]). 170 /// 171 /// This is a similar to the [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html) macro 172 /// from the `quote` crate but provides only just enough functionality needed by the current 173 /// `macros` crate. 174 /// 175 /// [`Span::mixed_site()`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.mixed_site 176 macro_rules! quote { 177 ($($tt:tt)*) => { 178 quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*) 179 } 180 } 181