1 // Copyright 2016 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
12 //! This file contains the implementation internals of the quasiquoter provided by `quote!`.
14 //! This quasiquoter uses macros 2.0 hygiene to reliably access
15 //! items from `proc_macro`, to build a `proc_macro::TokenStream`.
17 use {Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
19 macro_rules! quote_tt {
20 (($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) };
21 ([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) };
22 ({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
23 (,) => { Punct::new(',', Spacing::Alone) };
24 (.) => { Punct::new('.', Spacing::Alone) };
25 (:) => { Punct::new(':', Spacing::Alone) };
26 (;) => { Punct::new(';', Spacing::Alone) };
27 (!) => { Punct::new('!', Spacing::Alone) };
28 (<) => { Punct::new('<', Spacing::Alone) };
29 (>) => { Punct::new('>', Spacing::Alone) };
30 (&) => { Punct::new('&', Spacing::Alone) };
31 (=) => { Punct::new('=', Spacing::Alone) };
32 ($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
35 macro_rules! quote_ts {
36 ((@ $($t:tt)*)) => { $($t)* };
39 TokenTree::from(Punct::new(':', Spacing::Joint)),
40 TokenTree::from(Punct::new(':', Spacing::Alone)),
44 x.set_span(Span::def_site());
47 .collect::<TokenStream>()
49 ($t:tt) => { TokenTree::from(quote_tt!($t)) };
52 /// Simpler version of the real `quote!` macro, implemented solely
53 /// through `macro_rules`, for bootstrapping the real implementation
54 /// (see the `quote` function), which does not have access to the
55 /// real `quote!` macro due to the `proc_macro` crate not being
56 /// able to depend on itself.
58 /// Note: supported tokens are a subset of the real `quote!`, but
59 /// unquoting is different: instead of `$x`, this uses `(@ expr)`.
61 () => { TokenStream::new() };
64 $(TokenStream::from(quote_ts!($t)),)*
65 ].iter().cloned().collect::<TokenStream>()
69 /// Quote a `TokenStream` into a `TokenStream`.
70 /// This is the actual `quote!()` proc macro.
72 /// It is manually loaded in `CStore::load_macro_untracked`.
73 #[unstable(feature = "proc_macro_quote", issue = "54722")]
74 pub fn quote(stream: TokenStream) -> TokenStream {
75 if stream.is_empty() {
76 return quote!(::TokenStream::new());
78 let mut after_dollar = false;
85 TokenTree::Ident(_) => {
86 return Some(quote!(Into::<::TokenStream>::into(
87 Clone::clone(&(@ tree))),));
89 TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
90 _ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
92 } else if let TokenTree::Punct(ref tt) = tree {
93 if tt.as_char() == '$' {
99 Some(quote!(::TokenStream::from((@ match tree {
100 TokenTree::Punct(tt) => quote!(::TokenTree::Punct(::Punct::new(
101 (@ TokenTree::from(Literal::character(tt.as_char()))),
102 (@ match tt.spacing() {
103 Spacing::Alone => quote!(::Spacing::Alone),
104 Spacing::Joint => quote!(::Spacing::Joint),
107 TokenTree::Group(tt) => quote!(::TokenTree::Group(::Group::new(
108 (@ match tt.delimiter() {
109 Delimiter::Parenthesis => quote!(::Delimiter::Parenthesis),
110 Delimiter::Brace => quote!(::Delimiter::Brace),
111 Delimiter::Bracket => quote!(::Delimiter::Bracket),
112 Delimiter::None => quote!(::Delimiter::None),
114 (@ quote(tt.stream())),
116 TokenTree::Ident(tt) => quote!(::TokenTree::Ident(::Ident::new(
117 (@ TokenTree::from(Literal::string(&tt.to_string()))),
118 (@ quote_span(tt.span())),
120 TokenTree::Literal(tt) => quote!(::TokenTree::Literal({
121 let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
122 .parse::<::TokenStream>()
125 if let (Some(::TokenTree::Literal(mut lit)), None) =
126 (iter.next(), iter.next())
128 lit.set_span((@ quote_span(tt.span())));
136 .collect::<TokenStream>();
139 panic!("unexpected trailing `$` in `quote!`");
142 quote!([(@ tokens)].iter().cloned().collect::<::TokenStream>())
145 /// Quote a `Span` into a `TokenStream`.
146 /// This is needed to implement a custom quoter.
147 #[unstable(feature = "proc_macro_quote", issue = "54722")]
148 pub fn quote_span(_: Span) -> TokenStream {
149 quote!(::Span::def_site())