| 1 | //! # Quasiquoter |
| 2 | //! This file contains the implementation internals of the quasiquoter provided by `quote!`. |
| 3 | |
| 4 | //! This quasiquoter uses macros 2.0 hygiene to reliably access |
| 5 | //! items from `proc_macro`, to build a `proc_macro::TokenStream`. |
| 6 | |
| 7 | use crate::{ |
| 8 | BitOr, Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree, |
| 9 | }; |
| 10 | |
| 11 | #[doc (hidden)] |
| 12 | pub struct HasIterator; // True |
| 13 | #[doc (hidden)] |
| 14 | pub struct ThereIsNoIteratorInRepetition; // False |
| 15 | |
| 16 | impl BitOr<ThereIsNoIteratorInRepetition> for ThereIsNoIteratorInRepetition { |
| 17 | type Output = ThereIsNoIteratorInRepetition; |
| 18 | fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> ThereIsNoIteratorInRepetition { |
| 19 | ThereIsNoIteratorInRepetition |
| 20 | } |
| 21 | } |
| 22 | |
| 23 | impl BitOr<ThereIsNoIteratorInRepetition> for HasIterator { |
| 24 | type Output = HasIterator; |
| 25 | fn bitor(self, _rhs: ThereIsNoIteratorInRepetition) -> HasIterator { |
| 26 | HasIterator |
| 27 | } |
| 28 | } |
| 29 | |
| 30 | impl BitOr<HasIterator> for ThereIsNoIteratorInRepetition { |
| 31 | type Output = HasIterator; |
| 32 | fn bitor(self, _rhs: HasIterator) -> HasIterator { |
| 33 | HasIterator |
| 34 | } |
| 35 | } |
| 36 | |
| 37 | impl BitOr<HasIterator> for HasIterator { |
| 38 | type Output = HasIterator; |
| 39 | fn bitor(self, _rhs: HasIterator) -> HasIterator { |
| 40 | HasIterator |
| 41 | } |
| 42 | } |
| 43 | |
| 44 | /// Extension traits used by the implementation of `quote!`. These are defined |
| 45 | /// in separate traits, rather than as a single trait due to ambiguity issues. |
| 46 | /// |
| 47 | /// These traits expose a `quote_into_iter` method which should allow calling |
| 48 | /// whichever impl happens to be applicable. Calling that method repeatedly on |
| 49 | /// the returned value should be idempotent. |
| 50 | #[doc (hidden)] |
| 51 | pub mod ext { |
| 52 | use core::slice; |
| 53 | use std::collections::btree_set::{self, BTreeSet}; |
| 54 | |
| 55 | use super::{ |
| 56 | HasIterator as HasIter, RepInterp, ThereIsNoIteratorInRepetition as DoesNotHaveIter, |
| 57 | }; |
| 58 | use crate::ToTokens; |
| 59 | |
| 60 | /// Extension trait providing the `quote_into_iter` method on iterators. |
| 61 | #[doc (hidden)] |
| 62 | pub trait RepIteratorExt: Iterator + Sized { |
| 63 | fn quote_into_iter(self) -> (Self, HasIter) { |
| 64 | (self, HasIter) |
| 65 | } |
| 66 | } |
| 67 | |
| 68 | impl<T: Iterator> RepIteratorExt for T {} |
| 69 | |
| 70 | /// Extension trait providing the `quote_into_iter` method for |
| 71 | /// non-iterable types. These types interpolate the same value in each |
| 72 | /// iteration of the repetition. |
| 73 | #[doc (hidden)] |
| 74 | pub trait RepToTokensExt { |
| 75 | /// Pretend to be an iterator for the purposes of `quote_into_iter`. |
| 76 | /// This allows repeated calls to `quote_into_iter` to continue |
| 77 | /// correctly returning DoesNotHaveIter. |
| 78 | fn next(&self) -> Option<&Self> { |
| 79 | Some(self) |
| 80 | } |
| 81 | |
| 82 | fn quote_into_iter(&self) -> (&Self, DoesNotHaveIter) { |
| 83 | (self, DoesNotHaveIter) |
| 84 | } |
| 85 | } |
| 86 | |
| 87 | impl<T: ToTokens + ?Sized> RepToTokensExt for T {} |
| 88 | |
| 89 | /// Extension trait providing the `quote_into_iter` method for types that |
| 90 | /// can be referenced as an iterator. |
| 91 | #[doc (hidden)] |
| 92 | pub trait RepAsIteratorExt<'q> { |
| 93 | type Iter: Iterator; |
| 94 | |
| 95 | fn quote_into_iter(&'q self) -> (Self::Iter, HasIter); |
| 96 | } |
| 97 | |
| 98 | impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &T { |
| 99 | type Iter = T::Iter; |
| 100 | |
| 101 | fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { |
| 102 | <T as RepAsIteratorExt>::quote_into_iter(*self) |
| 103 | } |
| 104 | } |
| 105 | |
| 106 | impl<'q, T: RepAsIteratorExt<'q> + ?Sized> RepAsIteratorExt<'q> for &mut T { |
| 107 | type Iter = T::Iter; |
| 108 | |
| 109 | fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { |
| 110 | <T as RepAsIteratorExt>::quote_into_iter(*self) |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | impl<'q, T: 'q> RepAsIteratorExt<'q> for [T] { |
| 115 | type Iter = slice::Iter<'q, T>; |
| 116 | |
| 117 | fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { |
| 118 | (self.iter(), HasIter) |
| 119 | } |
| 120 | } |
| 121 | |
| 122 | impl<'q, T: 'q, const N: usize> RepAsIteratorExt<'q> for [T; N] { |
| 123 | type Iter = slice::Iter<'q, T>; |
| 124 | |
| 125 | fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { |
| 126 | (self.iter(), HasIter) |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | impl<'q, T: 'q> RepAsIteratorExt<'q> for Vec<T> { |
| 131 | type Iter = slice::Iter<'q, T>; |
| 132 | |
| 133 | fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { |
| 134 | (self.iter(), HasIter) |
| 135 | } |
| 136 | } |
| 137 | |
| 138 | impl<'q, T: 'q> RepAsIteratorExt<'q> for BTreeSet<T> { |
| 139 | type Iter = btree_set::Iter<'q, T>; |
| 140 | |
| 141 | fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { |
| 142 | (self.iter(), HasIter) |
| 143 | } |
| 144 | } |
| 145 | |
| 146 | impl<'q, T: RepAsIteratorExt<'q>> RepAsIteratorExt<'q> for RepInterp<T> { |
| 147 | type Iter = T::Iter; |
| 148 | |
| 149 | fn quote_into_iter(&'q self) -> (Self::Iter, HasIter) { |
| 150 | self.0.quote_into_iter() |
| 151 | } |
| 152 | } |
| 153 | } |
| 154 | |
| 155 | // Helper type used within interpolations to allow for repeated binding names. |
| 156 | // Implements the relevant traits, and exports a dummy `next()` method. |
| 157 | #[derive(Copy, Clone)] |
| 158 | #[doc (hidden)] |
| 159 | pub struct RepInterp<T>(pub T); |
| 160 | |
| 161 | impl<T> RepInterp<T> { |
| 162 | // This method is intended to look like `Iterator::next`, and is called when |
| 163 | // a name is bound multiple times, as the previous binding will shadow the |
| 164 | // original `Iterator` object. This allows us to avoid advancing the |
| 165 | // iterator multiple times per iteration. |
| 166 | pub fn next(self) -> Option<T> { |
| 167 | Some(self.0) |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | impl<T: Iterator> Iterator for RepInterp<T> { |
| 172 | type Item = T::Item; |
| 173 | |
| 174 | fn next(&mut self) -> Option<Self::Item> { |
| 175 | self.0.next() |
| 176 | } |
| 177 | } |
| 178 | |
| 179 | impl<T: ToTokens> ToTokens for RepInterp<T> { |
| 180 | fn to_tokens(&self, tokens: &mut TokenStream) { |
| 181 | self.0.to_tokens(tokens); |
| 182 | } |
| 183 | } |
| 184 | |
| 185 | macro_rules! minimal_quote_tt { |
| 186 | (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, minimal_quote!($($t)*)) }; |
| 187 | ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, minimal_quote!($($t)*)) }; |
| 188 | ({$($t:tt)*}) => { Group::new(Delimiter::Brace, minimal_quote!($($t)*)) }; |
| 189 | (,) => { Punct::new(',' , Spacing::Alone) }; |
| 190 | (.) => { Punct::new('.' , Spacing::Alone) }; |
| 191 | (;) => { Punct::new(';' , Spacing::Alone) }; |
| 192 | (!) => { Punct::new('!' , Spacing::Alone) }; |
| 193 | (<) => { Punct::new('<' , Spacing::Alone) }; |
| 194 | (>) => { Punct::new('>' , Spacing::Alone) }; |
| 195 | (&) => { Punct::new('&' , Spacing::Alone) }; |
| 196 | (=) => { Punct::new('=' , Spacing::Alone) }; |
| 197 | (#) => { Punct::new('#' , Spacing::Alone) }; |
| 198 | (|) => { Punct::new('|' , Spacing::Alone) }; |
| 199 | (:) => { Punct::new(':' , Spacing::Alone) }; |
| 200 | (*) => { Punct::new('*' , Spacing::Alone) }; |
| 201 | (_) => { Ident::new("_" , Span::def_site()) }; |
| 202 | ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) }; |
| 203 | ($lit:literal) => { stringify!($lit).parse::<Literal>().unwrap() }; |
| 204 | } |
| 205 | |
| 206 | macro_rules! minimal_quote_ts { |
| 207 | ((@ $($t:tt)*)) => { $($t)* }; |
| 208 | (::) => { |
| 209 | { |
| 210 | let mut c = ( |
| 211 | TokenTree::from(Punct::new(':' , Spacing::Joint)), |
| 212 | TokenTree::from(Punct::new(':' , Spacing::Alone)) |
| 213 | ); |
| 214 | c.0.set_span(Span::def_site()); |
| 215 | c.1.set_span(Span::def_site()); |
| 216 | [c.0, c.1].into_iter().collect::<TokenStream>() |
| 217 | } |
| 218 | }; |
| 219 | (=>) => { |
| 220 | { |
| 221 | let mut c = ( |
| 222 | TokenTree::from(Punct::new('=' , Spacing::Joint)), |
| 223 | TokenTree::from(Punct::new('>' , Spacing::Alone)) |
| 224 | ); |
| 225 | c.0.set_span(Span::def_site()); |
| 226 | c.1.set_span(Span::def_site()); |
| 227 | [c.0, c.1].into_iter().collect::<TokenStream>() |
| 228 | } |
| 229 | }; |
| 230 | (+=) => { |
| 231 | { |
| 232 | let mut c = ( |
| 233 | TokenTree::from(Punct::new('+' , Spacing::Joint)), |
| 234 | TokenTree::from(Punct::new('=' , Spacing::Alone)) |
| 235 | ); |
| 236 | c.0.set_span(Span::def_site()); |
| 237 | c.1.set_span(Span::def_site()); |
| 238 | [c.0, c.1].into_iter().collect::<TokenStream>() |
| 239 | } |
| 240 | }; |
| 241 | (!=) => { |
| 242 | { |
| 243 | let mut c = ( |
| 244 | TokenTree::from(Punct::new('!' , Spacing::Joint)), |
| 245 | TokenTree::from(Punct::new('=' , Spacing::Alone)) |
| 246 | ); |
| 247 | c.0.set_span(Span::def_site()); |
| 248 | c.1.set_span(Span::def_site()); |
| 249 | [c.0, c.1].into_iter().collect::<TokenStream>() |
| 250 | } |
| 251 | }; |
| 252 | ($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) }; |
| 253 | } |
| 254 | |
| 255 | /// Simpler version of the real `quote!` macro, implemented solely |
| 256 | /// through `macro_rules`, for bootstrapping the real implementation |
| 257 | /// (see the `quote` function), which does not have access to the |
| 258 | /// real `quote!` macro due to the `proc_macro` crate not being |
| 259 | /// able to depend on itself. |
| 260 | /// |
| 261 | /// Note: supported tokens are a subset of the real `quote!`, but |
| 262 | /// unquoting is different: instead of `$x`, this uses `(@ expr)`. |
| 263 | macro_rules! minimal_quote { |
| 264 | ($($t:tt)*) => { |
| 265 | { |
| 266 | #[allow(unused_mut)] // In case the expansion is empty |
| 267 | let mut ts = TokenStream::new(); |
| 268 | $(ToTokens::to_tokens(&minimal_quote_ts!($t), &mut ts);)* |
| 269 | ts |
| 270 | } |
| 271 | }; |
| 272 | } |
| 273 | |
| 274 | /// Quote a `TokenStream` into a `TokenStream`. |
| 275 | /// This is the actual implementation of the `quote!()` proc macro. |
| 276 | /// |
| 277 | /// It is loaded by the compiler in `register_builtin_macros`. |
| 278 | #[unstable (feature = "proc_macro_quote" , issue = "54722" )] |
| 279 | pub fn quote(stream: TokenStream) -> TokenStream { |
| 280 | if stream.is_empty() { |
| 281 | return minimal_quote!(crate::TokenStream::new()); |
| 282 | } |
| 283 | let proc_macro_crate = minimal_quote!(crate); |
| 284 | let mut after_dollar = false; |
| 285 | |
| 286 | let mut tokens = crate::TokenStream::new(); |
| 287 | let mut iter = stream.into_iter().peekable(); |
| 288 | while let Some(tree) = iter.next() { |
| 289 | if after_dollar { |
| 290 | after_dollar = false; |
| 291 | match tree { |
| 292 | TokenTree::Group(tt) => { |
| 293 | // Handles repetition by expanding `$( CONTENTS ) SEP_OPT *` to `{ REP_EXPANDED }`. |
| 294 | let contents = tt.stream(); |
| 295 | |
| 296 | // The `*` token is also consumed here. |
| 297 | let sep_opt: Option<Punct> = match (iter.next(), iter.peek()) { |
| 298 | (Some(TokenTree::Punct(sep)), Some(TokenTree::Punct(star))) |
| 299 | if sep.spacing() == Spacing::Joint && star.as_char() == '*' => |
| 300 | { |
| 301 | iter.next(); |
| 302 | Some(sep) |
| 303 | } |
| 304 | (Some(TokenTree::Punct(star)), _) if star.as_char() == '*' => None, |
| 305 | _ => panic!("`$(...)` must be followed by `*` in `quote!`" ), |
| 306 | }; |
| 307 | |
| 308 | let mut rep_expanded = TokenStream::new(); |
| 309 | |
| 310 | // Append setup code for a `while`, where recursively quoted `CONTENTS` |
| 311 | // and `SEP_OPT` are repeatedly processed, to `REP_EXPANDED`. |
| 312 | let meta_vars = collect_meta_vars(contents.clone()); |
| 313 | minimal_quote!( |
| 314 | use crate::ext::*; |
| 315 | (@ if sep_opt.is_some() { |
| 316 | minimal_quote!(let mut _i = 0usize;) |
| 317 | } else { |
| 318 | minimal_quote!(();) |
| 319 | }) |
| 320 | let has_iter = crate::ThereIsNoIteratorInRepetition; |
| 321 | ) |
| 322 | .to_tokens(&mut rep_expanded); |
| 323 | for meta_var in &meta_vars { |
| 324 | minimal_quote!( |
| 325 | #[allow(unused_mut)] |
| 326 | let (mut (@ meta_var), i) = (@ meta_var).quote_into_iter(); |
| 327 | let has_iter = has_iter | i; |
| 328 | ) |
| 329 | .to_tokens(&mut rep_expanded); |
| 330 | } |
| 331 | minimal_quote!(let _: crate::HasIterator = has_iter;) |
| 332 | .to_tokens(&mut rep_expanded); |
| 333 | |
| 334 | // Append the `while` to `REP_EXPANDED`. |
| 335 | let mut while_body = TokenStream::new(); |
| 336 | for meta_var in &meta_vars { |
| 337 | minimal_quote!( |
| 338 | let (@ meta_var) = match (@ meta_var).next() { |
| 339 | Some(_x) => crate::RepInterp(_x), |
| 340 | None => break, |
| 341 | }; |
| 342 | ) |
| 343 | .to_tokens(&mut while_body); |
| 344 | } |
| 345 | minimal_quote!( |
| 346 | (@ if let Some(sep) = sep_opt { |
| 347 | minimal_quote!( |
| 348 | if _i > 0 { |
| 349 | (@ minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new( |
| 350 | (@ TokenTree::from(Literal::character(sep.as_char()))), |
| 351 | (@ minimal_quote!(crate::Spacing::Alone)), |
| 352 | )), &mut ts);)) |
| 353 | } |
| 354 | _i += 1; |
| 355 | ) |
| 356 | } else { |
| 357 | minimal_quote!(();) |
| 358 | }) |
| 359 | (@ quote(contents.clone())).to_tokens(&mut ts); |
| 360 | ) |
| 361 | .to_tokens(&mut while_body); |
| 362 | rep_expanded.extend(vec![ |
| 363 | TokenTree::Ident(Ident::new("while" , Span::call_site())), |
| 364 | TokenTree::Ident(Ident::new("true" , Span::call_site())), |
| 365 | TokenTree::Group(Group::new(Delimiter::Brace, while_body)), |
| 366 | ]); |
| 367 | |
| 368 | minimal_quote!((@ TokenTree::Group(Group::new(Delimiter::Brace, rep_expanded)))).to_tokens(&mut tokens); |
| 369 | continue; |
| 370 | } |
| 371 | TokenTree::Ident(_) => { |
| 372 | minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);) |
| 373 | .to_tokens(&mut tokens); |
| 374 | continue; |
| 375 | } |
| 376 | TokenTree::Punct(ref tt) if tt.as_char() == '$' => {} |
| 377 | _ => panic!( |
| 378 | "`$` must be followed by an ident or `$` or a repetition group in `quote!`" |
| 379 | ), |
| 380 | } |
| 381 | } else if let TokenTree::Punct(ref tt) = tree { |
| 382 | if tt.as_char() == '$' { |
| 383 | after_dollar = true; |
| 384 | continue; |
| 385 | } |
| 386 | } |
| 387 | |
| 388 | match tree { |
| 389 | TokenTree::Punct(tt) => { |
| 390 | minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new( |
| 391 | (@ TokenTree::from(Literal::character(tt.as_char()))), |
| 392 | (@ match tt.spacing() { |
| 393 | Spacing::Alone => minimal_quote!(crate::Spacing::Alone), |
| 394 | Spacing::Joint => minimal_quote!(crate::Spacing::Joint), |
| 395 | }), |
| 396 | )), &mut ts);) |
| 397 | } |
| 398 | TokenTree::Group(tt) => { |
| 399 | minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Group(crate::Group::new( |
| 400 | (@ match tt.delimiter() { |
| 401 | Delimiter::Parenthesis => minimal_quote!(crate::Delimiter::Parenthesis), |
| 402 | Delimiter::Brace => minimal_quote!(crate::Delimiter::Brace), |
| 403 | Delimiter::Bracket => minimal_quote!(crate::Delimiter::Bracket), |
| 404 | Delimiter::None => minimal_quote!(crate::Delimiter::None), |
| 405 | }), |
| 406 | (@ quote(tt.stream())), |
| 407 | )), &mut ts);) |
| 408 | } |
| 409 | TokenTree::Ident(tt) => { |
| 410 | let literal = tt.to_string(); |
| 411 | let (literal, ctor) = if let Some(stripped) = literal.strip_prefix("r#" ) { |
| 412 | (stripped, minimal_quote!(crate::Ident::new_raw)) |
| 413 | } else { |
| 414 | (literal.as_str(), minimal_quote!(crate::Ident::new)) |
| 415 | }; |
| 416 | minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Ident((@ ctor)( |
| 417 | (@ TokenTree::from(Literal::string(literal))), |
| 418 | (@ quote_span(proc_macro_crate.clone(), tt.span())), |
| 419 | )), &mut ts);) |
| 420 | } |
| 421 | TokenTree::Literal(tt) => { |
| 422 | minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Literal({ |
| 423 | let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string()))) |
| 424 | .parse::<crate::TokenStream>() |
| 425 | .unwrap() |
| 426 | .into_iter(); |
| 427 | if let (Some(crate::TokenTree::Literal(mut lit)), None) = |
| 428 | (iter.next(), iter.next()) |
| 429 | { |
| 430 | lit.set_span((@ quote_span(proc_macro_crate.clone(), tt.span()))); |
| 431 | lit |
| 432 | } else { |
| 433 | unreachable!() |
| 434 | } |
| 435 | }), &mut ts);) |
| 436 | } |
| 437 | } |
| 438 | .to_tokens(&mut tokens); |
| 439 | } |
| 440 | if after_dollar { |
| 441 | panic!("unexpected trailing `$` in `quote!`" ); |
| 442 | } |
| 443 | |
| 444 | minimal_quote! { |
| 445 | { |
| 446 | let mut ts = crate::TokenStream::new(); |
| 447 | (@ tokens) |
| 448 | ts |
| 449 | } |
| 450 | } |
| 451 | } |
| 452 | |
| 453 | /// Helper function to support macro repetitions like `$( CONTENTS ) SEP_OPT *` in `quote!`. |
| 454 | /// Recursively collects all `Ident`s (meta-variables) that follow a `$` |
| 455 | /// from the given `CONTENTS` stream, preserving their order of appearance. |
| 456 | fn collect_meta_vars(content_stream: TokenStream) -> Vec<Ident> { |
| 457 | fn helper(stream: TokenStream, out: &mut Vec<Ident>) { |
| 458 | let mut iter = stream.into_iter().peekable(); |
| 459 | while let Some(tree: TokenTree) = iter.next() { |
| 460 | match &tree { |
| 461 | TokenTree::Punct(tt: &Punct) if tt.as_char() == '$' => { |
| 462 | if let Some(TokenTree::Ident(id: Ident)) = iter.peek() { |
| 463 | out.push(id.clone()); |
| 464 | iter.next(); |
| 465 | } |
| 466 | } |
| 467 | TokenTree::Group(tt: &Group) => { |
| 468 | helper(tt.stream(), out); |
| 469 | } |
| 470 | _ => {} |
| 471 | } |
| 472 | } |
| 473 | } |
| 474 | |
| 475 | let mut vars = Vec::new(); |
| 476 | helper(content_stream, &mut vars); |
| 477 | vars |
| 478 | } |
| 479 | |
| 480 | /// Quote a `Span` into a `TokenStream`. |
| 481 | /// This is needed to implement a custom quoter. |
| 482 | #[unstable (feature = "proc_macro_quote" , issue = "54722" )] |
| 483 | pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream { |
| 484 | let id: usize = span.save_span(); |
| 485 | minimal_quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id))))) |
| 486 | } |
| 487 | |