Skip to content

Commit 34fa27b

Browse files
authored
Rollup merge of #134693 - SpriteOvO:proc-macro-use-to-tokens-in-quote, r=tgross35
proc_macro: Use `ToTokens` trait in `quote` macro Tracking issues: #130977, #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

Lines changed: 691 additions & 113 deletions

‎library/proc_macro/src/lib.rs‎

Lines changed: 1 addition & 1 deletion
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‎

Lines changed: 86 additions & 63 deletions
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-
usecrate::{Delimiter,Group,Ident,Literal,Punct,Spacing,Span,TokenStream,TokenTree};
7+
usecrate::{
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+
letmut 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+
letmut 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
pubfnquote(stream:TokenStream) -> TokenStream{
6467
if stream.is_empty(){
65-
returnquote!(crate::TokenStream::new());
68+
returnminimal_quote!(crate::TokenStream::new());
6669
}
67-
let proc_macro_crate = quote!(crate);
70+
let proc_macro_crate = minimal_quote!(crate);
6871
letmut 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-
returnSome(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-
}elseifletTokenTree::Punct(ref tt) = tree {
83-
if tt.as_char() == '$'{
84-
after_dollar = true;
85-
returnNone;
72+
73+
letmut 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+
}elseifletTokenTree::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) = ifletSome(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
letmut 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+
letmut 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
pubfnquote_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‎

Lines changed: 0 additions & 49 deletions
This file was deleted.

0 commit comments

Comments
 (0)