Skip to content
/ rust Public
forked from rust-lang/rust

Commit 34fa27b

Browse files
authored
Rollup merge of rust-lang#134693 - SpriteOvO:proc-macro-use-to-tokens-in-quote, r=tgross35
proc_macro: Use `ToTokens` trait in `quote` macro Tracking issues: rust-lang#130977, rust-lang#54722 This PR changed `proc_macro::quote!` to use `ToTokens` trait instead of `TokenStream::from`, and migrated test cases from `quote` crate. r? `@dtolnay` CC `@tgross35`
2 parents 5eec2b0 + ef69c30 commit 34fa27b

19 files changed

+691
-113
lines changed

library/proc_macro/src/lib.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -419,7 +419,7 @@ pub mod token_stream {
419419
/// Unquoting is done with `$`, and works by taking the single next ident as the unquoted term.
420420
/// To quote `$` itself, use `$$`.
421421
#[unstable(feature = "proc_macro_quote", issue = "54722")]
422-
#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals)]
422+
#[allow_internal_unstable(proc_macro_def_site, proc_macro_internals, proc_macro_totokens)]
423423
#[rustc_builtin_macro]
424424
pub macro quote($($t:tt)*) {
425425
/* compiler built-in */

library/proc_macro/src/quote.rs

+86-63
Original file line numberDiff line numberDiff line change
@@ -4,12 +4,14 @@
44
//! This quasiquoter uses macros 2.0 hygiene to reliably access
55
//! items from `proc_macro`, to build a `proc_macro::TokenStream`.
66
7-
use crate::{Delimiter, Group, Ident, Literal, Punct, Spacing, Span, TokenStream, TokenTree};
7+
use crate::{
8+
Delimiter, Group, Ident, Literal, Punct, Spacing, Span, ToTokens, TokenStream, TokenTree,
9+
};
810

9-
macro_rules! quote_tt {
10-
(($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, quote!($($t)*)) };
11-
([$($t:tt)*]) => { Group::new(Delimiter::Bracket, quote!($($t)*)) };
12-
({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
11+
macro_rules! minimal_quote_tt {
12+
(($($t:tt)*)) => { Group::new(Delimiter::Parenthesis, minimal_quote!($($t)*)) };
13+
([$($t:tt)*]) => { Group::new(Delimiter::Bracket, minimal_quote!($($t)*)) };
14+
({$($t:tt)*}) => { Group::new(Delimiter::Brace, minimal_quote!($($t)*)) };
1315
(,) => { Punct::new(',', Spacing::Alone) };
1416
(.) => { Punct::new('.', Spacing::Alone) };
1517
(;) => { Punct::new(';', Spacing::Alone) };
@@ -21,21 +23,20 @@ macro_rules! quote_tt {
2123
($i:ident) => { Ident::new(stringify!($i), Span::def_site()) };
2224
}
2325

24-
macro_rules! quote_ts {
26+
macro_rules! minimal_quote_ts {
2527
((@ $($t:tt)*)) => { $($t)* };
2628
(::) => {
27-
[
28-
TokenTree::from(Punct::new(':', Spacing::Joint)),
29-
TokenTree::from(Punct::new(':', Spacing::Alone)),
30-
].iter()
31-
.cloned()
32-
.map(|mut x| {
33-
x.set_span(Span::def_site());
34-
x
35-
})
36-
.collect::<TokenStream>()
29+
{
30+
let mut c = (
31+
TokenTree::from(Punct::new(':', Spacing::Joint)),
32+
TokenTree::from(Punct::new(':', Spacing::Alone))
33+
);
34+
c.0.set_span(Span::def_site());
35+
c.1.set_span(Span::def_site());
36+
[c.0, c.1].into_iter().collect::<TokenStream>()
37+
}
3738
};
38-
($t:tt) => { TokenTree::from(quote_tt!($t)) };
39+
($t:tt) => { TokenTree::from(minimal_quote_tt!($t)) };
3940
}
4041

4142
/// Simpler version of the real `quote!` macro, implemented solely
@@ -46,12 +47,14 @@ macro_rules! quote_ts {
4647
///
4748
/// Note: supported tokens are a subset of the real `quote!`, but
4849
/// unquoting is different: instead of `$x`, this uses `(@ expr)`.
49-
macro_rules! quote {
50-
() => { TokenStream::new() };
50+
macro_rules! minimal_quote {
5151
($($t:tt)*) => {
52-
[
53-
$(TokenStream::from(quote_ts!($t)),)*
54-
].iter().cloned().collect::<TokenStream>()
52+
{
53+
#[allow(unused_mut)] // In case the expansion is empty
54+
let mut ts = TokenStream::new();
55+
$(ToTokens::to_tokens(&minimal_quote_ts!($t), &mut ts);)*
56+
ts
57+
}
5558
};
5659
}
5760

@@ -62,52 +65,66 @@ macro_rules! quote {
6265
#[unstable(feature = "proc_macro_quote", issue = "54722")]
6366
pub fn quote(stream: TokenStream) -> TokenStream {
6467
if stream.is_empty() {
65-
return quote!(crate::TokenStream::new());
68+
return minimal_quote!(crate::TokenStream::new());
6669
}
67-
let proc_macro_crate = quote!(crate);
70+
let proc_macro_crate = minimal_quote!(crate);
6871
let mut after_dollar = false;
69-
let tokens = stream
70-
.into_iter()
71-
.filter_map(|tree| {
72-
if after_dollar {
73-
after_dollar = false;
74-
match tree {
75-
TokenTree::Ident(_) => {
76-
return Some(quote!(Into::<crate::TokenStream>::into(
77-
Clone::clone(&(@ tree))),));
78-
}
79-
TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
80-
_ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
81-
}
82-
} else if let TokenTree::Punct(ref tt) = tree {
83-
if tt.as_char() == '$' {
84-
after_dollar = true;
85-
return None;
72+
73+
let mut tokens = crate::TokenStream::new();
74+
for tree in stream {
75+
if after_dollar {
76+
after_dollar = false;
77+
match tree {
78+
TokenTree::Ident(_) => {
79+
minimal_quote!(crate::ToTokens::to_tokens(&(@ tree), &mut ts);)
80+
.to_tokens(&mut tokens);
81+
continue;
8682
}
83+
TokenTree::Punct(ref tt) if tt.as_char() == '$' => {}
84+
_ => panic!("`$` must be followed by an ident or `$` in `quote!`"),
8785
}
86+
} else if let TokenTree::Punct(ref tt) = tree {
87+
if tt.as_char() == '$' {
88+
after_dollar = true;
89+
continue;
90+
}
91+
}
8892

89-
Some(quote!(crate::TokenStream::from((@ match tree {
90-
TokenTree::Punct(tt) => quote!(crate::TokenTree::Punct(crate::Punct::new(
93+
match tree {
94+
TokenTree::Punct(tt) => {
95+
minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Punct(crate::Punct::new(
9196
(@ TokenTree::from(Literal::character(tt.as_char()))),
9297
(@ match tt.spacing() {
93-
Spacing::Alone => quote!(crate::Spacing::Alone),
94-
Spacing::Joint => quote!(crate::Spacing::Joint),
98+
Spacing::Alone => minimal_quote!(crate::Spacing::Alone),
99+
Spacing::Joint => minimal_quote!(crate::Spacing::Joint),
95100
}),
96-
))),
97-
TokenTree::Group(tt) => quote!(crate::TokenTree::Group(crate::Group::new(
101+
)), &mut ts);)
102+
}
103+
TokenTree::Group(tt) => {
104+
minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Group(crate::Group::new(
98105
(@ match tt.delimiter() {
99-
Delimiter::Parenthesis => quote!(crate::Delimiter::Parenthesis),
100-
Delimiter::Brace => quote!(crate::Delimiter::Brace),
101-
Delimiter::Bracket => quote!(crate::Delimiter::Bracket),
102-
Delimiter::None => quote!(crate::Delimiter::None),
106+
Delimiter::Parenthesis => minimal_quote!(crate::Delimiter::Parenthesis),
107+
Delimiter::Brace => minimal_quote!(crate::Delimiter::Brace),
108+
Delimiter::Bracket => minimal_quote!(crate::Delimiter::Bracket),
109+
Delimiter::None => minimal_quote!(crate::Delimiter::None),
103110
}),
104111
(@ quote(tt.stream())),
105-
))),
106-
TokenTree::Ident(tt) => quote!(crate::TokenTree::Ident(crate::Ident::new(
107-
(@ TokenTree::from(Literal::string(&tt.to_string()))),
112+
)), &mut ts);)
113+
}
114+
TokenTree::Ident(tt) => {
115+
let literal = tt.to_string();
116+
let (literal, ctor) = if let Some(stripped) = literal.strip_prefix("r#") {
117+
(stripped, minimal_quote!(crate::Ident::new_raw))
118+
} else {
119+
(literal.as_str(), minimal_quote!(crate::Ident::new))
120+
};
121+
minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Ident((@ ctor)(
122+
(@ TokenTree::from(Literal::string(literal))),
108123
(@ quote_span(proc_macro_crate.clone(), tt.span())),
109-
))),
110-
TokenTree::Literal(tt) => quote!(crate::TokenTree::Literal({
124+
)), &mut ts);)
125+
}
126+
TokenTree::Literal(tt) => {
127+
minimal_quote!(crate::ToTokens::to_tokens(&crate::TokenTree::Literal({
111128
let mut iter = (@ TokenTree::from(Literal::string(&tt.to_string())))
112129
.parse::<crate::TokenStream>()
113130
.unwrap()
@@ -120,22 +137,28 @@ pub fn quote(stream: TokenStream) -> TokenStream {
120137
} else {
121138
unreachable!()
122139
}
123-
}))
124-
})),))
125-
})
126-
.collect::<TokenStream>();
127-
140+
}), &mut ts);)
141+
}
142+
}
143+
.to_tokens(&mut tokens);
144+
}
128145
if after_dollar {
129146
panic!("unexpected trailing `$` in `quote!`");
130147
}
131148

132-
quote!([(@ tokens)].iter().cloned().collect::<crate::TokenStream>())
149+
minimal_quote! {
150+
{
151+
let mut ts = crate::TokenStream::new();
152+
(@ tokens)
153+
ts
154+
}
155+
}
133156
}
134157

135158
/// Quote a `Span` into a `TokenStream`.
136159
/// This is needed to implement a custom quoter.
137160
#[unstable(feature = "proc_macro_quote", issue = "54722")]
138161
pub fn quote_span(proc_macro_crate: TokenStream, span: Span) -> TokenStream {
139162
let id = span.save_span();
140-
quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
163+
minimal_quote!((@ proc_macro_crate ) ::Span::recover_proc_macro_span((@ TokenTree::from(Literal::usize_unsuffixed(id)))))
141164
}

tests/ui/proc-macro/quote-debug.stdout

-49
This file was deleted.

0 commit comments

Comments
 (0)