Skip to content

Commit 32da851

Browse files
committed
Auto merge of #39087 - nrc:qquote-empty-delim, r=jseyfried
proc macros/qquote: Handle empty delimited tokens r? @jseyfried
2 parents 4ce7acc + 0541997 commit 32da851

File tree

4 files changed

+113
-40
lines changed

4 files changed

+113
-40
lines changed

src/libproc_macro_plugin/qquote.rs

+43-40
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,14 @@ pub fn qquote<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree])
6060
struct QDelimited {
6161
delim: token::DelimToken,
6262
open_span: Span,
63-
tts: Vec<QTT>,
63+
tts: Vec<Qtt>,
6464
close_span: Span,
6565
}
6666

6767
#[derive(Debug)]
68-
enum QTT {
68+
enum Qtt {
6969
TT(TokenTree),
70-
QDL(QDelimited),
70+
Delimited(QDelimited),
7171
QIdent(TokenTree),
7272
}
7373

@@ -103,10 +103,10 @@ fn qquoter<'cx>(cx: &'cx mut ExtCtxt, ts: TokenStream) -> TokenStream {
103103
}
104104
}
105105

106-
fn qquote_iter<'cx>(cx: &'cx mut ExtCtxt, depth: i64, ts: TokenStream) -> (Bindings, Vec<QTT>) {
106+
fn qquote_iter<'cx>(cx: &'cx mut ExtCtxt, depth: i64, ts: TokenStream) -> (Bindings, Vec<Qtt>) {
107107
let mut depth = depth;
108108
let mut bindings: Bindings = Vec::new();
109-
let mut output: Vec<QTT> = Vec::new();
109+
let mut output: Vec<Qtt> = Vec::new();
110110

111111
let mut iter = ts.iter();
112112

@@ -133,32 +133,32 @@ fn qquote_iter<'cx>(cx: &'cx mut ExtCtxt, depth: i64, ts: TokenStream) -> (Bindi
133133
for b in bindings.clone() {
134134
debug!("{:?} = {}", b.0, pprust::tts_to_string(&b.1.to_tts()[..]));
135135
}
136-
output.push(QTT::QIdent(as_tt(Token::Ident(new_id.clone()))));
136+
output.push(Qtt::QIdent(as_tt(Token::Ident(new_id.clone()))));
137137
} else {
138138
depth = depth - 1;
139-
output.push(QTT::TT(next.clone()));
139+
output.push(Qtt::TT(next.clone()));
140140
}
141141
}
142142
TokenTree::Token(_, Token::Ident(id)) if is_qquote(id) => {
143143
depth = depth + 1;
144144
}
145145
TokenTree::Delimited(_, ref dl) => {
146146
let br = qquote_iter(cx, depth, TokenStream::from_tts(dl.tts.clone().to_owned()));
147-
let mut bind_ = br.0;
148-
let res_ = br.1;
149-
bindings.append(&mut bind_);
147+
let mut nested_bindings = br.0;
148+
let nested = br.1;
149+
bindings.append(&mut nested_bindings);
150150

151151
let new_dl = QDelimited {
152152
delim: dl.delim,
153153
open_span: dl.open_span,
154-
tts: res_,
154+
tts: nested,
155155
close_span: dl.close_span,
156156
};
157157

158-
output.push(QTT::QDL(new_dl));
158+
output.push(Qtt::Delimited(new_dl));
159159
}
160160
t => {
161-
output.push(QTT::TT(t));
161+
output.push(Qtt::TT(t));
162162
}
163163
}
164164
}
@@ -188,9 +188,9 @@ fn unravel_concats(tss: Vec<TokenStream>) -> TokenStream {
188188
output
189189
}
190190

191-
/// This converts the vector of QTTs into a seet of Bindings for construction and the main
191+
/// This converts the vector of Qtts into a set of Bindings for construction and the main
192192
/// body as a TokenStream.
193-
fn convert_complex_tts<'cx>(cx: &'cx mut ExtCtxt, tts: Vec<QTT>) -> (Bindings, TokenStream) {
193+
fn convert_complex_tts<'cx>(cx: &'cx mut ExtCtxt, tts: Vec<Qtt>) -> (Bindings, TokenStream) {
194194
let mut pushes: Vec<TokenStream> = Vec::new();
195195
let mut bindings: Bindings = Vec::new();
196196

@@ -203,28 +203,37 @@ fn convert_complex_tts<'cx>(cx: &'cx mut ExtCtxt, tts: Vec<QTT>) -> (Bindings, T
203203
}
204204
let next = next.unwrap();
205205
match next {
206-
QTT::TT(TokenTree::Token(_, t)) => {
206+
Qtt::TT(TokenTree::Token(_, t)) => {
207207
let token_out = emit_token(t);
208208
pushes.push(token_out);
209209
}
210210
// FIXME handle sequence repetition tokens
211-
QTT::QDL(qdl) => {
212-
debug!(" QDL: {:?} ", qdl.tts);
213-
let new_id = Ident::with_empty_ctxt(Symbol::gensym("qdl_tmp"));
214-
let mut cct_rec = convert_complex_tts(cx, qdl.tts);
215-
bindings.append(&mut cct_rec.0);
216-
bindings.push((new_id, cct_rec.1));
217-
218-
let sep = build_delim_tok(qdl.delim);
219-
220-
pushes.push(build_mod_call(
221-
vec![Ident::from_str("proc_macro_tokens"),
222-
Ident::from_str("build"),
223-
Ident::from_str("build_delimited")],
224-
concat(from_tokens(vec![Token::Ident(new_id)]), concat(lex(","), sep)),
225-
));
211+
Qtt::Delimited(qdl) => {
212+
debug!(" Delimited: {:?} ", qdl.tts);
213+
let fresh_id = Ident::with_empty_ctxt(Symbol::gensym("qdl_tmp"));
214+
let (mut nested_bindings, nested_toks) = convert_complex_tts(cx, qdl.tts);
215+
216+
let body = if nested_toks.is_empty() {
217+
assert!(nested_bindings.is_empty());
218+
build_mod_call(vec![Ident::from_str("TokenStream"),
219+
Ident::from_str("mk_empty")],
220+
TokenStream::mk_empty())
221+
} else {
222+
bindings.append(&mut nested_bindings);
223+
bindings.push((fresh_id, nested_toks));
224+
TokenStream::from_tokens(vec![Token::Ident(fresh_id)])
225+
};
226+
227+
let delimitiers = build_delim_tok(qdl.delim);
228+
229+
pushes.push(build_mod_call(vec![Ident::from_str("proc_macro_tokens"),
230+
Ident::from_str("build"),
231+
Ident::from_str("build_delimited")],
232+
flatten(vec![body,
233+
lex(","),
234+
delimitiers].into_iter())));
226235
}
227-
QTT::QIdent(t) => {
236+
Qtt::QIdent(t) => {
228237
pushes.push(TokenStream::from_tts(vec![t]));
229238
pushes.push(TokenStream::mk_empty());
230239
}
@@ -240,14 +249,8 @@ fn convert_complex_tts<'cx>(cx: &'cx mut ExtCtxt, tts: Vec<QTT>) -> (Bindings, T
240249
// Utilities
241250

242251
/// Unravels Bindings into a TokenStream of `let` declarations.
243-
fn unravel(binds: Bindings) -> TokenStream {
244-
let mut output = TokenStream::mk_empty();
245-
246-
for b in binds {
247-
output = concat(output, build_let(b.0, b.1));
248-
}
249-
250-
output
252+
fn unravel(bindings: Bindings) -> TokenStream {
253+
flatten(bindings.into_iter().map(|(a, b)| build_let(a, b)))
251254
}
252255

253256
/// Checks if the Ident is `unquote`.

src/libproc_macro_tokens/build.rs

+13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ pub fn concat(ts1: TokenStream, ts2: TokenStream) -> TokenStream {
2424
TokenStream::concat(ts1, ts2)
2525
}
2626

27+
/// Flatten a sequence of TokenStreams into a single TokenStream.
28+
pub fn flatten<T: Iterator<Item=TokenStream>>(mut iter: T) -> TokenStream {
29+
match iter.next() {
30+
Some(mut ts) => {
31+
for next in iter {
32+
ts = TokenStream::concat(ts, next);
33+
}
34+
ts
35+
}
36+
None => TokenStream::mk_empty()
37+
}
38+
}
39+
2740
/// Checks if two identifiers have the same name, disregarding context. This allows us to
2841
/// fake 'reserved' keywords.
2942
// FIXME We really want `free-identifier-=?` (a la Dybvig 1993). von Tander 2007 is
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// Copyright 2017 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.
4+
//
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.
10+
11+
#![feature(plugin)]
12+
#![feature(plugin_registrar)]
13+
#![feature(rustc_private)]
14+
#![plugin(proc_macro_plugin)]
15+
16+
extern crate rustc_plugin;
17+
extern crate proc_macro_tokens;
18+
extern crate syntax;
19+
20+
use syntax::ext::proc_macro_shim::prelude::*;
21+
use proc_macro_tokens::prelude::*;
22+
23+
use rustc_plugin::Registry;
24+
25+
#[plugin_registrar]
26+
pub fn plugin_registrar(reg: &mut Registry) {
27+
reg.register_macro("hello", hello);
28+
}
29+
30+
// This macro is not very interesting, but it does contain delimited tokens with
31+
// no content - `()` and `{}` - which has caused problems in the past.
32+
fn hello<'cx>(cx: &'cx mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult + 'cx> {
33+
let output = qquote!({ fn hello() {} hello(); });
34+
build_block_emitter(cx, sp, output)
35+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2012-2014 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.
4+
//
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.
10+
11+
// Test that a macro can emit delimiters with nothing inside - `()`, `{}`
12+
13+
// aux-build:hello_macro.rs
14+
// ignore-stage1
15+
16+
#![feature(plugin)]
17+
#![feature(rustc_private)]
18+
#![plugin(hello_macro)]
19+
20+
fn main() {
21+
hello!();
22+
}

0 commit comments

Comments
 (0)