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 let span = $span; 52 quote_spanned!(@proc tokens span $($tt)*); 53 } 54 tokens 55 }}; 56 (@proc $v:ident $span:ident) => {}; 57 (@proc $v:ident $span:ident #$id:ident $($tt:tt)*) => { 58 $crate::quote::ToTokens::to_tokens(&$id, &mut $v); 59 quote_spanned!(@proc $v $span $($tt)*); 60 }; 61 (@proc $v:ident $span:ident #(#$id:ident)* $($tt:tt)*) => { 62 for token in $id { 63 $crate::quote::ToTokens::to_tokens(&token, &mut $v); 64 } 65 quote_spanned!(@proc $v $span $($tt)*); 66 }; 67 (@proc $v:ident $span:ident ( $($inner:tt)* ) $($tt:tt)*) => { 68 #[allow(unused_mut)] 69 let mut tokens = ::proc_macro::TokenStream::new(); 70 quote_spanned!(@proc tokens $span $($inner)*); 71 $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new( 72 ::proc_macro::Delimiter::Parenthesis, 73 tokens, 74 ))]); 75 quote_spanned!(@proc $v $span $($tt)*); 76 }; 77 (@proc $v:ident $span:ident [ $($inner:tt)* ] $($tt:tt)*) => { 78 let mut tokens = ::proc_macro::TokenStream::new(); 79 quote_spanned!(@proc tokens $span $($inner)*); 80 $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new( 81 ::proc_macro::Delimiter::Bracket, 82 tokens, 83 ))]); 84 quote_spanned!(@proc $v $span $($tt)*); 85 }; 86 (@proc $v:ident $span:ident { $($inner:tt)* } $($tt:tt)*) => { 87 let mut tokens = ::proc_macro::TokenStream::new(); 88 quote_spanned!(@proc tokens $span $($inner)*); 89 $v.extend([::proc_macro::TokenTree::Group(::proc_macro::Group::new( 90 ::proc_macro::Delimiter::Brace, 91 tokens, 92 ))]); 93 quote_spanned!(@proc $v $span $($tt)*); 94 }; 95 (@proc $v:ident $span:ident :: $($tt:tt)*) => { 96 $v.extend([::proc_macro::Spacing::Joint, ::proc_macro::Spacing::Alone].map(|spacing| { 97 ::proc_macro::TokenTree::Punct(::proc_macro::Punct::new(':', spacing)) 98 })); 99 quote_spanned!(@proc $v $span $($tt)*); 100 }; 101 (@proc $v:ident $span:ident : $($tt:tt)*) => { 102 $v.extend([::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.extend([::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.extend([::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.extend([::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 ; $($tt:tt)*) => { 126 $v.extend([::proc_macro::TokenTree::Punct( 127 ::proc_macro::Punct::new(';', ::proc_macro::Spacing::Alone), 128 )]); 129 quote_spanned!(@proc $v $span $($tt)*); 130 }; 131 (@proc $v:ident $span:ident + $($tt:tt)*) => { 132 $v.extend([::proc_macro::TokenTree::Punct( 133 ::proc_macro::Punct::new('+', ::proc_macro::Spacing::Alone), 134 )]); 135 quote_spanned!(@proc $v $span $($tt)*); 136 }; 137 (@proc $v:ident $span:ident = $($tt:tt)*) => { 138 $v.extend([::proc_macro::TokenTree::Punct( 139 ::proc_macro::Punct::new('=', ::proc_macro::Spacing::Alone), 140 )]); 141 quote_spanned!(@proc $v $span $($tt)*); 142 }; 143 (@proc $v:ident $span:ident # $($tt:tt)*) => { 144 $v.extend([::proc_macro::TokenTree::Punct( 145 ::proc_macro::Punct::new('#', ::proc_macro::Spacing::Alone), 146 )]); 147 quote_spanned!(@proc $v $span $($tt)*); 148 }; 149 (@proc $v:ident $span:ident _ $($tt:tt)*) => { 150 $v.extend([::proc_macro::TokenTree::Ident( 151 ::proc_macro::Ident::new("_", $span), 152 )]); 153 quote_spanned!(@proc $v $span $($tt)*); 154 }; 155 (@proc $v:ident $span:ident $id:ident $($tt:tt)*) => { 156 $v.extend([::proc_macro::TokenTree::Ident( 157 ::proc_macro::Ident::new(stringify!($id), $span), 158 )]); 159 quote_spanned!(@proc $v $span $($tt)*); 160 }; 161 } 162 163 /// Converts tokens into [`proc_macro::TokenStream`] and performs variable interpolations with 164 /// mixed site span ([`Span::mixed_site()`]). 165 /// 166 /// This is a similar to the [`quote!`](https://docs.rs/quote/latest/quote/macro.quote.html) macro 167 /// from the `quote` crate but provides only just enough functionality needed by the current 168 /// `macros` crate. 169 /// 170 /// [`Span::mixed_site()`]: https://doc.rust-lang.org/proc_macro/struct.Span.html#method.mixed_site 171 macro_rules! quote { 172 ($($tt:tt)*) => { 173 quote_spanned!(::proc_macro::Span::mixed_site() => $($tt)*) 174 } 175 } 176