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 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