Skip to content

Commit 5a87288

Browse files
committed
Auto merge of #29780 - KyleMayes:quote-ext, r=nrc
This is my first code contribution to Rust, so I'm sure there are some issues with the changes I've made. I've added the `quote_arg!`, `quote_block!`, `quote_path!`, and `quote_meta_item!` quasiquoting macros. From my experience trying to build AST in compiler plugins, I would like to be able to build any AST piece with a quasiquoting macro (e.g., `quote_struct_field!` or `quote_variant!`) and then use those AST pieces in other quasiquoting macros, but this pull request just adds some of the low-hanging fruit. I'm not sure if these additions are desirable, and I'm sure these macros can be implemented in an external crate if not.
2 parents 15e7824 + 8c88308 commit 5a87288

File tree

8 files changed

+137
-11
lines changed

8 files changed

+137
-11
lines changed

src/libsyntax/ext/base.rs

+12
Original file line numberDiff line numberDiff line change
@@ -512,6 +512,18 @@ fn initial_syntax_expander_table<'feat>(ecfg: &expand::ExpansionConfig<'feat>)
512512
syntax_expanders.insert(intern("quote_attr"),
513513
builtin_normal_expander(
514514
ext::quote::expand_quote_attr));
515+
syntax_expanders.insert(intern("quote_arg"),
516+
builtin_normal_expander(
517+
ext::quote::expand_quote_arg));
518+
syntax_expanders.insert(intern("quote_block"),
519+
builtin_normal_expander(
520+
ext::quote::expand_quote_block));
521+
syntax_expanders.insert(intern("quote_meta_item"),
522+
builtin_normal_expander(
523+
ext::quote::expand_quote_meta_item));
524+
syntax_expanders.insert(intern("quote_path"),
525+
builtin_normal_expander(
526+
ext::quote::expand_quote_path));
515527
}
516528

517529
syntax_expanders.insert(intern("line"),

src/libsyntax/ext/quote.rs

+51
Original file line numberDiff line numberDiff line change
@@ -158,6 +158,18 @@ pub mod rt {
158158
}
159159
}
160160

161+
impl ToTokens for ast::Arg {
162+
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
163+
vec![TokenTree::Token(DUMMY_SP, token::Interpolated(token::NtArg(self.clone())))]
164+
}
165+
}
166+
167+
impl ToTokens for P<ast::Block> {
168+
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
169+
vec![TokenTree::Token(DUMMY_SP, token::Interpolated(token::NtBlock(self.clone())))]
170+
}
171+
}
172+
161173
macro_rules! impl_to_tokens_slice {
162174
($t: ty, $sep: expr) => {
163175
impl ToTokens for [$t] {
@@ -177,6 +189,7 @@ pub mod rt {
177189

178190
impl_to_tokens_slice! { ast::Ty, [TokenTree::Token(DUMMY_SP, token::Comma)] }
179191
impl_to_tokens_slice! { P<ast::Item>, [] }
192+
impl_to_tokens_slice! { ast::Arg, [TokenTree::Token(DUMMY_SP, token::Comma)] }
180193

181194
impl ToTokens for P<ast::MetaItem> {
182195
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
@@ -383,6 +396,39 @@ pub fn expand_quote_attr(cx: &mut ExtCtxt,
383396
base::MacEager::expr(expanded)
384397
}
385398

399+
pub fn expand_quote_arg(cx: &mut ExtCtxt,
400+
sp: Span,
401+
tts: &[TokenTree])
402+
-> Box<base::MacResult+'static> {
403+
let expanded = expand_parse_call(cx, sp, "parse_arg_panic", vec!(), tts);
404+
base::MacEager::expr(expanded)
405+
}
406+
407+
pub fn expand_quote_block(cx: &mut ExtCtxt,
408+
sp: Span,
409+
tts: &[TokenTree])
410+
-> Box<base::MacResult+'static> {
411+
let expanded = expand_parse_call(cx, sp, "parse_block_panic", vec!(), tts);
412+
base::MacEager::expr(expanded)
413+
}
414+
415+
pub fn expand_quote_meta_item(cx: &mut ExtCtxt,
416+
sp: Span,
417+
tts: &[TokenTree])
418+
-> Box<base::MacResult+'static> {
419+
let expanded = expand_parse_call(cx, sp, "parse_meta_item_panic", vec!(), tts);
420+
base::MacEager::expr(expanded)
421+
}
422+
423+
pub fn expand_quote_path(cx: &mut ExtCtxt,
424+
sp: Span,
425+
tts: &[TokenTree])
426+
-> Box<base::MacResult+'static> {
427+
let mode = mk_parser_path(cx, sp, "LifetimeAndTypesWithoutColons");
428+
let expanded = expand_parse_call(cx, sp, "parse_path_panic", vec!(mode), tts);
429+
base::MacEager::expr(expanded)
430+
}
431+
386432
pub fn expand_quote_matcher(cx: &mut ExtCtxt,
387433
sp: Span,
388434
tts: &[TokenTree])
@@ -440,6 +486,11 @@ fn mk_token_path(cx: &ExtCtxt, sp: Span, name: &str) -> P<ast::Expr> {
440486
cx.expr_path(cx.path_global(sp, idents))
441487
}
442488

489+
fn mk_parser_path(cx: &ExtCtxt, sp: Span, name: &str) -> P<ast::Expr> {
490+
let idents = vec!(id_ext("syntax"), id_ext("parse"), id_ext("parser"), id_ext(name));
491+
cx.expr_path(cx.path_global(sp, idents))
492+
}
493+
443494
fn mk_binop(cx: &ExtCtxt, sp: Span, bop: token::BinOpToken) -> P<ast::Expr> {
444495
let name = match bop {
445496
token::Plus => "Plus",

src/libsyntax/fold.rs

+1
Original file line numberDiff line numberDiff line change
@@ -684,6 +684,7 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
684684
token::NtGenerics(generics) => token::NtGenerics(fld.fold_generics(generics)),
685685
token::NtWhereClause(where_clause) =>
686686
token::NtWhereClause(fld.fold_where_clause(where_clause)),
687+
token::NtArg(arg) => token::NtArg(fld.fold_arg(arg)),
687688
}
688689
}
689690

src/libsyntax/parse/parser.rs

+18
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,22 @@ impl<'a> Parser<'a> {
393393
panictry!(self.parse_attribute(permit_inner))
394394
}
395395

396+
pub fn parse_arg_panic(&mut self) -> Arg {
397+
panictry!(self.parse_arg())
398+
}
399+
400+
pub fn parse_block_panic(&mut self) -> P<Block> {
401+
panictry!(self.parse_block())
402+
}
403+
404+
pub fn parse_meta_item_panic(&mut self) -> P<ast::MetaItem> {
405+
panictry!(self.parse_meta_item())
406+
}
407+
408+
pub fn parse_path_panic(&mut self, mode: PathParsingMode) -> ast::Path {
409+
panictry!(self.parse_path(mode))
410+
}
411+
396412
/// Convert a token to a string using self's reader
397413
pub fn token_to_string(token: &token::Token) -> String {
398414
pprust::token_to_string(token)
@@ -1455,6 +1471,8 @@ impl<'a> Parser<'a> {
14551471
/// This version of parse arg doesn't necessarily require
14561472
/// identifier names.
14571473
pub fn parse_arg_general(&mut self, require_name: bool) -> PResult<Arg> {
1474+
maybe_whole!(no_clone self, NtArg);
1475+
14581476
let pat = if require_name || self.is_named_argument() {
14591477
debug!("parse_arg_general parse_pat (require_name:{})",
14601478
require_name);

src/libsyntax/parse/token.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -381,12 +381,13 @@ pub enum Nonterminal {
381381
NtMeta(P<ast::MetaItem>),
382382
NtPath(Box<ast::Path>),
383383
NtTT(P<ast::TokenTree>), // needs P'ed to break a circularity
384-
// These is not exposed to macros, but is used by quasiquote.
384+
// These are not exposed to macros, but are used by quasiquote.
385385
NtArm(ast::Arm),
386386
NtImplItem(P<ast::ImplItem>),
387387
NtTraitItem(P<ast::TraitItem>),
388388
NtGenerics(ast::Generics),
389389
NtWhereClause(ast::WhereClause),
390+
NtArg(ast::Arg),
390391
}
391392

392393
impl fmt::Debug for Nonterminal {
@@ -407,6 +408,7 @@ impl fmt::Debug for Nonterminal {
407408
NtTraitItem(..) => f.pad("NtTraitItem(..)"),
408409
NtGenerics(..) => f.pad("NtGenerics(..)"),
409410
NtWhereClause(..) => f.pad("NtWhereClause(..)"),
411+
NtArg(..) => f.pad("NtArg(..)"),
410412
}
411413
}
412414
}

src/libsyntax/print/pprust.rs

+1
Original file line numberDiff line numberDiff line change
@@ -305,6 +305,7 @@ pub fn token_to_string(tok: &Token) -> String {
305305
token::NtTraitItem(ref e) => trait_item_to_string(&**e),
306306
token::NtGenerics(ref e) => generics_to_string(&*e),
307307
token::NtWhereClause(ref e) => where_clause_to_string(&*e),
308+
token::NtArg(ref e) => arg_to_string(&*e),
308309
}
309310
}
310311
}

src/test/compile-fail-fulldeps/gated-quote.rs

+14-10
Original file line numberDiff line numberDiff line change
@@ -37,14 +37,18 @@ impl ParseSess {
3737

3838
pub fn main() {
3939
let ecx = &ParseSess;
40-
let x = quote_tokens!(ecx, 3); //~ ERROR macro undefined: 'quote_tokens!'
41-
let x = quote_expr!(ecx, 3); //~ ERROR macro undefined: 'quote_expr!'
42-
let x = quote_ty!(ecx, 3); //~ ERROR macro undefined: 'quote_ty!'
43-
let x = quote_method!(ecx, 3); //~ ERROR macro undefined: 'quote_method!'
44-
let x = quote_item!(ecx, 3); //~ ERROR macro undefined: 'quote_item!'
45-
let x = quote_pat!(ecx, 3); //~ ERROR macro undefined: 'quote_pat!'
46-
let x = quote_arm!(ecx, 3); //~ ERROR macro undefined: 'quote_arm!'
47-
let x = quote_stmt!(ecx, 3); //~ ERROR macro undefined: 'quote_stmt!'
48-
let x = quote_matcher!(ecx, 3); //~ ERROR macro undefined: 'quote_matcher!'
49-
let x = quote_attr!(ecx, 3); //~ ERROR macro undefined: 'quote_attr!'
40+
let x = quote_tokens!(ecx, 3); //~ ERROR macro undefined: 'quote_tokens!'
41+
let x = quote_expr!(ecx, 3); //~ ERROR macro undefined: 'quote_expr!'
42+
let x = quote_ty!(ecx, 3); //~ ERROR macro undefined: 'quote_ty!'
43+
let x = quote_method!(ecx, 3); //~ ERROR macro undefined: 'quote_method!'
44+
let x = quote_item!(ecx, 3); //~ ERROR macro undefined: 'quote_item!'
45+
let x = quote_pat!(ecx, 3); //~ ERROR macro undefined: 'quote_pat!'
46+
let x = quote_arm!(ecx, 3); //~ ERROR macro undefined: 'quote_arm!'
47+
let x = quote_stmt!(ecx, 3); //~ ERROR macro undefined: 'quote_stmt!'
48+
let x = quote_matcher!(ecx, 3); //~ ERROR macro undefined: 'quote_matcher!'
49+
let x = quote_attr!(ecx, 3); //~ ERROR macro undefined: 'quote_attr!'
50+
let x = quote_arg!(ecx, 3); //~ ERROR macro undefined: 'quote_arg!'
51+
let x = quote_block!(ecx, 3); //~ ERROR macro undefined: 'quote_block!'
52+
let x = quote_meta_item!(ecx, 3); //~ ERROR macro undefined: 'quote_meta_item!'
53+
let x = quote_path!(ecx, 3); //~ ERROR macro undefined: 'quote_path!'
5054
}

src/test/run-pass-fulldeps/qquote.rs

+37
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,41 @@ fn main() {
6363

6464
let attr = quote_attr!(cx, #![cfg(foo = "bar")]);
6565
check!(attribute_to_string, attr, quote_attr!(cx, $attr); r#"#![cfg(foo = "bar")]"#);
66+
67+
// quote_arg!
68+
69+
let arg = quote_arg!(cx, foo: i32);
70+
check!(arg_to_string, arg, quote_arg!(cx, $arg); "foo: i32");
71+
72+
let function = quote_item!(cx, fn f($arg) { }).unwrap();
73+
check!(item_to_string, function; "fn f(foo: i32) { }");
74+
75+
let args = vec![arg, quote_arg!(cx, bar: u32)];
76+
let args = &args[..];
77+
let function = quote_item!(cx, fn f($args) { }).unwrap();
78+
check!(item_to_string, function; "fn f(foo: i32, bar: u32) { }");
79+
80+
// quote_block!
81+
82+
let block = quote_block!(cx, { $stmt let y = 40u32; });
83+
check!(block_to_string, block, *quote_block!(cx, $block); "{ let x = 20u16; let y = 40u32; }");
84+
85+
let function = quote_item!(cx, fn f() $block).unwrap();
86+
check!(item_to_string, function; "fn f() { let x = 20u16; let y = 40u32; }");
87+
88+
// quote_path!
89+
90+
let path = quote_path!(cx, ::syntax::ptr::P<MetaItem>);
91+
check!(path_to_string, path, quote_path!(cx, $path); "::syntax::ptr::P<MetaItem>");
92+
93+
let ty = quote_ty!(cx, $path);
94+
check!(ty_to_string, ty; "::syntax::ptr::P<MetaItem>");
95+
96+
// quote_meta_item!
97+
98+
let meta = quote_meta_item!(cx, cfg(foo = "bar"));
99+
check!(meta_item_to_string, meta, *quote_meta_item!(cx, $meta); r#"cfg(foo = "bar")"#);
100+
101+
let attr = quote_attr!(cx, #![$meta]);
102+
check!(attribute_to_string, attr; r#"#![cfg(foo = "bar")]"#);
66103
}

0 commit comments

Comments
 (0)