Auto merge of #34570 - jseyfried:no_rename, r=nrc
Simplify the macro hygiene algorithm This PR removes renaming from the hygiene algorithm and treats differently marked identifiers as unequal. This change makes the scope of identifiers in `macro_rules!` items empty. That is, identifiers in `macro_rules!` definitions do not inherit any semantics from the `macro_rules!`'s scope. Since `macro_rules!` macros are items, the scope of their identifiers "should" be the same as that of other items; in particular, the scope should contain only items. Since all items are unhygienic today, this would mean the scope should be empty. However, the scope of an identifier in a `macro_rules!` statement today is the scope that the identifier would have if it replaced the `macro_rules!` (excluding anything unhygienic, i.e. locals only). To continue to support this, this PR tracks the scope of each `macro_rules!` and uses it in `resolve` to ensure that an identifier expanded from a `macro_rules!` gets a chance to resolve to the locals in the `macro_rules!`'s scope. This PR is a pure refactoring. After this PR, - `syntax::ext::expand` is much simpler. - We can expand macros in any order without causing problems for hygiene (needed for macro modularization). - We can deprecate or remove today's `macro_rules!` scope easily. - Expansion performance improves by 25%, post-expansion memory usage decreases by ~5%. - Expanding a block is no longer quadratic in the number of `let` statements (fixes #10607). r? @nrc
This commit is contained in:
@@ -8,23 +8,21 @@
|
||||
// option. This file may not be copied, modified, or distributed
|
||||
// except according to those terms.
|
||||
|
||||
use ast::{Block, Crate, PatKind};
|
||||
use ast::{Local, Ident, Mac_, Name, SpannedIdent};
|
||||
use ast::{Block, Crate, Ident, Mac_, Name, PatKind};
|
||||
use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
|
||||
use ast;
|
||||
use attr::HasAttrs;
|
||||
use ext::mtwt;
|
||||
use attr;
|
||||
use attr::AttrMetaMethods;
|
||||
use codemap::{Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
||||
use codemap::{dummy_spanned, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
||||
use syntax_pos::{self, Span, ExpnId};
|
||||
use config::StripUnconfigured;
|
||||
use ext::base::*;
|
||||
use feature_gate::{self, Features};
|
||||
use fold;
|
||||
use fold::*;
|
||||
use util::move_map::MoveMap;
|
||||
use parse::token::{fresh_mark, fresh_name, intern, keywords};
|
||||
use parse::token::{fresh_mark, intern, keywords};
|
||||
use ptr::P;
|
||||
use tokenstream::TokenTree;
|
||||
use util::small_vector::SmallVector;
|
||||
@@ -96,89 +94,32 @@ impl MacroGenerable for Option<P<ast::Expr>> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn expand_expr(mut expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
|
||||
match expr.node {
|
||||
// expr_mac should really be expr_ext or something; it's the
|
||||
// entry-point for all syntax extensions.
|
||||
ast::ExprKind::Mac(mac) => {
|
||||
return expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, fld);
|
||||
}
|
||||
_ => P(noop_fold_expr(expr, fld)),
|
||||
}
|
||||
}
|
||||
|
||||
ast::ExprKind::While(cond, body, opt_ident) => {
|
||||
let cond = fld.fold_expr(cond);
|
||||
let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
|
||||
expr.node = ast::ExprKind::While(cond, body, opt_ident);
|
||||
}
|
||||
|
||||
ast::ExprKind::WhileLet(pat, cond, body, opt_ident) => {
|
||||
let pat = fld.fold_pat(pat);
|
||||
let cond = fld.fold_expr(cond);
|
||||
|
||||
// Hygienic renaming of the body.
|
||||
let ((body, opt_ident), mut rewritten_pats) =
|
||||
rename_in_scope(vec![pat],
|
||||
fld,
|
||||
(body, opt_ident),
|
||||
|rename_fld, fld, (body, opt_ident)| {
|
||||
expand_loop_block(rename_fld.fold_block(body), opt_ident, fld)
|
||||
});
|
||||
assert!(rewritten_pats.len() == 1);
|
||||
|
||||
expr.node = ast::ExprKind::WhileLet(rewritten_pats.remove(0), cond, body, opt_ident);
|
||||
}
|
||||
|
||||
ast::ExprKind::Loop(loop_block, opt_ident) => {
|
||||
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
|
||||
expr.node = ast::ExprKind::Loop(loop_block, opt_ident);
|
||||
}
|
||||
|
||||
ast::ExprKind::ForLoop(pat, head, body, opt_ident) => {
|
||||
let pat = fld.fold_pat(pat);
|
||||
|
||||
// Hygienic renaming of the for loop body (for loop binds its pattern).
|
||||
let ((body, opt_ident), mut rewritten_pats) =
|
||||
rename_in_scope(vec![pat],
|
||||
fld,
|
||||
(body, opt_ident),
|
||||
|rename_fld, fld, (body, opt_ident)| {
|
||||
expand_loop_block(rename_fld.fold_block(body), opt_ident, fld)
|
||||
});
|
||||
assert!(rewritten_pats.len() == 1);
|
||||
|
||||
let head = fld.fold_expr(head);
|
||||
expr.node = ast::ExprKind::ForLoop(rewritten_pats.remove(0), head, body, opt_ident);
|
||||
}
|
||||
|
||||
ast::ExprKind::IfLet(pat, sub_expr, body, else_opt) => {
|
||||
let pat = fld.fold_pat(pat);
|
||||
|
||||
// Hygienic renaming of the body.
|
||||
let (body, mut rewritten_pats) =
|
||||
rename_in_scope(vec![pat],
|
||||
fld,
|
||||
body,
|
||||
|rename_fld, fld, body| {
|
||||
fld.fold_block(rename_fld.fold_block(body))
|
||||
});
|
||||
assert!(rewritten_pats.len() == 1);
|
||||
|
||||
let else_opt = else_opt.map(|else_opt| fld.fold_expr(else_opt));
|
||||
let sub_expr = fld.fold_expr(sub_expr);
|
||||
expr.node = ast::ExprKind::IfLet(rewritten_pats.remove(0), sub_expr, body, else_opt);
|
||||
}
|
||||
|
||||
ast::ExprKind::Closure(capture_clause, fn_decl, block, fn_decl_span) => {
|
||||
let (rewritten_fn_decl, rewritten_block)
|
||||
= expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
|
||||
expr.node = ast::ExprKind::Closure(capture_clause,
|
||||
rewritten_fn_decl,
|
||||
rewritten_block,
|
||||
fn_decl_span);
|
||||
}
|
||||
|
||||
_ => expr = noop_fold_expr(expr, fld),
|
||||
};
|
||||
P(expr)
|
||||
struct MacroScopePlaceholder;
|
||||
impl MacResult for MacroScopePlaceholder {
|
||||
fn make_items(self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> {
|
||||
Some(SmallVector::one(P(ast::Item {
|
||||
ident: keywords::Invalid.ident(),
|
||||
attrs: Vec::new(),
|
||||
id: ast::DUMMY_NODE_ID,
|
||||
node: ast::ItemKind::Mac(dummy_spanned(ast::Mac_ {
|
||||
path: ast::Path { span: syntax_pos::DUMMY_SP, global: false, segments: Vec::new() },
|
||||
tts: Vec::new(),
|
||||
})),
|
||||
vis: ast::Visibility::Inherited,
|
||||
span: syntax_pos::DUMMY_SP,
|
||||
})))
|
||||
}
|
||||
}
|
||||
|
||||
/// Expand a macro invocation. Returns the result of expansion.
|
||||
@@ -219,6 +160,7 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
|
||||
};
|
||||
|
||||
let ident = ident.unwrap_or(keywords::Invalid.ident());
|
||||
let marked_tts = mark_tts(&tts, mark);
|
||||
match *extension {
|
||||
NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
|
||||
if ident.name != keywords::Invalid.name() {
|
||||
@@ -237,7 +179,6 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
|
||||
},
|
||||
});
|
||||
|
||||
let marked_tts = mark_tts(&tts, mark);
|
||||
Some(expandfun.expand(fld.cx, call_site, &marked_tts))
|
||||
}
|
||||
|
||||
@@ -257,7 +198,6 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
|
||||
}
|
||||
});
|
||||
|
||||
let marked_tts = mark_tts(&tts, mark);
|
||||
Some(expander.expand(fld.cx, call_site, ident, marked_tts))
|
||||
}
|
||||
|
||||
@@ -286,15 +226,14 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
|
||||
span: call_site,
|
||||
imported_from: None,
|
||||
use_locally: true,
|
||||
body: tts,
|
||||
body: marked_tts,
|
||||
export: attr::contains_name(&attrs, "macro_export"),
|
||||
allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
|
||||
attrs: attrs,
|
||||
});
|
||||
|
||||
// macro_rules! has a side effect but expands to nothing.
|
||||
fld.cx.bt_pop();
|
||||
None
|
||||
Some(Box::new(MacroScopePlaceholder))
|
||||
}
|
||||
|
||||
MultiDecorator(..) | MultiModifier(..) => {
|
||||
@@ -327,41 +266,6 @@ fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attr
|
||||
fully_expanded
|
||||
}
|
||||
|
||||
/// Rename loop label and expand its loop body
|
||||
///
|
||||
/// The renaming procedure for loop is different in the sense that the loop
|
||||
/// body is in a block enclosed by loop head so the renaming of loop label
|
||||
/// must be propagated to the enclosed context.
|
||||
fn expand_loop_block(loop_block: P<Block>,
|
||||
opt_ident: Option<SpannedIdent>,
|
||||
fld: &mut MacroExpander) -> (P<Block>, Option<SpannedIdent>) {
|
||||
match opt_ident {
|
||||
Some(label) => {
|
||||
let new_label = fresh_name(label.node);
|
||||
let rename = (label.node, new_label);
|
||||
|
||||
// The rename *must not* be added to the pending list of current
|
||||
// syntax context otherwise an unrelated `break` or `continue` in
|
||||
// the same context will pick that up in the deferred renaming pass
|
||||
// and be renamed incorrectly.
|
||||
let mut rename_list = vec!(rename);
|
||||
let mut rename_fld = IdentRenamer{renames: &mut rename_list};
|
||||
let renamed_ident = rename_fld.fold_ident(label.node);
|
||||
|
||||
// The rename *must* be added to the enclosed syntax context for
|
||||
// `break` or `continue` to pick up because by definition they are
|
||||
// in a block enclosed by loop head.
|
||||
fld.cx.syntax_env.push_frame();
|
||||
fld.cx.syntax_env.info().pending_renames.push(rename);
|
||||
let expanded_block = expand_block_elts(loop_block, fld);
|
||||
fld.cx.syntax_env.pop_frame();
|
||||
|
||||
(expanded_block, Some(Spanned { node: renamed_ident, span: label.span }))
|
||||
}
|
||||
None => (fld.fold_block(loop_block), opt_ident)
|
||||
}
|
||||
}
|
||||
|
||||
// eval $e with a new exts frame.
|
||||
// must be a macro so that $e isn't evaluated too early.
|
||||
macro_rules! with_exts_frame {
|
||||
@@ -381,20 +285,6 @@ pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
|
||||
.into_iter().map(|i| i.expect_item()).collect()
|
||||
}
|
||||
|
||||
/// Expand item_kind
|
||||
fn expand_item_kind(item: ast::ItemKind, fld: &mut MacroExpander) -> ast::ItemKind {
|
||||
match item {
|
||||
ast::ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
|
||||
let (rewritten_fn_decl, rewritten_body)
|
||||
= expand_and_rename_fn_decl_and_block(decl, body, fld);
|
||||
let expanded_generics = fold::noop_fold_generics(generics,fld);
|
||||
ast::ItemKind::Fn(rewritten_fn_decl, unsafety, constness, abi,
|
||||
expanded_generics, rewritten_body)
|
||||
}
|
||||
_ => noop_fold_item_kind(item, fld)
|
||||
}
|
||||
}
|
||||
|
||||
// does this attribute list contain "macro_use" ?
|
||||
fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
|
||||
for attr in attrs {
|
||||
@@ -425,16 +315,9 @@ fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool
|
||||
|
||||
/// Expand a stmt
|
||||
fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
|
||||
// perform all pending renames
|
||||
let stmt = {
|
||||
let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
|
||||
let mut rename_fld = IdentRenamer{renames:pending_renames};
|
||||
rename_fld.fold_stmt(stmt).expect_one("rename_fold didn't return one value")
|
||||
};
|
||||
|
||||
let (mac, style, attrs) = match stmt.node {
|
||||
StmtKind::Mac(mac) => mac.unwrap(),
|
||||
_ => return expand_non_macro_stmt(stmt, fld)
|
||||
_ => return noop_fold_stmt(stmt, fld)
|
||||
};
|
||||
|
||||
let mut fully_expanded: SmallVector<ast::Stmt> =
|
||||
@@ -451,167 +334,6 @@ fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
|
||||
fully_expanded
|
||||
}
|
||||
|
||||
// expand a non-macro stmt. this is essentially the fallthrough for
|
||||
// expand_stmt, above.
|
||||
fn expand_non_macro_stmt(stmt: Stmt, fld: &mut MacroExpander)
|
||||
-> SmallVector<Stmt> {
|
||||
// is it a let?
|
||||
match stmt.node {
|
||||
StmtKind::Local(local) => {
|
||||
// take it apart:
|
||||
let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| {
|
||||
// expand the ty since TyKind::FixedLengthVec contains an Expr
|
||||
// and thus may have a macro use
|
||||
let expanded_ty = ty.map(|t| fld.fold_ty(t));
|
||||
// expand the pat (it might contain macro uses):
|
||||
let expanded_pat = fld.fold_pat(pat);
|
||||
// find the PatIdents in the pattern:
|
||||
// oh dear heaven... this is going to include the enum
|
||||
// names, as well... but that should be okay, as long as
|
||||
// the new names are gensyms for the old ones.
|
||||
// generate fresh names, push them to a new pending list
|
||||
let idents = pattern_bindings(&expanded_pat);
|
||||
let mut new_pending_renames =
|
||||
idents.iter().map(|ident| (*ident, fresh_name(*ident))).collect();
|
||||
// rewrite the pattern using the new names (the old
|
||||
// ones have already been applied):
|
||||
let rewritten_pat = {
|
||||
// nested binding to allow borrow to expire:
|
||||
let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
|
||||
rename_fld.fold_pat(expanded_pat)
|
||||
};
|
||||
// add them to the existing pending renames:
|
||||
fld.cx.syntax_env.info().pending_renames
|
||||
.extend(new_pending_renames);
|
||||
Local {
|
||||
id: id,
|
||||
ty: expanded_ty,
|
||||
pat: rewritten_pat,
|
||||
// also, don't forget to expand the init:
|
||||
init: init.map(|e| fld.fold_expr(e)),
|
||||
span: span,
|
||||
attrs: fold::fold_thin_attrs(attrs, fld),
|
||||
}
|
||||
});
|
||||
SmallVector::one(Stmt {
|
||||
id: stmt.id,
|
||||
node: StmtKind::Local(rewritten_local),
|
||||
span: stmt.span,
|
||||
})
|
||||
}
|
||||
_ => noop_fold_stmt(stmt, fld),
|
||||
}
|
||||
}
|
||||
|
||||
// expand the arm of a 'match', renaming for macro hygiene
|
||||
fn expand_arm(arm: ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
|
||||
// expand pats... they might contain macro uses:
|
||||
let expanded_pats = arm.pats.move_map(|pat| fld.fold_pat(pat));
|
||||
if expanded_pats.is_empty() {
|
||||
panic!("encountered match arm with 0 patterns");
|
||||
}
|
||||
|
||||
// apply renaming and then expansion to the guard and the body:
|
||||
let ((rewritten_guard, rewritten_body), rewritten_pats) =
|
||||
rename_in_scope(expanded_pats,
|
||||
fld,
|
||||
(arm.guard, arm.body),
|
||||
|rename_fld, fld, (ag, ab)|{
|
||||
let rewritten_guard = ag.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
|
||||
let rewritten_body = fld.fold_expr(rename_fld.fold_expr(ab));
|
||||
(rewritten_guard, rewritten_body)
|
||||
});
|
||||
|
||||
ast::Arm {
|
||||
attrs: fold::fold_attrs(arm.attrs, fld),
|
||||
pats: rewritten_pats,
|
||||
guard: rewritten_guard,
|
||||
body: rewritten_body,
|
||||
}
|
||||
}
|
||||
|
||||
fn rename_in_scope<X, F>(pats: Vec<P<ast::Pat>>,
|
||||
fld: &mut MacroExpander,
|
||||
x: X,
|
||||
f: F)
|
||||
-> (X, Vec<P<ast::Pat>>)
|
||||
where F: Fn(&mut IdentRenamer, &mut MacroExpander, X) -> X
|
||||
{
|
||||
// all of the pats must have the same set of bindings, so use the
|
||||
// first one to extract them and generate new names:
|
||||
let idents = pattern_bindings(&pats[0]);
|
||||
let new_renames = idents.into_iter().map(|id| (id, fresh_name(id))).collect();
|
||||
// apply the renaming, but only to the PatIdents:
|
||||
let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
|
||||
let rewritten_pats = pats.move_map(|pat| rename_pats_fld.fold_pat(pat));
|
||||
|
||||
let mut rename_fld = IdentRenamer{ renames:&new_renames };
|
||||
(f(&mut rename_fld, fld, x), rewritten_pats)
|
||||
}
|
||||
|
||||
/// A visitor that extracts the PatKind::Ident (binding) paths
|
||||
/// from a given thingy and puts them in a mutable
|
||||
/// array
|
||||
#[derive(Clone)]
|
||||
struct PatIdentFinder {
|
||||
ident_accumulator: Vec<ast::Ident>
|
||||
}
|
||||
|
||||
impl Visitor for PatIdentFinder {
|
||||
fn visit_pat(&mut self, pattern: &ast::Pat) {
|
||||
match *pattern {
|
||||
ast::Pat { id: _, node: PatKind::Ident(_, ref path1, ref inner), span: _ } => {
|
||||
self.ident_accumulator.push(path1.node);
|
||||
// visit optional subpattern of PatKind::Ident:
|
||||
if let Some(ref subpat) = *inner {
|
||||
self.visit_pat(subpat)
|
||||
}
|
||||
}
|
||||
// use the default traversal for non-PatIdents
|
||||
_ => visit::walk_pat(self, pattern)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// find the PatKind::Ident paths in a pattern
|
||||
fn pattern_bindings(pat: &ast::Pat) -> Vec<ast::Ident> {
|
||||
let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
|
||||
name_finder.visit_pat(pat);
|
||||
name_finder.ident_accumulator
|
||||
}
|
||||
|
||||
/// find the PatKind::Ident paths in a
|
||||
fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
|
||||
let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
|
||||
for arg in &fn_decl.inputs {
|
||||
pat_idents.visit_pat(&arg.pat);
|
||||
}
|
||||
pat_idents.ident_accumulator
|
||||
}
|
||||
|
||||
// expand a block. pushes a new exts_frame, then calls expand_block_elts
|
||||
pub fn expand_block(blk: P<Block>, fld: &mut MacroExpander) -> P<Block> {
|
||||
// see note below about treatment of exts table
|
||||
with_exts_frame!(fld.cx.syntax_env,false,
|
||||
expand_block_elts(blk, fld))
|
||||
}
|
||||
|
||||
// expand the elements of a block.
|
||||
pub fn expand_block_elts(b: P<Block>, fld: &mut MacroExpander) -> P<Block> {
|
||||
b.map(|Block {id, stmts, rules, span}| {
|
||||
let new_stmts = stmts.into_iter().flat_map(|x| {
|
||||
// perform pending renames and expand macros in the statement
|
||||
fld.fold_stmt(x).into_iter()
|
||||
}).collect();
|
||||
Block {
|
||||
id: fld.new_id(id),
|
||||
stmts: new_stmts,
|
||||
rules: rules,
|
||||
span: span
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
|
||||
match p.node {
|
||||
PatKind::Mac(_) => {}
|
||||
@@ -625,62 +347,16 @@ fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
|
||||
})
|
||||
}
|
||||
|
||||
/// A tree-folder that applies every rename in its (mutable) list
|
||||
/// to every identifier, including both bindings and varrefs
|
||||
/// (and lots of things that will turn out to be neither)
|
||||
pub struct IdentRenamer<'a> {
|
||||
renames: &'a mtwt::RenameList,
|
||||
}
|
||||
|
||||
impl<'a> Folder for IdentRenamer<'a> {
|
||||
fn fold_ident(&mut self, id: Ident) -> Ident {
|
||||
mtwt::apply_renames(self.renames, id)
|
||||
}
|
||||
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||
fold::noop_fold_mac(mac, self)
|
||||
}
|
||||
}
|
||||
|
||||
/// A tree-folder that applies every rename in its list to
|
||||
/// the idents that are in PatKind::Ident patterns. This is more narrowly
|
||||
/// focused than IdentRenamer, and is needed for FnDecl,
|
||||
/// where we want to rename the args but not the fn name or the generics etc.
|
||||
pub struct PatIdentRenamer<'a> {
|
||||
renames: &'a mtwt::RenameList,
|
||||
}
|
||||
|
||||
impl<'a> Folder for PatIdentRenamer<'a> {
|
||||
fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
|
||||
match pat.node {
|
||||
PatKind::Ident(..) => {},
|
||||
_ => return noop_fold_pat(pat, self)
|
||||
}
|
||||
|
||||
pat.map(|ast::Pat {id, node, span}| match node {
|
||||
PatKind::Ident(binding_mode, Spanned{span: sp, node: ident}, sub) => {
|
||||
let new_ident = mtwt::apply_renames(self.renames, ident);
|
||||
let new_node =
|
||||
PatKind::Ident(binding_mode,
|
||||
Spanned{span: sp, node: new_ident},
|
||||
sub.map(|p| self.fold_pat(p)));
|
||||
ast::Pat {
|
||||
id: id,
|
||||
node: new_node,
|
||||
span: span,
|
||||
}
|
||||
},
|
||||
_ => unreachable!()
|
||||
})
|
||||
}
|
||||
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
||||
fold::noop_fold_mac(mac, self)
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
|
||||
match a {
|
||||
Annotatable::Item(it) => match it.node {
|
||||
ast::ItemKind::Mac(..) => {
|
||||
if match it.node {
|
||||
ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(),
|
||||
_ => unreachable!(),
|
||||
} {
|
||||
return SmallVector::one(Annotatable::Item(it));
|
||||
}
|
||||
it.and_then(|it| match it.node {
|
||||
ItemKind::Mac(mac) =>
|
||||
expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld),
|
||||
@@ -774,21 +450,6 @@ fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVe
|
||||
fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
|
||||
-> SmallVector<ast::ImplItem> {
|
||||
match ii.node {
|
||||
ast::ImplItemKind::Method(..) => SmallVector::one(ast::ImplItem {
|
||||
id: ii.id,
|
||||
ident: ii.ident,
|
||||
attrs: ii.attrs,
|
||||
vis: ii.vis,
|
||||
defaultness: ii.defaultness,
|
||||
node: match ii.node {
|
||||
ast::ImplItemKind::Method(sig, body) => {
|
||||
let (sig, body) = expand_and_rename_method(sig, body, fld);
|
||||
ast::ImplItemKind::Method(sig, body)
|
||||
}
|
||||
_ => unreachable!()
|
||||
},
|
||||
span: ii.span,
|
||||
}),
|
||||
ast::ImplItemKind::Macro(mac) => {
|
||||
expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
|
||||
}
|
||||
@@ -799,21 +460,6 @@ fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
|
||||
fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
|
||||
-> SmallVector<ast::TraitItem> {
|
||||
match ti.node {
|
||||
ast::TraitItemKind::Method(_, Some(_)) => {
|
||||
SmallVector::one(ast::TraitItem {
|
||||
id: ti.id,
|
||||
ident: ti.ident,
|
||||
attrs: ti.attrs,
|
||||
node: match ti.node {
|
||||
ast::TraitItemKind::Method(sig, Some(body)) => {
|
||||
let (sig, body) = expand_and_rename_method(sig, body, fld);
|
||||
ast::TraitItemKind::Method(sig, Some(body))
|
||||
}
|
||||
_ => unreachable!()
|
||||
},
|
||||
span: ti.span,
|
||||
})
|
||||
}
|
||||
ast::TraitItemKind::Macro(mac) => {
|
||||
expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
|
||||
}
|
||||
@@ -821,39 +467,6 @@ fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
|
||||
}
|
||||
}
|
||||
|
||||
/// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
|
||||
/// PatIdents in its arguments to perform renaming in the FnDecl and
|
||||
/// the block, returning both the new FnDecl and the new Block.
|
||||
fn expand_and_rename_fn_decl_and_block(fn_decl: P<ast::FnDecl>, block: P<ast::Block>,
|
||||
fld: &mut MacroExpander)
|
||||
-> (P<ast::FnDecl>, P<ast::Block>) {
|
||||
let expanded_decl = fld.fold_fn_decl(fn_decl);
|
||||
let idents = fn_decl_arg_bindings(&expanded_decl);
|
||||
let renames =
|
||||
idents.iter().map(|id| (*id,fresh_name(*id))).collect();
|
||||
// first, a renamer for the PatIdents, for the fn_decl:
|
||||
let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
|
||||
let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
|
||||
// now, a renamer for *all* idents, for the body:
|
||||
let mut rename_fld = IdentRenamer{renames: &renames};
|
||||
let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
|
||||
(rewritten_fn_decl,rewritten_body)
|
||||
}
|
||||
|
||||
fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
|
||||
fld: &mut MacroExpander)
|
||||
-> (ast::MethodSig, P<ast::Block>) {
|
||||
let (rewritten_fn_decl, rewritten_body)
|
||||
= expand_and_rename_fn_decl_and_block(sig.decl, body, fld);
|
||||
(ast::MethodSig {
|
||||
generics: fld.fold_generics(sig.generics),
|
||||
abi: sig.abi,
|
||||
unsafety: sig.unsafety,
|
||||
constness: sig.constness,
|
||||
decl: rewritten_fn_decl
|
||||
}, rewritten_body)
|
||||
}
|
||||
|
||||
pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
|
||||
let t = match t.node.clone() {
|
||||
ast::TyKind::Mac(mac) => {
|
||||
@@ -976,25 +589,17 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
|
||||
result
|
||||
}
|
||||
|
||||
fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
|
||||
expand_item_kind(item, self)
|
||||
}
|
||||
|
||||
fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
|
||||
expand_stmt(stmt, self)
|
||||
}
|
||||
|
||||
fn fold_block(&mut self, block: P<Block>) -> P<Block> {
|
||||
let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true);
|
||||
let result = expand_block(block, self);
|
||||
let result = with_exts_frame!(self.cx.syntax_env, false, noop_fold_block(block, self));
|
||||
self.cx.in_block = was_in_block;
|
||||
result
|
||||
}
|
||||
|
||||
fn fold_arm(&mut self, arm: ast::Arm) -> ast::Arm {
|
||||
expand_arm(arm, self)
|
||||
}
|
||||
|
||||
fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
|
||||
expand_annotatable(Annotatable::TraitItem(P(i)), self)
|
||||
.into_iter().map(|i| i.expect_trait_item()).collect()
|
||||
@@ -1145,18 +750,11 @@ fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{pattern_bindings, expand_crate};
|
||||
use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
|
||||
use super::{expand_crate, ExpansionConfig};
|
||||
use ast;
|
||||
use ast::Name;
|
||||
use syntax_pos;
|
||||
use ext::base::{ExtCtxt, DummyMacroLoader};
|
||||
use ext::mtwt;
|
||||
use fold::Folder;
|
||||
use parse;
|
||||
use parse::token;
|
||||
use util::parser_testing::{string_to_parser};
|
||||
use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
|
||||
use visit;
|
||||
use visit::Visitor;
|
||||
|
||||
@@ -1177,32 +775,6 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
// find the variable references in a crate
|
||||
fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
|
||||
let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
|
||||
visit::walk_crate(&mut path_finder, the_crate);
|
||||
path_finder.path_accumulator
|
||||
}
|
||||
|
||||
/// A Visitor that extracts the identifiers from a thingy.
|
||||
// as a side note, I'm starting to want to abstract over these....
|
||||
struct IdentFinder {
|
||||
ident_accumulator: Vec<ast::Ident>
|
||||
}
|
||||
|
||||
impl Visitor for IdentFinder {
|
||||
fn visit_ident(&mut self, _: syntax_pos::Span, id: ast::Ident){
|
||||
self.ident_accumulator.push(id);
|
||||
}
|
||||
}
|
||||
|
||||
/// Find the idents in a crate
|
||||
fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
|
||||
let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
|
||||
visit::walk_crate(&mut ident_finder, the_crate);
|
||||
ident_finder.ident_accumulator
|
||||
}
|
||||
|
||||
// these following tests are quite fragile, in that they don't test what
|
||||
// *kind* of failure occurs.
|
||||
|
||||
@@ -1264,13 +836,6 @@ mod tests {
|
||||
expand_crate(ecx, vec![], crate_ast).0
|
||||
}
|
||||
|
||||
// find the pat_ident paths in a crate
|
||||
fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
|
||||
let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
|
||||
visit::walk_crate(&mut name_finder, the_crate);
|
||||
name_finder.ident_accumulator
|
||||
}
|
||||
|
||||
#[test] fn macro_tokens_should_match(){
|
||||
expand_crate_str(
|
||||
"macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
|
||||
@@ -1287,93 +852,4 @@ mod tests {
|
||||
// create a really evil test case where a $x appears inside a binding of $x
|
||||
// but *shouldn't* bind because it was inserted by a different macro....
|
||||
// can't write this test case until we have macro-generating macros.
|
||||
|
||||
#[test]
|
||||
fn fmt_in_macro_used_inside_module_macro() {
|
||||
let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
|
||||
macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
|
||||
foo_module!();
|
||||
".to_string();
|
||||
let cr = expand_crate_str(crate_str);
|
||||
// find the xx binding
|
||||
let bindings = crate_bindings(&cr);
|
||||
let cxbinds: Vec<&ast::Ident> =
|
||||
bindings.iter().filter(|b| b.name.as_str() == "xx").collect();
|
||||
let cxbinds: &[&ast::Ident] = &cxbinds[..];
|
||||
let cxbind = match (cxbinds.len(), cxbinds.get(0)) {
|
||||
(1, Some(b)) => *b,
|
||||
_ => panic!("expected just one binding for ext_cx")
|
||||
};
|
||||
let resolved_binding = mtwt::resolve(*cxbind);
|
||||
let varrefs = crate_varrefs(&cr);
|
||||
|
||||
// the xx binding should bind all of the xx varrefs:
|
||||
for (idx,v) in varrefs.iter().filter(|p| {
|
||||
p.segments.len() == 1
|
||||
&& p.segments[0].identifier.name.as_str() == "xx"
|
||||
}).enumerate() {
|
||||
if mtwt::resolve(v.segments[0].identifier) != resolved_binding {
|
||||
println!("uh oh, xx binding didn't match xx varref:");
|
||||
println!("this is xx varref \\# {}", idx);
|
||||
println!("binding: {}", cxbind);
|
||||
println!("resolves to: {}", resolved_binding);
|
||||
println!("varref: {}", v.segments[0].identifier);
|
||||
println!("resolves to: {}",
|
||||
mtwt::resolve(v.segments[0].identifier));
|
||||
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
||||
}
|
||||
assert_eq!(mtwt::resolve(v.segments[0].identifier),
|
||||
resolved_binding);
|
||||
};
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn pat_idents(){
|
||||
let pat = string_to_pat(
|
||||
"(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
|
||||
let idents = pattern_bindings(&pat);
|
||||
assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
|
||||
}
|
||||
|
||||
// test the list of identifier patterns gathered by the visitor. Note that
|
||||
// 'None' is listed as an identifier pattern because we don't yet know that
|
||||
// it's the name of a 0-ary variant, and that 'i' appears twice in succession.
|
||||
#[test]
|
||||
fn crate_bindings_test(){
|
||||
let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| {
|
||||
match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
|
||||
let idents = crate_bindings(&the_crate);
|
||||
assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
|
||||
}
|
||||
|
||||
// test the IdentRenamer directly
|
||||
#[test]
|
||||
fn ident_renamer_test () {
|
||||
let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
|
||||
let f_ident = token::str_to_ident("f");
|
||||
let x_ident = token::str_to_ident("x");
|
||||
let int_ident = token::str_to_ident("i32");
|
||||
let renames = vec!((x_ident,Name(16)));
|
||||
let mut renamer = IdentRenamer{renames: &renames};
|
||||
let renamed_crate = renamer.fold_crate(the_crate);
|
||||
let idents = crate_idents(&renamed_crate);
|
||||
let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
|
||||
assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)]);
|
||||
}
|
||||
|
||||
// test the PatIdentRenamer; only PatIdents get renamed
|
||||
#[test]
|
||||
fn pat_ident_renamer_test () {
|
||||
let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
|
||||
let f_ident = token::str_to_ident("f");
|
||||
let x_ident = token::str_to_ident("x");
|
||||
let int_ident = token::str_to_ident("i32");
|
||||
let renames = vec!((x_ident,Name(16)));
|
||||
let mut renamer = PatIdentRenamer{renames: &renames};
|
||||
let renamed_crate = renamer.fold_crate(the_crate);
|
||||
let idents = crate_idents(&renamed_crate);
|
||||
let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
|
||||
let x_name = x_ident.name;
|
||||
assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name]);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user