Skip to content

Commit 56aaa53

Browse files
committed
proc_macro: clean up the implementation of quasi-quoting.
1 parent d10d0b3 commit 56aaa53

File tree

4 files changed

+101
-185
lines changed

4 files changed

+101
-185
lines changed

src/libproc_macro/lib.rs

+4-10
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,9 @@ impl fmt::Debug for TokenStream {
145145
}
146146
}
147147

148+
#[unstable(feature = "proc_macro_quote", issue = "38356")]
149+
pub use quote::{quote, quote_span};
150+
148151
/// Creates a token stream containing a single token tree.
149152
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
150153
impl From<TokenTree> for TokenStream {
@@ -237,7 +240,7 @@ pub mod token_stream {
237240
/// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term.
238241
/// To quote `$` itself, use `$$`.
239242
///
240-
/// This is a dummy macro, the actual implementation is in quote::Quoter
243+
/// This is a dummy macro, the actual implementation is in `quote::quote`.`
241244
#[unstable(feature = "proc_macro_quote", issue = "38356")]
242245
#[macro_export]
243246
macro_rules! quote { () => {} }
@@ -246,13 +249,6 @@ macro_rules! quote { () => {} }
246249
#[doc(hidden)]
247250
mod quote;
248251

249-
/// Quote a `Span` into a `TokenStream`.
250-
/// This is needed to implement a custom quoter.
251-
#[unstable(feature = "proc_macro_quote", issue = "38356")]
252-
pub fn quote_span(span: Span) -> TokenStream {
253-
quote::Quote::quote(span)
254-
}
255-
256252
/// A region of source code, along with macro expansion information.
257253
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
258254
#[derive(Copy, Clone)]
@@ -1364,8 +1360,6 @@ impl TokenTree {
13641360
#[unstable(feature = "proc_macro_internals", issue = "27812")]
13651361
#[doc(hidden)]
13661362
pub mod __internal {
1367-
pub use quote::{Quoter, unquote};
1368-
13691363
use std::cell::Cell;
13701364
use std::ptr;
13711365

src/libproc_macro/quote.rs

+92-173
Original file line numberDiff line numberDiff line change
@@ -14,34 +14,26 @@
1414
//! This quasiquoter uses macros 2.0 hygiene to reliably access
1515
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
1616
17-
use {Delimiter, Literal, Spacing, Span, Ident, Punct, Group, TokenStream, TokenTree};
18-
19-
use syntax::ext::base::{ExtCtxt, ProcMacro};
20-
use syntax::tokenstream;
21-
22-
/// This is the actual quote!() proc macro
23-
///
24-
/// It is manually loaded in CStore::load_macro_untracked
25-
pub struct Quoter;
26-
27-
pub fn unquote<T: Into<TokenStream> + Clone>(tokens: &T) -> TokenStream {
28-
tokens.clone().into()
29-
}
30-
31-
pub trait Quote {
32-
fn quote(self) -> TokenStream;
33-
}
34-
35-
macro_rules! tt2ts {
36-
($e:expr) => (TokenStream::from(TokenTree::from($e)))
37-
}
38-
39-
macro_rules! quote_tok {
40-
(,) => { tt2ts!(Punct::new(',', Spacing::Alone)) };
41-
(.) => { tt2ts!(Punct::new('.', Spacing::Alone)) };
42-
(:) => { tt2ts!(Punct::new(':', Spacing::Alone)) };
43-
(;) => { tt2ts!(Punct::new(';', Spacing::Alone)) };
44-
(|) => { tt2ts!(Punct::new('|', Spacing::Alone)) };
17+
use {Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
18+
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()) };
33+
}
34+
35+
macro_rules! quote_ts {
36+
((@ $($t:tt)*)) => { $($t)* };
4537
(::) => {
4638
[
4739
TokenTree::from(Punct::new(':', Spacing::Joint)),
@@ -54,57 +46,45 @@ macro_rules! quote_tok {
5446
})
5547
.collect::<TokenStream>()
5648
};
57-
(!) => { tt2ts!(Punct::new('!', Spacing::Alone)) };
58-
(<) => { tt2ts!(Punct::new('<', Spacing::Alone)) };
59-
(>) => { tt2ts!(Punct::new('>', Spacing::Alone)) };
60-
(_) => { tt2ts!(Punct::new('_', Spacing::Alone)) };
61-
(0) => { tt2ts!(Literal::i8_unsuffixed(0)) };
62-
(&) => { tt2ts!(Punct::new('&', Spacing::Alone)) };
63-
(=) => { tt2ts!(Punct::new('=', Spacing::Alone)) };
64-
($i:ident) => { tt2ts!(Ident::new(stringify!($i), Span::def_site())) };
65-
}
66-
67-
macro_rules! quote_tree {
68-
((unquote $($t:tt)*)) => { $($t)* };
69-
((quote $($t:tt)*)) => { ($($t)*).quote() };
70-
(($($t:tt)*)) => { tt2ts!(Group::new(Delimiter::Parenthesis, quote!($($t)*))) };
71-
([$($t:tt)*]) => { tt2ts!(Group::new(Delimiter::Bracket, quote!($($t)*))) };
72-
({$($t:tt)*}) => { tt2ts!(Group::new(Delimiter::Brace, quote!($($t)*))) };
73-
($t:tt) => { quote_tok!($t) };
49+
($t:tt) => { TokenTree::from(quote_tt!($t)) };
7450
}
7551

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.
57+
///
58+
/// Note: supported tokens are a subset of the real `quote!`, but
59+
/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
7660
macro_rules! quote {
7761
() => { TokenStream::new() };
7862
($($t:tt)*) => {
79-
[$(quote_tree!($t),)*].iter()
80-
.cloned()
81-
.flat_map(|x| x.into_iter())
82-
.collect::<TokenStream>()
63+
[
64+
$(TokenStream::from(quote_ts!($t)),)*
65+
].iter().cloned().collect::<TokenStream>()
8366
};
8467
}
8568

86-
impl ProcMacro for Quoter {
87-
fn expand<'cx>(&self, cx: &'cx mut ExtCtxt,
88-
_: ::syntax_pos::Span,
89-
stream: tokenstream::TokenStream)
90-
-> tokenstream::TokenStream {
91-
::__internal::set_sess(cx, || TokenStream(stream).quote().0)
69+
/// Quote a `TokenStream` into a `TokenStream`.
70+
/// This is the actual `quote!()` proc macro.
71+
///
72+
/// It is manually loaded in `CStore::load_macro_untracked`.
73+
#[unstable(feature = "proc_macro_quote", issue = "38356")]
74+
pub fn quote(stream: TokenStream) -> TokenStream {
75+
if stream.is_empty() {
76+
return quote!(::TokenStream::new());
9277
}
93-
}
94-
95-
impl Quote for TokenStream {
96-
fn quote(self) -> TokenStream {
97-
if self.is_empty() {
98-
return quote!(::TokenStream::new());
99-
}
100-
let mut after_dollar = false;
101-
let tokens = self.into_iter().filter_map(|tree| {
78+
let mut after_dollar = false;
79+
let tokens = stream
80+
.into_iter()
81+
.filter_map(|tree| {
10282
if after_dollar {
10383
after_dollar = false;
10484
match tree {
10585
TokenTree::Ident(_) => {
106-
let tree = TokenStream::from(tree);
107-
return Some(quote!(::__internal::unquote(&(unquote tree)),));
86+
return Some(quote!(Into::<::TokenStream>::into(
87+
Clone::clone(&(@ tree))),));
10888
}
10989
TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
11090
_ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
@@ -116,116 +96,55 @@ impl Quote for TokenStream {
11696
}
11797
}
11898

119-
Some(quote!(::TokenStream::from((quote tree)),))
120-
}).flat_map(|t| t.into_iter()).collect::<TokenStream>();
121-
122-
if after_dollar {
123-
panic!("unexpected trailing `$` in `quote!`");
124-
}
125-
126-
quote!(
127-
[(unquote tokens)].iter()
128-
.cloned()
129-
.flat_map(|x| x.into_iter())
130-
.collect::<::TokenStream>()
131-
)
132-
}
133-
}
134-
135-
impl Quote for TokenTree {
136-
fn quote(self) -> TokenStream {
137-
match self {
138-
TokenTree::Punct(tt) => quote!(::TokenTree::Punct( (quote tt) )),
139-
TokenTree::Group(tt) => quote!(::TokenTree::Group( (quote tt) )),
140-
TokenTree::Ident(tt) => quote!(::TokenTree::Ident( (quote tt) )),
141-
TokenTree::Literal(tt) => quote!(::TokenTree::Literal( (quote tt) )),
142-
}
143-
}
144-
}
145-
146-
impl Quote for char {
147-
fn quote(self) -> TokenStream {
148-
TokenTree::from(Literal::character(self)).into()
149-
}
150-
}
151-
152-
impl<'a> Quote for &'a str {
153-
fn quote(self) -> TokenStream {
154-
TokenTree::from(Literal::string(self)).into()
155-
}
156-
}
157-
158-
impl Quote for u16 {
159-
fn quote(self) -> TokenStream {
160-
TokenTree::from(Literal::u16_unsuffixed(self)).into()
161-
}
162-
}
163-
164-
impl Quote for Group {
165-
fn quote(self) -> TokenStream {
166-
quote!(::Group::new((quote self.delimiter()), (quote self.stream())))
167-
}
168-
}
169-
170-
impl Quote for Punct {
171-
fn quote(self) -> TokenStream {
172-
quote!(::Punct::new((quote self.as_char()), (quote self.spacing())))
173-
}
174-
}
175-
176-
impl Quote for Ident {
177-
fn quote(self) -> TokenStream {
178-
quote!(::Ident::new((quote self.sym.as_str()), (quote self.span())))
179-
}
180-
}
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),
105+
}),
106+
))),
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),
113+
}),
114+
(@ quote(tt.stream())),
115+
))),
116+
TokenTree::Ident(tt) => quote!(::TokenTree::Ident(::Ident::new(
117+
(@ TokenTree::from(Literal::string(&tt.to_string()))),
118+
(@ quote_span(tt.span())),
119+
))),
120+
TokenTree::Literal(tt) => quote!(::TokenTree::Literal({
121+
let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
122+
.parse::<::TokenStream>()
123+
.unwrap()
124+
.into_iter();
125+
if let (Some(::TokenTree::Literal(mut lit)), None) =
126+
(iter.next(), iter.next())
127+
{
128+
lit.set_span((@ quote_span(tt.span())));
129+
lit
130+
} else {
131+
unreachable!()
132+
}
133+
}))
134+
})),))
135+
})
136+
.collect::<TokenStream>();
181137

182-
impl Quote for Span {
183-
fn quote(self) -> TokenStream {
184-
quote!(::Span::def_site())
138+
if after_dollar {
139+
panic!("unexpected trailing `$` in `quote!`");
185140
}
186-
}
187-
188-
impl Quote for Literal {
189-
fn quote(self) -> TokenStream {
190-
quote! {{
191-
let mut iter = (quote self.to_string())
192-
.parse::<::TokenStream>()
193-
.unwrap()
194-
.into_iter();
195-
if let (Some(::TokenTree::Literal(mut lit)), None) = (iter.next(), iter.next()) {
196-
lit.set_span((quote self.span));
197-
lit
198-
} else {
199-
unreachable!()
200-
}
201-
}}
202-
}
203-
}
204-
205-
impl Quote for Delimiter {
206-
fn quote(self) -> TokenStream {
207-
macro_rules! gen_match {
208-
($($i:ident),*) => {
209-
match self {
210-
$(Delimiter::$i => { quote!(::Delimiter::$i) })*
211-
}
212-
}
213-
}
214141

215-
gen_match!(Parenthesis, Brace, Bracket, None)
216-
}
142+
quote!([(@ tokens)].iter().cloned().collect::<::TokenStream>())
217143
}
218144

219-
impl Quote for Spacing {
220-
fn quote(self) -> TokenStream {
221-
macro_rules! gen_match {
222-
($($i:ident),*) => {
223-
match self {
224-
$(Spacing::$i => { quote!(::Spacing::$i) })*
225-
}
226-
}
227-
}
228-
229-
gen_match!(Alone, Joint)
230-
}
145+
/// Quote a `Span` into a `TokenStream`.
146+
/// This is needed to implement a custom quoter.
147+
#[unstable(feature = "proc_macro_quote", issue = "38356")]
148+
pub fn quote_span(_: Span) -> TokenStream {
149+
quote!(::Span::def_site())
231150
}

src/librustc_metadata/cstore_impl.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,6 @@ use syntax::ast;
3939
use syntax::attr;
4040
use syntax::codemap;
4141
use syntax::edition::Edition;
42-
use syntax::ext::base::SyntaxExtension;
4342
use syntax::parse::filemap_to_stream;
4443
use syntax::symbol::Symbol;
4544
use syntax_pos::{Span, NO_EXPANSION, FileName};
@@ -517,8 +516,11 @@ impl CrateStore for cstore::CStore {
517516
return LoadedMacro::ProcMacro(proc_macros[id.index.to_proc_macro_index()].1.clone());
518517
} else if data.name == "proc_macro" &&
519518
self.get_crate_data(id.krate).item_name(id.index) == "quote" {
519+
use syntax::ext::base::SyntaxExtension;
520+
use syntax_ext::proc_macro_impl::BangProcMacro;
521+
520522
let ext = SyntaxExtension::ProcMacro {
521-
expander: Box::new(::proc_macro::__internal::Quoter),
523+
expander: Box::new(BangProcMacro { inner: ::proc_macro::quote }),
522524
allow_internal_unstable: true,
523525
edition: data.root.edition,
524526
};

src/librustc_metadata/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#![feature(libc)]
2020
#![feature(macro_at_most_once_rep)]
2121
#![feature(proc_macro_internals)]
22+
#![feature(proc_macro_quote)]
2223
#![feature(quote)]
2324
#![feature(rustc_diagnostic_macros)]
2425
#![feature(slice_sort_by_cached_key)]

0 commit comments

Comments
 (0)