Auto merge of #23085 - goffrie:interpolating-quote, r=huonw

This changes the `ToTokens` implementations for expressions, statements, etc. with almost-trivial ones that produce `Interpolated(*Nt(...))` pseudo-tokens. In this way, quasiquote now works the same way as macros do: already-parsed AST fragments are used as-is, not reparsed.

The `ToSource` trait is removed. Quasiquote no longer involves pretty-printing at all, which removes the need for the `encode_with_hygiene` hack. All associated machinery is removed.

New `Nonterminal`s are added: NtArm, NtImplItem, and NtTraitItem. These are just for quasiquote, not macros.

`ToTokens` is no longer implemented for `Arg` (although this could be added again) and `Generics` (which I don't think makes sense).

This breaks any compiler extensions that relied on the ability of `ToTokens` to turn AST fragments back into inspectable token trees. For this reason, this closes #16987.

As such, this is a [breaking-change].

Fixes #16472.
Fixes #15962.
Fixes #17397.
Fixes #16617.
This commit is contained in:
bors 2015-04-26 09:52:28 +00:00
commit 6365080c5c
15 changed files with 276 additions and 744 deletions

View File

@ -89,12 +89,6 @@ pub fn new(name: Name) -> Ident { Ident {name: name, ctxt: EMPTY_CTXT}}
pub fn as_str<'a>(&'a self) -> &'a str {
self.name.as_str()
}
pub fn encode_with_hygiene(&self) -> String {
format!("\x00name_{},ctxt_{}\x00",
self.name.usize(),
self.ctxt)
}
}
impl fmt::Debug for Ident {

View File

@ -30,16 +30,16 @@ pub mod rt {
use ext::base::ExtCtxt;
use parse::token;
use parse;
use print::pprust;
use ptr::P;
use std::rc::Rc;
use ast::{TokenTree, Generics, Expr};
use ast::{TokenTree, Expr};
pub use parse::new_parser_from_tts;
pub use codemap::{BytePos, Span, dummy_spanned};
pub use codemap::{BytePos, Span, dummy_spanned, DUMMY_SP};
pub trait ToTokens {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> ;
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree>;
}
impl ToTokens for TokenTree {
@ -70,277 +70,189 @@ fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
}
}
/* Should be (when bugs in default methods are fixed):
trait ToSource : ToTokens {
// Takes a thing and generates a string containing rust code for it.
pub fn to_source() -> String;
// If you can make source, you can definitely make tokens.
pub fn to_tokens(cx: &ExtCtxt) -> ~[TokenTree] {
cx.parse_tts(self.to_source())
impl ToTokens for ast::Ident {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(DUMMY_SP, token::Ident(*self, token::Plain))]
}
}
*/
// FIXME: Move this trait to pprust and get rid of *_to_str?
pub trait ToSource {
// Takes a thing and generates a string containing rust code for it.
fn to_source(&self) -> String;
impl ToTokens for ast::Path {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(DUMMY_SP, token::Interpolated(token::NtPath(Box::new(self.clone()))))]
}
}
// FIXME (Issue #16472): This should go away after ToToken impls
// are revised to go directly to token-trees.
trait ToSourceWithHygiene : ToSource {
// Takes a thing and generates a string containing rust code
// for it, encoding Idents as special byte sequences to
// maintain hygiene across serialization and deserialization.
fn to_source_with_hygiene(&self) -> String;
impl ToTokens for ast::Ty {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(self.span, token::Interpolated(token::NtTy(P(self.clone()))))]
}
}
macro_rules! impl_to_source {
(P<$t:ty>, $pp:ident) => (
impl ToSource for P<$t> {
fn to_source(&self) -> String {
pprust::$pp(&**self)
impl ToTokens for ast::Block {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(self.span, token::Interpolated(token::NtBlock(P(self.clone()))))]
}
}
impl ToTokens for P<ast::Item> {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(self.span, token::Interpolated(token::NtItem(self.clone())))]
}
}
impl ToTokens for P<ast::ImplItem> {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(self.span, token::Interpolated(token::NtImplItem(self.clone())))]
}
}
impl ToTokens for P<ast::TraitItem> {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(self.span, token::Interpolated(token::NtTraitItem(self.clone())))]
}
}
impl ToTokens for P<ast::Stmt> {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(self.span, token::Interpolated(token::NtStmt(self.clone())))]
}
}
impl ToTokens for P<ast::Expr> {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(self.span, token::Interpolated(token::NtExpr(self.clone())))]
}
}
impl ToTokens for P<ast::Pat> {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(self.span, token::Interpolated(token::NtPat(self.clone())))]
}
}
impl ToTokens for ast::Arm {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(DUMMY_SP, token::Interpolated(token::NtArm(self.clone())))]
}
}
macro_rules! impl_to_tokens_slice {
($t: ty, $sep: expr) => {
impl ToTokens for [$t] {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
let mut v = vec![];
for (i, x) in self.iter().enumerate() {
if i > 0 {
v.push_all(&$sep);
}
v.extend(x.to_tokens(cx));
}
v
}
}
impl ToSourceWithHygiene for P<$t> {
fn to_source_with_hygiene(&self) -> String {
pprust::with_hygiene::$pp(&**self)
}
};
}
impl_to_tokens_slice! { ast::Ty, [ast::TtToken(DUMMY_SP, token::Comma)] }
impl_to_tokens_slice! { P<ast::Item>, [] }
impl ToTokens for P<ast::MetaItem> {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtToken(DUMMY_SP, token::Interpolated(token::NtMeta(self.clone())))]
}
}
impl ToTokens for ast::Attribute {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
let mut r = vec![];
// FIXME: The spans could be better
r.push(ast::TtToken(self.span, token::Pound));
if self.node.style == ast::AttrInner {
r.push(ast::TtToken(self.span, token::Not));
}
);
($t:ty, $pp:ident) => (
impl ToSource for $t {
fn to_source(&self) -> String {
pprust::$pp(self)
}
}
impl ToSourceWithHygiene for $t {
fn to_source_with_hygiene(&self) -> String {
pprust::with_hygiene::$pp(self)
}
}
);
}
fn slice_to_source<'a, T: ToSource>(sep: &'static str, xs: &'a [T]) -> String {
xs.iter()
.map(|i| i.to_source())
.collect::<Vec<String>>()
.connect(sep)
.to_string()
}
fn slice_to_source_with_hygiene<'a, T: ToSourceWithHygiene>(
sep: &'static str, xs: &'a [T]) -> String {
xs.iter()
.map(|i| i.to_source_with_hygiene())
.collect::<Vec<String>>()
.connect(sep)
.to_string()
}
macro_rules! impl_to_source_slice {
($t:ty, $sep:expr) => (
impl ToSource for [$t] {
fn to_source(&self) -> String {
slice_to_source($sep, self)
}
}
impl ToSourceWithHygiene for [$t] {
fn to_source_with_hygiene(&self) -> String {
slice_to_source_with_hygiene($sep, self)
}
}
)
}
impl ToSource for ast::Ident {
fn to_source(&self) -> String {
token::get_ident(*self).to_string()
r.push(ast::TtDelimited(self.span, Rc::new(ast::Delimited {
delim: token::Bracket,
open_span: self.span,
tts: self.node.value.to_tokens(cx),
close_span: self.span,
})));
r
}
}
impl ToSourceWithHygiene for ast::Ident {
fn to_source_with_hygiene(&self) -> String {
self.encode_with_hygiene()
impl ToTokens for str {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
let lit = ast::LitStr(
token::intern_and_get_ident(self), ast::CookedStr);
dummy_spanned(lit).to_tokens(cx)
}
}
impl_to_source! { ast::Path, path_to_string }
impl_to_source! { ast::Ty, ty_to_string }
impl_to_source! { ast::Block, block_to_string }
impl_to_source! { ast::Arg, arg_to_string }
impl_to_source! { Generics, generics_to_string }
impl_to_source! { ast::WhereClause, where_clause_to_string }
impl_to_source! { P<ast::Item>, item_to_string }
impl_to_source! { P<ast::ImplItem>, impl_item_to_string }
impl_to_source! { P<ast::TraitItem>, trait_item_to_string }
impl_to_source! { P<ast::Stmt>, stmt_to_string }
impl_to_source! { P<ast::Expr>, expr_to_string }
impl_to_source! { P<ast::Pat>, pat_to_string }
impl_to_source! { ast::Arm, arm_to_string }
impl_to_source_slice! { ast::Ty, ", " }
impl_to_source_slice! { P<ast::Item>, "\n\n" }
impl ToSource for ast::Attribute_ {
fn to_source(&self) -> String {
pprust::attribute_to_string(&dummy_spanned(self.clone()))
}
}
impl ToSourceWithHygiene for ast::Attribute_ {
fn to_source_with_hygiene(&self) -> String {
self.to_source()
impl ToTokens for () {
fn to_tokens(&self, _cx: &ExtCtxt) -> Vec<TokenTree> {
vec![ast::TtDelimited(DUMMY_SP, Rc::new(ast::Delimited {
delim: token::Paren,
open_span: DUMMY_SP,
tts: vec![],
close_span: DUMMY_SP,
}))]
}
}
impl ToSource for str {
fn to_source(&self) -> String {
let lit = dummy_spanned(ast::LitStr(
token::intern_and_get_ident(self), ast::CookedStr));
pprust::lit_to_string(&lit)
}
}
impl ToSourceWithHygiene for str {
fn to_source_with_hygiene(&self) -> String {
self.to_source()
impl ToTokens for ast::Lit {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
// FIXME: This is wrong
P(ast::Expr {
id: ast::DUMMY_NODE_ID,
node: ast::ExprLit(P(self.clone())),
span: DUMMY_SP,
}).to_tokens(cx)
}
}
impl ToSource for () {
fn to_source(&self) -> String {
"()".to_string()
}
}
impl ToSourceWithHygiene for () {
fn to_source_with_hygiene(&self) -> String {
self.to_source()
impl ToTokens for bool {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
dummy_spanned(ast::LitBool(*self)).to_tokens(cx)
}
}
impl ToSource for bool {
fn to_source(&self) -> String {
let lit = dummy_spanned(ast::LitBool(*self));
pprust::lit_to_string(&lit)
}
}
impl ToSourceWithHygiene for bool {
fn to_source_with_hygiene(&self) -> String {
self.to_source()
impl ToTokens for char {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
dummy_spanned(ast::LitChar(*self)).to_tokens(cx)
}
}
impl ToSource for char {
fn to_source(&self) -> String {
let lit = dummy_spanned(ast::LitChar(*self));
pprust::lit_to_string(&lit)
}
}
impl ToSourceWithHygiene for char {
fn to_source_with_hygiene(&self) -> String {
self.to_source()
}
}
macro_rules! impl_to_source_int {
macro_rules! impl_to_tokens_int {
(signed, $t:ty, $tag:expr) => (
impl ToSource for $t {
fn to_source(&self) -> String {
impl ToTokens for $t {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
let lit = ast::LitInt(*self as u64, ast::SignedIntLit($tag,
ast::Sign::new(*self)));
pprust::lit_to_string(&dummy_spanned(lit))
}
}
impl ToSourceWithHygiene for $t {
fn to_source_with_hygiene(&self) -> String {
self.to_source()
dummy_spanned(lit).to_tokens(cx)
}
}
);
(unsigned, $t:ty, $tag:expr) => (
impl ToSource for $t {
fn to_source(&self) -> String {
impl ToTokens for $t {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
let lit = ast::LitInt(*self as u64, ast::UnsignedIntLit($tag));
pprust::lit_to_string(&dummy_spanned(lit))
}
}
impl ToSourceWithHygiene for $t {
fn to_source_with_hygiene(&self) -> String {
self.to_source()
dummy_spanned(lit).to_tokens(cx)
}
}
);
}
impl_to_source_int! { signed, isize, ast::TyIs }
impl_to_source_int! { signed, i8, ast::TyI8 }
impl_to_source_int! { signed, i16, ast::TyI16 }
impl_to_source_int! { signed, i32, ast::TyI32 }
impl_to_source_int! { signed, i64, ast::TyI64 }
impl_to_tokens_int! { signed, isize, ast::TyIs }
impl_to_tokens_int! { signed, i8, ast::TyI8 }
impl_to_tokens_int! { signed, i16, ast::TyI16 }
impl_to_tokens_int! { signed, i32, ast::TyI32 }
impl_to_tokens_int! { signed, i64, ast::TyI64 }
impl_to_source_int! { unsigned, usize, ast::TyUs }
impl_to_source_int! { unsigned, u8, ast::TyU8 }
impl_to_source_int! { unsigned, u16, ast::TyU16 }
impl_to_source_int! { unsigned, u32, ast::TyU32 }
impl_to_source_int! { unsigned, u64, ast::TyU64 }
// Alas ... we write these out instead. All redundant.
macro_rules! impl_to_tokens {
($t:ty) => (
impl ToTokens for $t {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
cx.parse_tts_with_hygiene(self.to_source_with_hygiene())
}
}
)
}
macro_rules! impl_to_tokens_lifetime {
($t:ty) => (
impl<'a> ToTokens for $t {
fn to_tokens(&self, cx: &ExtCtxt) -> Vec<TokenTree> {
cx.parse_tts_with_hygiene(self.to_source_with_hygiene())
}
}
)
}
impl_to_tokens! { ast::Ident }
impl_to_tokens! { ast::Path }
impl_to_tokens! { P<ast::Item> }
impl_to_tokens! { P<ast::ImplItem> }
impl_to_tokens! { P<ast::TraitItem> }
impl_to_tokens! { P<ast::Pat> }
impl_to_tokens! { ast::Arm }
impl_to_tokens_lifetime! { &'a [P<ast::Item>] }
impl_to_tokens! { ast::Ty }
impl_to_tokens_lifetime! { &'a [ast::Ty] }
impl_to_tokens! { Generics }
impl_to_tokens! { ast::WhereClause }
impl_to_tokens! { P<ast::Stmt> }
impl_to_tokens! { P<ast::Expr> }
impl_to_tokens! { ast::Block }
impl_to_tokens! { ast::Arg }
impl_to_tokens! { ast::Attribute_ }
impl_to_tokens_lifetime! { &'a str }
impl_to_tokens! { () }
impl_to_tokens! { char }
impl_to_tokens! { bool }
impl_to_tokens! { isize }
impl_to_tokens! { i8 }
impl_to_tokens! { i16 }
impl_to_tokens! { i32 }
impl_to_tokens! { i64 }
impl_to_tokens! { usize }
impl_to_tokens! { u8 }
impl_to_tokens! { u16 }
impl_to_tokens! { u32 }
impl_to_tokens! { u64 }
impl_to_tokens_int! { unsigned, usize, ast::TyUs }
impl_to_tokens_int! { unsigned, u8, ast::TyU8 }
impl_to_tokens_int! { unsigned, u16, ast::TyU16 }
impl_to_tokens_int! { unsigned, u32, ast::TyU32 }
impl_to_tokens_int! { unsigned, u64, ast::TyU64 }
pub trait ExtParseUtils {
fn parse_item(&self, s: String) -> P<ast::Item>;
@ -349,12 +261,6 @@ pub trait ExtParseUtils {
fn parse_tts(&self, s: String) -> Vec<ast::TokenTree>;
}
trait ExtParseUtilsWithHygiene {
// FIXME (Issue #16472): This should go away after ToToken impls
// are revised to go directly to token-trees.
fn parse_tts_with_hygiene(&self, s: String) -> Vec<ast::TokenTree>;
}
impl<'a> ExtParseUtils for ExtCtxt<'a> {
fn parse_item(&self, s: String) -> P<ast::Item> {
@ -386,19 +292,6 @@ fn parse_tts(&self, s: String) -> Vec<ast::TokenTree> {
self.parse_sess())
}
}
impl<'a> ExtParseUtilsWithHygiene for ExtCtxt<'a> {
fn parse_tts_with_hygiene(&self, s: String) -> Vec<ast::TokenTree> {
use parse::with_hygiene::parse_tts_from_source_str;
parse_tts_from_source_str("<quote expansion>".to_string(),
s,
self.cfg(),
self.parse_sess())
}
}
}
pub fn expand_quote_tokens<'cx>(cx: &'cx mut ExtCtxt,

View File

@ -682,6 +682,13 @@ pub fn noop_fold_interpolated<T: Folder>(nt: token::Nonterminal, fld: &mut T)
token::NtMeta(meta_item) => token::NtMeta(fld.fold_meta_item(meta_item)),
token::NtPath(path) => token::NtPath(Box::new(fld.fold_path(*path))),
token::NtTT(tt) => token::NtTT(P(fld.fold_tt(&*tt))),
token::NtArm(arm) => token::NtArm(fld.fold_arm(arm)),
token::NtImplItem(arm) =>
token::NtImplItem(fld.fold_impl_item(arm)
.expect_one("expected fold to produce exactly one item")),
token::NtTraitItem(arm) =>
token::NtTraitItem(fld.fold_trait_item(arm)
.expect_one("expected fold to produce exactly one item")),
}
}

View File

@ -19,7 +19,6 @@
use std::borrow::Cow;
use std::char;
use std::fmt;
use std::mem::replace;
use std::rc::Rc;
@ -71,11 +70,6 @@ pub struct StringReader<'a> {
pub peek_tok: token::Token,
pub peek_span: Span,
// FIXME (Issue #16472): This field should go away after ToToken impls
// are revised to go directly to token-trees.
/// Is \x00<name>,<ctxt>\x00 is interpreted as encoded ast::Ident?
read_embedded_ident: bool,
// cache a direct reference to the source text, so that we don't have to
// retrieve it via `self.filemap.src.as_ref().unwrap()` all the time.
source_text: Rc<String>
@ -130,17 +124,6 @@ fn peek(&self) -> TokenAndSpan {
}
}
// FIXME (Issue #16472): This function should go away after
// ToToken impls are revised to go directly to token-trees.
pub fn make_reader_with_embedded_idents<'b>(span_diagnostic: &'b SpanHandler,
filemap: Rc<codemap::FileMap>)
-> StringReader<'b> {
let mut sr = StringReader::new_raw(span_diagnostic, filemap);
sr.read_embedded_ident = true;
sr.advance_token();
sr
}
impl<'a> StringReader<'a> {
/// For comments.rs, which hackily pokes into pos and curr
pub fn new_raw<'b>(span_diagnostic: &'b SpanHandler,
@ -162,7 +145,6 @@ pub fn new_raw<'b>(span_diagnostic: &'b SpanHandler,
/* dummy values; not read */
peek_tok: token::Eof,
peek_span: codemap::DUMMY_SP,
read_embedded_ident: false,
source_text: source_text
};
sr.bump();
@ -578,81 +560,6 @@ fn scan_block_comment(&mut self) -> Option<TokenAndSpan> {
})
}
// FIXME (Issue #16472): The scan_embedded_hygienic_ident function
// should go away after we revise the syntax::ext::quote::ToToken
// impls to go directly to token-trees instead of thing -> string
// -> token-trees. (The function is currently used to resolve
// Issues #15750 and #15962.)
//
// Since this function is only used for certain internal macros,
// and the functionality it provides is not exposed to end user
// programs, pnkfelix deliberately chose to write it in a way that
// favors rustc debugging effectiveness over runtime efficiency.
/// Scan through input of form \x00name_NNNNNN,ctxt_CCCCCCC\x00
/// whence: `NNNNNN` is a string of characters forming an integer
/// (the name) and `CCCCCCC` is a string of characters forming an
/// integer (the ctxt), separate by a comma and delimited by a
/// `\x00` marker.
#[inline(never)]
fn scan_embedded_hygienic_ident(&mut self) -> ast::Ident {
fn bump_expecting_char<'a,D:fmt::Debug>(r: &mut StringReader<'a>,
c: char,
described_c: D,
whence: &str) {
match r.curr {
Some(r_c) if r_c == c => r.bump(),
Some(r_c) => panic!("expected {:?}, hit {:?}, {}", described_c, r_c, whence),
None => panic!("expected {:?}, hit EOF, {}", described_c, whence),
}
}
let whence = "while scanning embedded hygienic ident";
// skip over the leading `\x00`
bump_expecting_char(self, '\x00', "nul-byte", whence);
// skip over the "name_"
for c in "name_".chars() {
bump_expecting_char(self, c, c, whence);
}
let start_bpos = self.last_pos;
let base = 10;
// find the integer representing the name
self.scan_digits(base, base);
let encoded_name : u32 = self.with_str_from(start_bpos, |s| {
u32::from_str_radix(s, 10).unwrap_or_else(|_| {
panic!("expected digits representing a name, got {:?}, {}, range [{:?},{:?}]",
s, whence, start_bpos, self.last_pos);
})
});
// skip over the `,`
bump_expecting_char(self, ',', "comma", whence);
// skip over the "ctxt_"
for c in "ctxt_".chars() {
bump_expecting_char(self, c, c, whence);
}
// find the integer representing the ctxt
let start_bpos = self.last_pos;
self.scan_digits(base, base);
let encoded_ctxt : ast::SyntaxContext = self.with_str_from(start_bpos, |s| {
u32::from_str_radix(s, 10).unwrap_or_else(|_| {
panic!("expected digits representing a ctxt, got {:?}, {}", s, whence);
})
});
// skip over the `\x00`
bump_expecting_char(self, '\x00', "nul-byte", whence);
ast::Ident { name: ast::Name(encoded_name),
ctxt: encoded_ctxt, }
}
/// Scan through any digits (base `scan_radix`) or underscores,
/// and return how many digits there were.
///
@ -1020,20 +927,6 @@ fn next_token_inner(&mut self) -> token::Token {
return token::Literal(num, suffix)
}
if self.read_embedded_ident {
match (c.unwrap(), self.nextch(), self.nextnextch()) {
('\x00', Some('n'), Some('a')) => {
let ast_ident = self.scan_embedded_hygienic_ident();
return if self.curr_is(':') && self.nextch_is(':') {
token::Ident(ast_ident, token::ModName)
} else {
token::Ident(ast_ident, token::Plain)
};
}
_ => {}
}
}
match c.expect("next_token_inner called at EOF") {
// One-byte tokens.
';' => { self.bump(); return token::Semi; }

View File

@ -166,9 +166,6 @@ pub fn parse_stmt_from_source_str(name: String,
maybe_aborted(p.parse_stmt(), p)
}
// Note: keep in sync with `with_hygiene::parse_tts_from_source_str`
// until #16472 is resolved.
//
// Warning: This parses with quote_depth > 0, which is not the default.
pub fn parse_tts_from_source_str(name: String,
source: String,
@ -186,8 +183,6 @@ pub fn parse_tts_from_source_str(name: String,
maybe_aborted(panictry!(p.parse_all_token_trees()),p)
}
// Note: keep in sync with `with_hygiene::new_parser_from_source_str`
// until #16472 is resolved.
// Create a new parser from a source string
pub fn new_parser_from_source_str<'a>(sess: &'a ParseSess,
cfg: ast::CrateConfig,
@ -220,8 +215,6 @@ pub fn new_sub_parser_from_file<'a>(sess: &'a ParseSess,
p
}
// Note: keep this in sync with `with_hygiene::filemap_to_parser` until
// #16472 is resolved.
/// Given a filemap and config, return a parser
pub fn filemap_to_parser<'a>(sess: &'a ParseSess,
filemap: Rc<FileMap>,
@ -277,8 +270,6 @@ pub fn string_to_filemap(sess: &ParseSess, source: String, path: String)
sess.span_diagnostic.cm.new_filemap(path, source)
}
// Note: keep this in sync with `with_hygiene::filemap_to_tts` (apart
// from the StringReader constructor), until #16472 is resolved.
/// Given a filemap, produce a sequence of token-trees
pub fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>)
-> Vec<ast::TokenTree> {
@ -300,69 +291,6 @@ pub fn tts_to_parser<'a>(sess: &'a ParseSess,
p
}
// FIXME (Issue #16472): The `with_hygiene` mod should go away after
// ToToken impls are revised to go directly to token-trees.
pub mod with_hygiene {
use ast;
use codemap::FileMap;
use parse::parser::Parser;
use std::rc::Rc;
use super::ParseSess;
use super::{maybe_aborted, string_to_filemap, tts_to_parser};
// Note: keep this in sync with `super::parse_tts_from_source_str` until
// #16472 is resolved.
//
// Warning: This parses with quote_depth > 0, which is not the default.
pub fn parse_tts_from_source_str(name: String,
source: String,
cfg: ast::CrateConfig,
sess: &ParseSess) -> Vec<ast::TokenTree> {
let mut p = new_parser_from_source_str(
sess,
cfg,
name,
source
);
p.quote_depth += 1;
// right now this is re-creating the token trees from ... token trees.
maybe_aborted(panictry!(p.parse_all_token_trees()),p)
}
// Note: keep this in sync with `super::new_parser_from_source_str` until
// #16472 is resolved.
// Create a new parser from a source string
fn new_parser_from_source_str<'a>(sess: &'a ParseSess,
cfg: ast::CrateConfig,
name: String,
source: String) -> Parser<'a> {
filemap_to_parser(sess, string_to_filemap(sess, source, name), cfg)
}
// Note: keep this in sync with `super::filemap_to_parserr` until
// #16472 is resolved.
/// Given a filemap and config, return a parser
fn filemap_to_parser<'a>(sess: &'a ParseSess,
filemap: Rc<FileMap>,
cfg: ast::CrateConfig) -> Parser<'a> {
tts_to_parser(sess, filemap_to_tts(sess, filemap), cfg)
}
// Note: keep this in sync with `super::filemap_to_tts` until
// #16472 is resolved.
/// Given a filemap, produce a sequence of token-trees
fn filemap_to_tts(sess: &ParseSess, filemap: Rc<FileMap>)
-> Vec<ast::TokenTree> {
// it appears to me that the cfg doesn't matter here... indeed,
// parsing tt's probably shouldn't require a parser at all.
use super::lexer::make_reader_with_embedded_idents as make_reader;
let cfg = Vec::new();
let srdr = make_reader(&sess.span_diagnostic, filemap);
let mut p1 = Parser::new(sess, cfg, Box::new(srdr));
panictry!(p1.parse_all_token_trees())
}
}
/// Abort if necessary
pub fn maybe_aborted<T>(result: T, p: Parser) -> T {
p.abort_if_errors();

View File

@ -1152,7 +1152,8 @@ pub fn parse_trait_items(&mut self) -> PResult<Vec<P<TraitItem>>> {
&token::OpenDelim(token::Brace),
&token::CloseDelim(token::Brace),
seq_sep_none(),
|p| {
|p| -> PResult<P<TraitItem>> {
maybe_whole!(no_clone p, NtTraitItem);
let mut attrs = p.parse_outer_attributes();
let lo = p.span.lo;
@ -2943,6 +2944,8 @@ fn parse_match_expr(&mut self) -> PResult<P<Expr>> {
}
pub fn parse_arm_nopanic(&mut self) -> PResult<Arm> {
maybe_whole!(no_clone self, NtArm);
let attrs = self.parse_outer_attributes();
let pats = try!(self.parse_pats());
let mut guard = None;
@ -4335,6 +4338,8 @@ fn parse_item_fn(&mut self, unsafety: Unsafety, abi: abi::Abi) -> PResult<ItemIn
/// Parse an impl item.
pub fn parse_impl_item(&mut self) -> PResult<P<ImplItem>> {
maybe_whole!(no_clone self, NtImplItem);
let mut attrs = self.parse_outer_attributes();
let lo = self.span.lo;
let vis = try!(self.parse_visibility());

View File

@ -381,6 +381,10 @@ pub enum Nonterminal {
NtMeta(P<ast::MetaItem>),
NtPath(Box<ast::Path>),
NtTT(P<ast::TokenTree>), // needs P'ed to break a circularity
// These is not exposed to macros, but is used by quasiquote.
NtArm(ast::Arm),
NtImplItem(P<ast::ImplItem>),
NtTraitItem(P<ast::TraitItem>),
}
impl fmt::Debug for Nonterminal {
@ -396,6 +400,9 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
NtMeta(..) => f.pad("NtMeta(..)"),
NtPath(..) => f.pad("NtPath(..)"),
NtTT(..) => f.pad("NtTT(..)"),
NtArm(..) => f.pad("NtArm(..)"),
NtImplItem(..) => f.pad("NtImplItem(..)"),
NtTraitItem(..) => f.pad("NtTraitItem(..)"),
}
}
}

View File

@ -66,7 +66,6 @@ pub struct State<'a> {
cur_cmnt_and_lit: CurrentCommentAndLiteral,
boxes: Vec<pp::Breaks>,
ann: &'a (PpAnn+'a),
encode_idents_with_hygiene: bool,
}
pub fn rust_printer<'a>(writer: Box<Write+'a>) -> State<'a> {
@ -87,7 +86,6 @@ pub fn rust_printer_annotated<'a>(writer: Box<Write+'a>,
},
boxes: Vec::new(),
ann: ann,
encode_idents_with_hygiene: false,
}
}
@ -179,7 +177,6 @@ pub fn new(cm: &'a CodeMap,
},
boxes: Vec::new(),
ann: ann,
encode_idents_with_hygiene: false,
}
}
}
@ -290,103 +287,99 @@ pub fn token_to_string(tok: &Token) -> String {
token::SpecialVarNt(var) => format!("${}", var.as_str()),
token::Interpolated(ref nt) => match *nt {
token::NtExpr(ref e) => expr_to_string(&**e),
token::NtMeta(ref e) => meta_item_to_string(&**e),
token::NtTy(ref e) => ty_to_string(&**e),
token::NtPath(ref e) => path_to_string(&**e),
token::NtItem(..) => "an interpolated item".to_string(),
token::NtBlock(..) => "an interpolated block".to_string(),
token::NtStmt(..) => "an interpolated statement".to_string(),
token::NtPat(..) => "an interpolated pattern".to_string(),
token::NtIdent(..) => "an interpolated identifier".to_string(),
token::NtTT(..) => "an interpolated tt".to_string(),
token::NtExpr(ref e) => expr_to_string(&**e),
token::NtMeta(ref e) => meta_item_to_string(&**e),
token::NtTy(ref e) => ty_to_string(&**e),
token::NtPath(ref e) => path_to_string(&**e),
token::NtItem(..) => "an interpolated item".to_string(),
token::NtBlock(..) => "an interpolated block".to_string(),
token::NtStmt(..) => "an interpolated statement".to_string(),
token::NtPat(..) => "an interpolated pattern".to_string(),
token::NtIdent(..) => "an interpolated identifier".to_string(),
token::NtTT(..) => "an interpolated tt".to_string(),
token::NtArm(..) => "an interpolated arm".to_string(),
token::NtImplItem(..) => "an interpolated impl item".to_string(),
token::NtTraitItem(..) => "an interpolated trait item".to_string(),
}
}
}
// FIXME (Issue #16472): the thing_to_string_impls macro should go away
// after we revise the syntax::ext::quote::ToToken impls to go directly
// to token-trees instead of thing -> string -> token-trees.
macro_rules! thing_to_string_impls {
($to_string:ident) => {
pub fn ty_to_string(ty: &ast::Ty) -> String {
$to_string(|s| s.print_type(ty))
to_string(|s| s.print_type(ty))
}
pub fn bounds_to_string(bounds: &[ast::TyParamBound]) -> String {
$to_string(|s| s.print_bounds("", bounds))
to_string(|s| s.print_bounds("", bounds))
}
pub fn pat_to_string(pat: &ast::Pat) -> String {
$to_string(|s| s.print_pat(pat))
to_string(|s| s.print_pat(pat))
}
pub fn arm_to_string(arm: &ast::Arm) -> String {
$to_string(|s| s.print_arm(arm))
to_string(|s| s.print_arm(arm))
}
pub fn expr_to_string(e: &ast::Expr) -> String {
$to_string(|s| s.print_expr(e))
to_string(|s| s.print_expr(e))
}
pub fn lifetime_to_string(e: &ast::Lifetime) -> String {
$to_string(|s| s.print_lifetime(e))
to_string(|s| s.print_lifetime(e))
}
pub fn tt_to_string(tt: &ast::TokenTree) -> String {
$to_string(|s| s.print_tt(tt))
to_string(|s| s.print_tt(tt))
}
pub fn tts_to_string(tts: &[ast::TokenTree]) -> String {
$to_string(|s| s.print_tts(tts))
to_string(|s| s.print_tts(tts))
}
pub fn stmt_to_string(stmt: &ast::Stmt) -> String {
$to_string(|s| s.print_stmt(stmt))
to_string(|s| s.print_stmt(stmt))
}
pub fn attr_to_string(attr: &ast::Attribute) -> String {
$to_string(|s| s.print_attribute(attr))
to_string(|s| s.print_attribute(attr))
}
pub fn item_to_string(i: &ast::Item) -> String {
$to_string(|s| s.print_item(i))
to_string(|s| s.print_item(i))
}
pub fn impl_item_to_string(i: &ast::ImplItem) -> String {
$to_string(|s| s.print_impl_item(i))
to_string(|s| s.print_impl_item(i))
}
pub fn trait_item_to_string(i: &ast::TraitItem) -> String {
$to_string(|s| s.print_trait_item(i))
to_string(|s| s.print_trait_item(i))
}
pub fn generics_to_string(generics: &ast::Generics) -> String {
$to_string(|s| s.print_generics(generics))
to_string(|s| s.print_generics(generics))
}
pub fn where_clause_to_string(i: &ast::WhereClause) -> String {
$to_string(|s| s.print_where_clause(i))
to_string(|s| s.print_where_clause(i))
}
pub fn fn_block_to_string(p: &ast::FnDecl) -> String {
$to_string(|s| s.print_fn_block_args(p))
to_string(|s| s.print_fn_block_args(p))
}
pub fn path_to_string(p: &ast::Path) -> String {
$to_string(|s| s.print_path(p, false, 0))
to_string(|s| s.print_path(p, false, 0))
}
pub fn ident_to_string(id: &ast::Ident) -> String {
$to_string(|s| s.print_ident(*id))
to_string(|s| s.print_ident(*id))
}
pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ident,
opt_explicit_self: Option<&ast::ExplicitSelf_>,
generics: &ast::Generics) -> String {
$to_string(|s| {
to_string(|s| {
try!(s.head(""));
try!(s.print_fn(decl, unsafety, abi::Rust, Some(name),
generics, opt_explicit_self, ast::Inherited));
@ -396,7 +389,7 @@ pub fn fun_to_string(decl: &ast::FnDecl, unsafety: ast::Unsafety, name: ast::Ide
}
pub fn block_to_string(blk: &ast::Block) -> String {
$to_string(|s| {
to_string(|s| {
// containing cbox, will be closed by print-block at }
try!(s.cbox(indent_unit));
// head-ibox, will be closed by print-block after {
@ -406,59 +399,31 @@ pub fn block_to_string(blk: &ast::Block) -> String {
}
pub fn meta_item_to_string(mi: &ast::MetaItem) -> String {
$to_string(|s| s.print_meta_item(mi))
to_string(|s| s.print_meta_item(mi))
}
pub fn attribute_to_string(attr: &ast::Attribute) -> String {
$to_string(|s| s.print_attribute(attr))
to_string(|s| s.print_attribute(attr))
}
pub fn lit_to_string(l: &ast::Lit) -> String {
$to_string(|s| s.print_literal(l))
to_string(|s| s.print_literal(l))
}
pub fn explicit_self_to_string(explicit_self: &ast::ExplicitSelf_) -> String {
$to_string(|s| s.print_explicit_self(explicit_self, ast::MutImmutable).map(|_| {}))
to_string(|s| s.print_explicit_self(explicit_self, ast::MutImmutable).map(|_| {}))
}
pub fn variant_to_string(var: &ast::Variant) -> String {
$to_string(|s| s.print_variant(var))
to_string(|s| s.print_variant(var))
}
pub fn arg_to_string(arg: &ast::Arg) -> String {
$to_string(|s| s.print_arg(arg))
to_string(|s| s.print_arg(arg))
}
pub fn mac_to_string(arg: &ast::Mac) -> String {
$to_string(|s| s.print_mac(arg, ::parse::token::Paren))
}
} }
thing_to_string_impls! { to_string }
// FIXME (Issue #16472): the whole `with_hygiene` mod should go away
// after we revise the syntax::ext::quote::ToToken impls to go directly
// to token-trees instea of thing -> string -> token-trees.
pub mod with_hygiene {
use abi;
use ast;
use std::io;
use super::indent_unit;
// This function is the trick that all the rest of the routines
// hang on.
pub fn to_string_hyg<F>(f: F) -> String where
F: FnOnce(&mut super::State) -> io::Result<()>,
{
super::to_string(move |s| {
s.encode_idents_with_hygiene = true;
f(s)
})
}
thing_to_string_impls! { to_string_hyg }
to_string(|s| s.print_mac(arg, ::parse::token::Paren))
}
pub fn visibility_qualified(vis: ast::Visibility, s: &str) -> String {
@ -2006,12 +1971,7 @@ pub fn print_decl(&mut self, decl: &ast::Decl) -> io::Result<()> {
}
pub fn print_ident(&mut self, ident: ast::Ident) -> io::Result<()> {
if self.encode_idents_with_hygiene {
let encoded = ident.encode_with_hygiene();
try!(word(&mut self.s, &encoded[..]))
} else {
try!(word(&mut self.s, &token::get_ident(ident)))
}
try!(word(&mut self.s, &token::get_ident(ident)));
self.ann.post(self, NodeIdent(&ident))
}

View File

@ -32,7 +32,6 @@ macro_rules! unexported_macro { () => (3) }
#[plugin_registrar]
pub fn plugin_registrar(reg: &mut Registry) {
reg.register_macro("make_a_1", expand_make_a_1);
reg.register_macro("forged_ident", expand_forged_ident);
reg.register_macro("identity", expand_identity);
reg.register_syntax_extension(
token::intern("into_foo"),
@ -104,29 +103,4 @@ fn expand_into_foo_multi(cx: &mut ExtCtxt,
}
}
fn expand_forged_ident(cx: &mut ExtCtxt, sp: Span, tts: &[TokenTree]) -> Box<MacResult+'static> {
use syntax::ext::quote::rt::*;
if !tts.is_empty() {
cx.span_fatal(sp, "forged_ident takes no arguments");
}
// Most of this is modelled after the expansion of the `quote_expr!`
// macro ...
let parse_sess = cx.parse_sess();
let cfg = cx.cfg();
// ... except this is where we inject a forged identifier,
// and deliberately do not call `cx.parse_tts_with_hygiene`
// (because we are testing that this will be *rejected*
// by the default parser).
let expr = {
let tt = cx.parse_tts("\x00name_2,ctxt_0\x00".to_string());
let mut parser = new_parser_from_tts(parse_sess, cfg, tt);
parser.parse_expr()
};
MacEager::expr(expr)
}
pub fn foo() {}

View File

@ -1,28 +0,0 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
// aux-build:macro_crate_test.rs
// ignore-stage1
// error-pattern: unknown start of token: \u{0}
// Issue #15750 and #15962 : this test is checking that the standard
// parser rejects embedded idents. pnkfelix did not want to attempt
// to make a test file that itself used the embedded ident input form,
// since he worried that would be difficult to work with in many text
// editors, so instead he made a macro that expands into the embedded
// ident form.
#![feature(plugin)]
#![plugin(macro_crate_test)]
fn main() {
let x = 0;
assert_eq!(3, forged_ident!());
}

View File

@ -15,38 +15,25 @@
extern crate syntax;
use syntax::ast;
use syntax::codemap;
use syntax::codemap::{self, DUMMY_SP};
use syntax::parse;
use syntax::print::pprust;
trait FakeExtCtxt {
fn call_site(&self) -> codemap::Span;
fn cfg(&self) -> ast::CrateConfig;
fn ident_of(&self, st: &str) -> ast::Ident;
fn name_of(&self, st: &str) -> ast::Name;
fn parse_sess(&self) -> &parse::ParseSess;
}
impl FakeExtCtxt for parse::ParseSess {
fn call_site(&self) -> codemap::Span {
codemap::Span {
lo: codemap::BytePos(0),
hi: codemap::BytePos(0),
expn_id: codemap::NO_EXPANSION,
}
}
fn cfg(&self) -> ast::CrateConfig { Vec::new() }
fn ident_of(&self, st: &str) -> ast::Ident {
parse::token::str_to_ident(st)
}
fn name_of(&self, st: &str) -> ast::Name {
parse::token::intern(st)
}
fn parse_sess(&self) -> &parse::ParseSess { self }
}
fn main() {
let cx = parse::new_parse_sess();
let ps = syntax::parse::new_parse_sess();
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps, vec![],
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()));
cx.bt_push(syntax::codemap::ExpnInfo {
call_site: DUMMY_SP,
callee: syntax::codemap::NameAndSpan {
name: "".to_string(),
format: syntax::codemap::MacroBang,
allow_internal_unstable: false,
span: None,
}
});
let cx = &mut cx;
assert_eq!(pprust::expr_to_string(&*quote_expr!(&cx, 23)), "23");

View File

@ -17,38 +17,25 @@
extern crate syntax;
use syntax::ast;
use syntax::codemap;
use syntax::codemap::{self, DUMMY_SP};
use syntax::parse;
use syntax::print::pprust;
trait FakeExtCtxt {
fn call_site(&self) -> codemap::Span;
fn cfg(&self) -> ast::CrateConfig;
fn ident_of(&self, st: &str) -> ast::Ident;
fn name_of(&self, st: &str) -> ast::Name;
fn parse_sess(&self) -> &parse::ParseSess;
}
impl FakeExtCtxt for parse::ParseSess {
fn call_site(&self) -> codemap::Span {
codemap::Span {
lo: codemap::BytePos(0),
hi: codemap::BytePos(0),
expn_id: codemap::NO_EXPANSION,
}
}
fn cfg(&self) -> ast::CrateConfig { Vec::new() }
fn ident_of(&self, st: &str) -> ast::Ident {
parse::token::str_to_ident(st)
}
fn name_of(&self, st: &str) -> ast::Name {
parse::token::intern(st)
}
fn parse_sess(&self) -> &parse::ParseSess { self }
}
fn main() {
let cx = parse::new_parse_sess();
let ps = syntax::parse::new_parse_sess();
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps, vec![],
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()));
cx.bt_push(syntax::codemap::ExpnInfo {
call_site: DUMMY_SP,
callee: syntax::codemap::NameAndSpan {
name: "".to_string(),
format: syntax::codemap::MacroBang,
allow_internal_unstable: false,
span: None,
}
});
let cx = &mut cx;
assert_eq!(pprust::expr_to_string(&*quote_expr!(&cx, 23)), "23");

View File

@ -1,28 +0,0 @@
-include ../tools.mk
# Issue #15750, #15962 : This test ensures that our special embedded
# ident syntax hack is not treated as legitimate input by the lexer in
# normal mode.
#
# It is modelled after the `unicode-input/` test, since we need to
# create files with syntax that can trip up normal text editting tools
# (namely text with embedded nul-bytes).
# This test attempts to run rustc itself from the compiled binary; but
# that means that you need to set the LD_LIBRARY_PATH for rustc itself
# while running create_and_compile, and that won't work for stage1.
# FIXME ignore windows
ifndef IS_WINDOWS
ifeq ($(RUST_BUILD_STAGE),1)
DOTEST=
else
DOTEST=dotest
endif
endif
all: $(DOTEST)
dotest:
$(RUSTC) create_and_compile.rs
$(call RUN,create_and_compile) "$(RUSTC)" "$(TMPDIR)"

View File

@ -1,44 +0,0 @@
// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use std::env;
use std::fs::File;
use std::process::Command;
use std::io::Write;
use std::path::Path;
// creates broken.rs, which has the Ident \x00name_0,ctxt_0\x00
// embedded within it, and then attempts to compile broken.rs with the
// provided `rustc`
fn main() {
let args: Vec<String> = env::args().collect();
let rustc = &args[1];
let tmpdir = Path::new(&args[2]);
let main_file = tmpdir.join("broken.rs");
let _ = File::create(&main_file).unwrap()
.write_all(b"pub fn main() {
let \x00name_0,ctxt_0\x00 = 3;
println!(\"{}\", \x00name_0,ctxt_0\x00);
}").unwrap();
// rustc is passed to us with --out-dir and -L etc., so we
// can't exec it directly
let result = Command::new("sh")
.arg("-c")
.arg(&format!("{} {}", rustc, main_file.display()))
.output().unwrap();
let err = String::from_utf8_lossy(&result.stderr);
// positive test so that this test will be updated when the
// compiler changes.
assert!(err.contains("unknown start of token"))
}

View File

@ -9,59 +9,56 @@
// except according to those terms.
// ignore-cross-compile
// ignore-pretty
#![feature(quote, rustc_private)]
extern crate syntax;
use syntax::ast;
use syntax::codemap;
use syntax::parse;
use syntax::print::pprust;
trait FakeExtCtxt {
fn call_site(&self) -> codemap::Span;
fn cfg(&self) -> ast::CrateConfig;
fn ident_of(&self, st: &str) -> ast::Ident;
fn name_of(&self, st: &str) -> ast::Name;
fn parse_sess(&self) -> &parse::ParseSess;
}
impl FakeExtCtxt for parse::ParseSess {
fn call_site(&self) -> codemap::Span {
codemap::Span {
lo: codemap::BytePos(0),
hi: codemap::BytePos(0),
expn_id: codemap::NO_EXPANSION,
}
}
fn cfg(&self) -> ast::CrateConfig { Vec::new() }
fn ident_of(&self, st: &str) -> ast::Ident {
parse::token::str_to_ident(st)
}
fn name_of(&self, st: &str) -> ast::Name {
parse::token::intern(st)
}
fn parse_sess(&self) -> &parse::ParseSess { self }
}
use syntax::codemap::DUMMY_SP;
use syntax::print::pprust::*;
fn main() {
let cx = parse::new_parse_sess();
let ps = syntax::parse::new_parse_sess();
let mut cx = syntax::ext::base::ExtCtxt::new(
&ps, vec![],
syntax::ext::expand::ExpansionConfig::default("qquote".to_string()));
cx.bt_push(syntax::codemap::ExpnInfo {
call_site: DUMMY_SP,
callee: syntax::codemap::NameAndSpan {
name: "".to_string(),
format: syntax::codemap::MacroBang,
allow_internal_unstable: false,
span: None,
}
});
let cx = &mut cx;
assert_eq!(pprust::expr_to_string(&*quote_expr!(&cx, 23)), "23");
assert_eq!(pprust::pat_to_string(&*quote_pat!(&cx, Some(_))), "Some(_)");
assert_eq!(pprust::ty_to_string(&*quote_ty!(&cx, isize)), "isize");
macro_rules! check {
($f: ident, $($e: expr),+; $expect: expr) => ({
$(assert_eq!($f(&$e), $expect);)+
});
}
let arm = quote_arm!(&cx, (ref x, ref y) => (x, y),);
assert_eq!(pprust::arm_to_string(&arm), " (ref x, ref y) => (x, y),");
let abc = quote_expr!(cx, 23);
check!(expr_to_string, abc, *quote_expr!(cx, $abc); "23");
let attr = quote_attr!(&cx, #![cfg(foo = "bar")]);
assert_eq!(pprust::attr_to_string(&attr), "#![cfg(foo = \"bar\")]");
let ty = quote_ty!(cx, isize);
check!(ty_to_string, ty, *quote_ty!(cx, $ty); "isize");
let item = quote_item!(&cx, static x : isize = 10;).unwrap();
assert_eq!(pprust::item_to_string(&*item), "static x: isize = 10;");
let item = quote_item!(cx, static x: $ty = 10;).unwrap();
check!(item_to_string, item, quote_item!(cx, $item).unwrap(); "static x: isize = 10;");
let stmt = quote_stmt!(&cx, let x = 20;).unwrap();
assert_eq!(pprust::stmt_to_string(&*stmt), "let x = 20;");
let twenty: u16 = 20;
let stmt = quote_stmt!(cx, let x = $twenty;).unwrap();
check!(stmt_to_string, stmt, *quote_stmt!(cx, $stmt).unwrap(); "let x = 20u16;");
let pat = quote_pat!(cx, Some(_));
check!(pat_to_string, pat, *quote_pat!(cx, $pat); "Some(_)");
let expr = quote_expr!(cx, (x, y));
let arm = quote_arm!(cx, (ref x, ref y) => $expr,);
check!(arm_to_string, arm, quote_arm!(cx, $arm); " (ref x, ref y) => (x, y),");
let attr = quote_attr!(cx, #![cfg(foo = "bar")]);
check!(attribute_to_string, attr, quote_attr!(cx, $attr); r#"#![cfg(foo = "bar")]"#);
}