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.
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.
11 use ast::{Block, Crate, PatKind};
12 use ast::{Local, Ident, Mac_, Name, SpannedIdent};
13 use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
18 use attr::AttrMetaMethods;
19 use codemap::{Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
20 use syntax_pos::{self, Span, ExpnId};
21 use config::StripUnconfigured;
23 use feature_gate::{self, Features};
26 use util::move_map::MoveMap;
27 use parse::token::{fresh_mark, fresh_name, intern, keywords};
29 use tokenstream::TokenTree;
30 use util::small_vector::SmallVector;
35 use std::collections::HashSet;
37 // A trait for AST nodes and AST node lists into which macro invocations may expand.
38 trait MacroGenerable: Sized {
39 // Expand the given MacResult using its appropriate `make_*` method.
40 fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self>;
42 // Fold this node or list of nodes using the given folder.
43 fn fold_with<F: Folder>(self, folder: &mut F) -> Self;
44 fn visit_with<V: Visitor>(&self, visitor: &mut V);
46 // Return a placeholder expansion to allow compilation to continue after an erroring expansion.
47 fn dummy(span: Span) -> Self;
49 // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics.
50 fn kind_name() -> &'static str;
53 macro_rules! impl_macro_generable {
54 ($($ty:ty: $kind_name:expr, .$make:ident,
55 $(.$fold:ident)* $(lift .$fold_elt:ident)*,
56 $(.$visit:ident)* $(lift .$visit_elt:ident)*,
57 |$span:ident| $dummy:expr;)*) => { $(
58 impl MacroGenerable for $ty {
59 fn kind_name() -> &'static str { $kind_name }
60 fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> { result.$make() }
61 fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
62 $( folder.$fold(self) )*
63 $( self.into_iter().flat_map(|item| folder. $fold_elt (item)).collect() )*
65 fn visit_with<V: Visitor>(&self, visitor: &mut V) {
66 $( visitor.$visit(self) )*
67 $( for item in self.as_slice() { visitor. $visit_elt (item) } )*
69 fn dummy($span: Span) -> Self { $dummy }
74 impl_macro_generable! {
75 P<ast::Pat>: "pattern", .make_pat, .fold_pat, .visit_pat, |span| P(DummyResult::raw_pat(span));
76 P<ast::Ty>: "type", .make_ty, .fold_ty, .visit_ty, |span| DummyResult::raw_ty(span);
78 "expression", .make_expr, .fold_expr, .visit_expr, |span| DummyResult::raw_expr(span);
79 SmallVector<ast::Stmt>:
80 "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt, |_span| SmallVector::zero();
81 SmallVector<P<ast::Item>>:
82 "item", .make_items, lift .fold_item, lift .visit_item, |_span| SmallVector::zero();
83 SmallVector<ast::TraitItem>:
84 "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item,
85 |_span| SmallVector::zero();
86 SmallVector<ast::ImplItem>:
87 "impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item,
88 |_span| SmallVector::zero();
91 impl MacroGenerable for Option<P<ast::Expr>> {
92 fn kind_name() -> &'static str { "expression" }
93 fn dummy(_span: Span) -> Self { None }
94 fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> {
95 result.make_expr().map(Some)
97 fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
98 self.and_then(|expr| folder.fold_opt_expr(expr))
100 fn visit_with<V: Visitor>(&self, visitor: &mut V) {
101 self.as_ref().map(|expr| visitor.visit_expr(expr));
105 pub fn expand_expr(mut expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
107 // expr_mac should really be expr_ext or something; it's the
108 // entry-point for all syntax extensions.
109 ast::ExprKind::Mac(mac) => {
110 return expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, fld);
113 ast::ExprKind::While(cond, body, opt_ident) => {
114 let cond = fld.fold_expr(cond);
115 let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
116 expr.node = ast::ExprKind::While(cond, body, opt_ident);
119 ast::ExprKind::WhileLet(pat, cond, body, opt_ident) => {
120 let pat = fld.fold_pat(pat);
121 let cond = fld.fold_expr(cond);
123 // Hygienic renaming of the body.
124 let ((body, opt_ident), mut rewritten_pats) =
125 rename_in_scope(vec![pat],
128 |rename_fld, fld, (body, opt_ident)| {
129 expand_loop_block(rename_fld.fold_block(body), opt_ident, fld)
131 assert!(rewritten_pats.len() == 1);
133 expr.node = ast::ExprKind::WhileLet(rewritten_pats.remove(0), cond, body, opt_ident);
136 ast::ExprKind::Loop(loop_block, opt_ident) => {
137 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
138 expr.node = ast::ExprKind::Loop(loop_block, opt_ident);
141 ast::ExprKind::ForLoop(pat, head, body, opt_ident) => {
142 let pat = fld.fold_pat(pat);
144 // Hygienic renaming of the for loop body (for loop binds its pattern).
145 let ((body, opt_ident), mut rewritten_pats) =
146 rename_in_scope(vec![pat],
149 |rename_fld, fld, (body, opt_ident)| {
150 expand_loop_block(rename_fld.fold_block(body), opt_ident, fld)
152 assert!(rewritten_pats.len() == 1);
154 let head = fld.fold_expr(head);
155 expr.node = ast::ExprKind::ForLoop(rewritten_pats.remove(0), head, body, opt_ident);
158 ast::ExprKind::IfLet(pat, sub_expr, body, else_opt) => {
159 let pat = fld.fold_pat(pat);
161 // Hygienic renaming of the body.
162 let (body, mut rewritten_pats) =
163 rename_in_scope(vec![pat],
166 |rename_fld, fld, body| {
167 fld.fold_block(rename_fld.fold_block(body))
169 assert!(rewritten_pats.len() == 1);
171 let else_opt = else_opt.map(|else_opt| fld.fold_expr(else_opt));
172 let sub_expr = fld.fold_expr(sub_expr);
173 expr.node = ast::ExprKind::IfLet(rewritten_pats.remove(0), sub_expr, body, else_opt);
176 ast::ExprKind::Closure(capture_clause, fn_decl, block, fn_decl_span) => {
177 let (rewritten_fn_decl, rewritten_block)
178 = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
179 expr.node = ast::ExprKind::Closure(capture_clause,
185 _ => expr = noop_fold_expr(expr, fld),
190 /// Expand a macro invocation. Returns the result of expansion.
191 fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attribute>, span: Span,
192 fld: &mut MacroExpander) -> T
193 where T: MacroGenerable,
195 // It would almost certainly be cleaner to pass the whole macro invocation in,
196 // rather than pulling it apart and marking the tts and the ctxt separately.
197 let Mac_ { path, tts, .. } = mac.node;
198 let mark = fresh_mark();
200 fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
201 attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
202 -> Option<Box<MacResult + 'a>> {
203 // Detect use of feature-gated or invalid attributes on macro invoations
204 // since they will not be detected after macro expansion.
205 for attr in attrs.iter() {
206 feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic,
207 &fld.cx.parse_sess.codemap(),
208 &fld.cx.ecfg.features.unwrap());
211 if path.segments.len() > 1 {
212 fld.cx.span_err(path.span, "expected macro name without module separators");
216 let extname = path.segments[0].identifier.name;
217 let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) {
220 let mut err = fld.cx.struct_span_err(path.span,
221 &format!("macro undefined: '{}!'", &extname));
222 fld.cx.suggest_macro_name(&extname.as_str(), &mut err);
227 let ident = ident.unwrap_or(keywords::Invalid.ident());
229 NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
230 if ident.name != keywords::Invalid.name() {
232 format!("macro {}! expects no ident argument, given '{}'", extname, ident);
233 fld.cx.span_err(path.span, &msg);
237 fld.cx.bt_push(ExpnInfo {
238 call_site: call_site,
239 callee: NameAndSpan {
240 format: MacroBang(extname),
242 allow_internal_unstable: allow_internal_unstable,
246 let marked_tts = mark_tts(tts, mark);
247 Some(expandfun.expand(fld.cx, call_site, &marked_tts))
250 IdentTT(ref expander, tt_span, allow_internal_unstable) => {
251 if ident.name == keywords::Invalid.name() {
252 fld.cx.span_err(path.span,
253 &format!("macro {}! expects an ident argument", extname));
257 fld.cx.bt_push(ExpnInfo {
258 call_site: call_site,
259 callee: NameAndSpan {
260 format: MacroBang(extname),
262 allow_internal_unstable: allow_internal_unstable,
266 let marked_tts = mark_tts(tts, mark);
267 Some(expander.expand(fld.cx, call_site, ident, marked_tts))
271 if ident.name == keywords::Invalid.name() {
272 fld.cx.span_err(path.span,
273 &format!("macro {}! expects an ident argument", extname));
277 fld.cx.bt_push(ExpnInfo {
278 call_site: call_site,
279 callee: NameAndSpan {
280 format: MacroBang(extname),
282 // `macro_rules!` doesn't directly allow unstable
283 // (this is orthogonal to whether the macro it creates allows it)
284 allow_internal_unstable: false,
288 // DON'T mark before expansion.
289 fld.cx.insert_macro(ast::MacroDef {
291 id: ast::DUMMY_NODE_ID,
296 export: attr::contains_name(&attrs, "macro_export"),
297 allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
301 // macro_rules! has a side effect but expands to nothing.
306 MultiDecorator(..) | MultiModifier(..) => {
307 fld.cx.span_err(path.span,
308 &format!("`{}` can only be used in attributes", extname));
314 let opt_expanded = T::make_with(match mac_result(&path, ident, tts, mark, attrs, span, fld) {
315 Some(result) => result,
316 None => return T::dummy(span),
319 let expanded = if let Some(expanded) = opt_expanded {
322 let msg = format!("non-{kind} macro in {kind} position: {name}",
323 name = path.segments[0].identifier.name, kind = T::kind_name());
324 fld.cx.span_err(path.span, &msg);
325 return T::dummy(span);
328 let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) });
329 let configured = marked.fold_with(&mut fld.strip_unconfigured());
330 fld.load_macros(&configured);
331 let fully_expanded = configured.fold_with(fld);
336 /// Rename loop label and expand its loop body
338 /// The renaming procedure for loop is different in the sense that the loop
339 /// body is in a block enclosed by loop head so the renaming of loop label
340 /// must be propagated to the enclosed context.
341 fn expand_loop_block(loop_block: P<Block>,
342 opt_ident: Option<SpannedIdent>,
343 fld: &mut MacroExpander) -> (P<Block>, Option<SpannedIdent>) {
346 let new_label = fresh_name(label.node);
347 let rename = (label.node, new_label);
349 // The rename *must not* be added to the pending list of current
350 // syntax context otherwise an unrelated `break` or `continue` in
351 // the same context will pick that up in the deferred renaming pass
352 // and be renamed incorrectly.
353 let mut rename_list = vec!(rename);
354 let mut rename_fld = IdentRenamer{renames: &mut rename_list};
355 let renamed_ident = rename_fld.fold_ident(label.node);
357 // The rename *must* be added to the enclosed syntax context for
358 // `break` or `continue` to pick up because by definition they are
359 // in a block enclosed by loop head.
360 fld.cx.syntax_env.push_frame();
361 fld.cx.syntax_env.info().pending_renames.push(rename);
362 let expanded_block = expand_block_elts(loop_block, fld);
363 fld.cx.syntax_env.pop_frame();
365 (expanded_block, Some(Spanned { node: renamed_ident, span: label.span }))
367 None => (fld.fold_block(loop_block), opt_ident)
371 // eval $e with a new exts frame.
372 // must be a macro so that $e isn't evaluated too early.
373 macro_rules! with_exts_frame {
374 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
375 ({$extsboxexpr.push_frame();
376 $extsboxexpr.info().macros_escape = $macros_escape;
378 $extsboxexpr.pop_frame();
383 // When we enter a module, record it, for the sake of `module!`
384 pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
385 -> SmallVector<P<ast::Item>> {
386 expand_annotatable(Annotatable::Item(it), fld)
387 .into_iter().map(|i| i.expect_item()).collect()
391 fn expand_item_kind(item: ast::ItemKind, fld: &mut MacroExpander) -> ast::ItemKind {
393 ast::ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
394 let (rewritten_fn_decl, rewritten_body)
395 = expand_and_rename_fn_decl_and_block(decl, body, fld);
396 let expanded_generics = fold::noop_fold_generics(generics,fld);
397 ast::ItemKind::Fn(rewritten_fn_decl, unsafety, constness, abi,
398 expanded_generics, rewritten_body)
400 _ => noop_fold_item_kind(item, fld)
404 // does this attribute list contain "macro_use" ?
405 fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
407 let mut is_use = attr.check_name("macro_use");
408 if attr.check_name("macro_escape") {
410 fld.cx.struct_span_warn(attr.span,
411 "macro_escape is a deprecated synonym for macro_use");
413 if let ast::AttrStyle::Inner = attr.node.style {
414 err.help("consider an outer attribute, \
415 #[macro_use] mod ...").emit();
422 match attr.node.value.node {
423 ast::MetaItemKind::Word(..) => (),
424 _ => fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"),
433 fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
434 // perform all pending renames
436 let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
437 let mut rename_fld = IdentRenamer{renames:pending_renames};
438 rename_fld.fold_stmt(stmt).expect_one("rename_fold didn't return one value")
441 let (mac, style, attrs) = match stmt.node {
442 StmtKind::Mac(mac) => mac.unwrap(),
443 _ => return expand_non_macro_stmt(stmt, fld)
446 let mut fully_expanded: SmallVector<ast::Stmt> =
447 expand_mac_invoc(mac, None, attrs.into(), stmt.span, fld);
449 // If this is a macro invocation with a semicolon, then apply that
450 // semicolon to the final statement produced by expansion.
451 if style == MacStmtStyle::Semicolon {
452 if let Some(stmt) = fully_expanded.pop() {
453 fully_expanded.push(Stmt {
455 node: match stmt.node {
456 StmtKind::Expr(expr) => StmtKind::Semi(expr),
457 _ => stmt.node /* might already have a semi */
467 // expand a non-macro stmt. this is essentially the fallthrough for
468 // expand_stmt, above.
469 fn expand_non_macro_stmt(stmt: Stmt, fld: &mut MacroExpander)
470 -> SmallVector<Stmt> {
473 StmtKind::Local(local) => {
475 let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| {
476 // expand the ty since TyKind::FixedLengthVec contains an Expr
477 // and thus may have a macro use
478 let expanded_ty = ty.map(|t| fld.fold_ty(t));
479 // expand the pat (it might contain macro uses):
480 let expanded_pat = fld.fold_pat(pat);
481 // find the PatIdents in the pattern:
482 // oh dear heaven... this is going to include the enum
483 // names, as well... but that should be okay, as long as
484 // the new names are gensyms for the old ones.
485 // generate fresh names, push them to a new pending list
486 let idents = pattern_bindings(&expanded_pat);
487 let mut new_pending_renames =
488 idents.iter().map(|ident| (*ident, fresh_name(*ident))).collect();
489 // rewrite the pattern using the new names (the old
490 // ones have already been applied):
491 let rewritten_pat = {
492 // nested binding to allow borrow to expire:
493 let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
494 rename_fld.fold_pat(expanded_pat)
496 // add them to the existing pending renames:
497 fld.cx.syntax_env.info().pending_renames
498 .extend(new_pending_renames);
503 // also, don't forget to expand the init:
504 init: init.map(|e| fld.fold_expr(e)),
506 attrs: fold::fold_thin_attrs(attrs, fld),
509 SmallVector::one(Stmt {
511 node: StmtKind::Local(rewritten_local),
515 _ => noop_fold_stmt(stmt, fld),
519 // expand the arm of a 'match', renaming for macro hygiene
520 fn expand_arm(arm: ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
521 // expand pats... they might contain macro uses:
522 let expanded_pats = arm.pats.move_map(|pat| fld.fold_pat(pat));
523 if expanded_pats.is_empty() {
524 panic!("encountered match arm with 0 patterns");
527 // apply renaming and then expansion to the guard and the body:
528 let ((rewritten_guard, rewritten_body), rewritten_pats) =
529 rename_in_scope(expanded_pats,
531 (arm.guard, arm.body),
532 |rename_fld, fld, (ag, ab)|{
533 let rewritten_guard = ag.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
534 let rewritten_body = fld.fold_expr(rename_fld.fold_expr(ab));
535 (rewritten_guard, rewritten_body)
539 attrs: fold::fold_attrs(arm.attrs, fld),
540 pats: rewritten_pats,
541 guard: rewritten_guard,
542 body: rewritten_body,
546 fn rename_in_scope<X, F>(pats: Vec<P<ast::Pat>>,
547 fld: &mut MacroExpander,
550 -> (X, Vec<P<ast::Pat>>)
551 where F: Fn(&mut IdentRenamer, &mut MacroExpander, X) -> X
553 // all of the pats must have the same set of bindings, so use the
554 // first one to extract them and generate new names:
555 let idents = pattern_bindings(&pats[0]);
556 let new_renames = idents.into_iter().map(|id| (id, fresh_name(id))).collect();
557 // apply the renaming, but only to the PatIdents:
558 let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
559 let rewritten_pats = pats.move_map(|pat| rename_pats_fld.fold_pat(pat));
561 let mut rename_fld = IdentRenamer{ renames:&new_renames };
562 (f(&mut rename_fld, fld, x), rewritten_pats)
565 /// A visitor that extracts the PatKind::Ident (binding) paths
566 /// from a given thingy and puts them in a mutable
569 struct PatIdentFinder {
570 ident_accumulator: Vec<ast::Ident>
573 impl Visitor for PatIdentFinder {
574 fn visit_pat(&mut self, pattern: &ast::Pat) {
576 ast::Pat { id: _, node: PatKind::Ident(_, ref path1, ref inner), span: _ } => {
577 self.ident_accumulator.push(path1.node);
578 // visit optional subpattern of PatKind::Ident:
579 if let Some(ref subpat) = *inner {
580 self.visit_pat(subpat)
583 // use the default traversal for non-PatIdents
584 _ => visit::walk_pat(self, pattern)
589 /// find the PatKind::Ident paths in a pattern
590 fn pattern_bindings(pat: &ast::Pat) -> Vec<ast::Ident> {
591 let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
592 name_finder.visit_pat(pat);
593 name_finder.ident_accumulator
596 /// find the PatKind::Ident paths in a
597 fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
598 let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
599 for arg in &fn_decl.inputs {
600 pat_idents.visit_pat(&arg.pat);
602 pat_idents.ident_accumulator
605 // expand a block. pushes a new exts_frame, then calls expand_block_elts
606 pub fn expand_block(blk: P<Block>, fld: &mut MacroExpander) -> P<Block> {
607 // see note below about treatment of exts table
608 with_exts_frame!(fld.cx.syntax_env,false,
609 expand_block_elts(blk, fld))
612 // expand the elements of a block.
613 pub fn expand_block_elts(b: P<Block>, fld: &mut MacroExpander) -> P<Block> {
614 b.map(|Block {id, stmts, rules, span}| {
615 let new_stmts = stmts.into_iter().flat_map(|x| {
616 // perform pending renames and expand macros in the statement
617 fld.fold_stmt(x).into_iter()
628 fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
630 PatKind::Mac(_) => {}
631 _ => return noop_fold_pat(p, fld)
633 p.and_then(|ast::Pat {node, span, ..}| {
635 PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld),
641 /// A tree-folder that applies every rename in its (mutable) list
642 /// to every identifier, including both bindings and varrefs
643 /// (and lots of things that will turn out to be neither)
644 pub struct IdentRenamer<'a> {
645 renames: &'a mtwt::RenameList,
648 impl<'a> Folder for IdentRenamer<'a> {
649 fn fold_ident(&mut self, id: Ident) -> Ident {
650 mtwt::apply_renames(self.renames, id)
652 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
653 fold::noop_fold_mac(mac, self)
657 /// A tree-folder that applies every rename in its list to
658 /// the idents that are in PatKind::Ident patterns. This is more narrowly
659 /// focused than IdentRenamer, and is needed for FnDecl,
660 /// where we want to rename the args but not the fn name or the generics etc.
661 pub struct PatIdentRenamer<'a> {
662 renames: &'a mtwt::RenameList,
665 impl<'a> Folder for PatIdentRenamer<'a> {
666 fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
668 PatKind::Ident(..) => {},
669 _ => return noop_fold_pat(pat, self)
672 pat.map(|ast::Pat {id, node, span}| match node {
673 PatKind::Ident(binding_mode, Spanned{span: sp, node: ident}, sub) => {
674 let new_ident = mtwt::apply_renames(self.renames, ident);
676 PatKind::Ident(binding_mode,
677 Spanned{span: sp, node: new_ident},
678 sub.map(|p| self.fold_pat(p)));
688 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
689 fold::noop_fold_mac(mac, self)
693 fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
694 let new_items: SmallVector<Annotatable> = match a {
695 Annotatable::Item(it) => match it.node {
696 ast::ItemKind::Mac(..) => {
697 it.and_then(|it| match it.node {
698 ItemKind::Mac(mac) =>
699 expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld),
703 ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => {
705 it.ident.name != keywords::Invalid.name();
708 fld.cx.mod_push(it.ident);
710 let macro_use = contains_macro_use(fld, &it.attrs);
711 let result = with_exts_frame!(fld.cx.syntax_env,
713 noop_fold_item(it, fld));
719 _ => noop_fold_item(it, fld),
720 }.into_iter().map(|i| Annotatable::Item(i)).collect(),
722 Annotatable::TraitItem(it) => {
723 expand_trait_item(it.unwrap(), fld).into_iter().
724 map(|it| Annotatable::TraitItem(P(it))).collect()
727 Annotatable::ImplItem(ii) => {
728 expand_impl_item(ii.unwrap(), fld).into_iter().
729 map(|ii| Annotatable::ImplItem(P(ii))).collect()
733 new_items.into_iter().flat_map(|a| decorate(a, fld)).collect()
736 fn decorate(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
737 let mut decorator_items = SmallVector::zero();
738 let mut new_attrs = Vec::new();
739 expand_decorators(a.clone(), fld, &mut decorator_items, &mut new_attrs);
741 let mut new_items = SmallVector::one(a.fold_attrs(new_attrs));
742 new_items.push_all(decorator_items);
746 fn expand_decorators(a: Annotatable,
747 fld: &mut MacroExpander,
748 decorator_items: &mut SmallVector<Annotatable>,
749 new_attrs: &mut Vec<ast::Attribute>)
751 for attr in a.attrs() {
752 let mname = intern(&attr.name());
753 match fld.cx.syntax_env.find(mname) {
754 Some(rc) => match *rc {
755 MultiDecorator(ref dec) => {
756 attr::mark_used(&attr);
758 fld.cx.bt_push(ExpnInfo {
759 call_site: attr.span,
760 callee: NameAndSpan {
761 format: MacroAttribute(mname),
762 span: Some(attr.span),
763 // attributes can do whatever they like,
765 allow_internal_unstable: true,
769 let mut items: SmallVector<Annotatable> = SmallVector::zero();
774 &mut |ann| items.push(ann));
777 for configured_item in item.fold_with(&mut fld.strip_unconfigured()) {
778 decorator_items.extend(expand_annotatable(configured_item, fld));
784 _ => new_attrs.push((*attr).clone()),
786 _ => new_attrs.push((*attr).clone()),
791 fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
792 let mut multi_modifier = None;
793 item = item.map_attrs(|mut attrs| {
794 for i in 0..attrs.len() {
795 if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
796 if let MultiModifier(..) = *extension {
797 multi_modifier = Some((attrs.remove(i), extension));
805 match multi_modifier {
806 None => expand_multi_modified(item, fld),
807 Some((attr, extension)) => match *extension {
808 MultiModifier(ref mac) => {
809 attr::mark_used(&attr);
810 fld.cx.bt_push(ExpnInfo {
811 call_site: attr.span,
812 callee: NameAndSpan {
813 format: MacroAttribute(intern(&attr.name())),
814 span: Some(attr.span),
815 // attributes can do whatever they like, for now
816 allow_internal_unstable: true,
819 let modified = mac.expand(fld.cx, attr.span, &attr.node.value, item);
821 modified.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
828 fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
829 -> SmallVector<ast::ImplItem> {
831 ast::ImplItemKind::Method(..) => SmallVector::one(ast::ImplItem {
836 defaultness: ii.defaultness,
837 node: match ii.node {
838 ast::ImplItemKind::Method(sig, body) => {
839 let (sig, body) = expand_and_rename_method(sig, body, fld);
840 ast::ImplItemKind::Method(sig, body)
846 ast::ImplItemKind::Macro(mac) => {
847 expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
849 _ => fold::noop_fold_impl_item(ii, fld)
853 fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
854 -> SmallVector<ast::TraitItem> {
856 ast::TraitItemKind::Method(_, Some(_)) => {
857 SmallVector::one(ast::TraitItem {
861 node: match ti.node {
862 ast::TraitItemKind::Method(sig, Some(body)) => {
863 let (sig, body) = expand_and_rename_method(sig, body, fld);
864 ast::TraitItemKind::Method(sig, Some(body))
871 ast::TraitItemKind::Macro(mac) => {
872 expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
874 _ => fold::noop_fold_trait_item(ti, fld)
878 /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
879 /// PatIdents in its arguments to perform renaming in the FnDecl and
880 /// the block, returning both the new FnDecl and the new Block.
881 fn expand_and_rename_fn_decl_and_block(fn_decl: P<ast::FnDecl>, block: P<ast::Block>,
882 fld: &mut MacroExpander)
883 -> (P<ast::FnDecl>, P<ast::Block>) {
884 let expanded_decl = fld.fold_fn_decl(fn_decl);
885 let idents = fn_decl_arg_bindings(&expanded_decl);
887 idents.iter().map(|id| (*id,fresh_name(*id))).collect();
888 // first, a renamer for the PatIdents, for the fn_decl:
889 let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
890 let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
891 // now, a renamer for *all* idents, for the body:
892 let mut rename_fld = IdentRenamer{renames: &renames};
893 let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
894 (rewritten_fn_decl,rewritten_body)
897 fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
898 fld: &mut MacroExpander)
899 -> (ast::MethodSig, P<ast::Block>) {
900 let (rewritten_fn_decl, rewritten_body)
901 = expand_and_rename_fn_decl_and_block(sig.decl, body, fld);
903 generics: fld.fold_generics(sig.generics),
905 unsafety: sig.unsafety,
906 constness: sig.constness,
907 decl: rewritten_fn_decl
911 pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
912 let t = match t.node.clone() {
913 ast::TyKind::Mac(mac) => {
914 if fld.cx.ecfg.features.unwrap().type_macros {
915 expand_mac_invoc(mac, None, Vec::new(), t.span, fld)
917 feature_gate::emit_feature_err(
918 &fld.cx.parse_sess.span_diagnostic,
921 feature_gate::GateIssue::Language,
922 "type macros are experimental");
924 DummyResult::raw_ty(t.span)
930 fold::noop_fold_ty(t, fld)
933 /// A tree-folder that performs macro expansion
934 pub struct MacroExpander<'a, 'b:'a> {
935 pub cx: &'a mut ExtCtxt<'b>,
938 impl<'a, 'b> MacroExpander<'a, 'b> {
939 pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
940 MacroExpander { cx: cx }
943 fn strip_unconfigured(&mut self) -> StripUnconfigured {
945 config: &self.cx.cfg,
946 should_test: self.cx.ecfg.should_test,
947 sess: self.cx.parse_sess,
948 features: self.cx.ecfg.features,
952 fn load_macros<T: MacroGenerable>(&mut self, node: &T) {
953 struct MacroLoadingVisitor<'a, 'b: 'a>{
954 cx: &'a mut ExtCtxt<'b>,
958 impl<'a, 'b> Visitor for MacroLoadingVisitor<'a, 'b> {
959 fn visit_mac(&mut self, _: &ast::Mac) {}
960 fn visit_item(&mut self, item: &ast::Item) {
961 if let ast::ItemKind::ExternCrate(..) = item.node {
962 // We need to error on `#[macro_use] extern crate` when it isn't at the
963 // crate root, because `$crate` won't work properly.
964 for def in self.cx.loader.load_crate(item, self.at_crate_root) {
965 self.cx.insert_macro(def);
968 let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
969 visit::walk_item(self, item);
970 self.at_crate_root = at_crate_root;
973 fn visit_block(&mut self, block: &ast::Block) {
974 let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
975 visit::walk_block(self, block);
976 self.at_crate_root = at_crate_root;
980 node.visit_with(&mut MacroLoadingVisitor {
981 at_crate_root: self.cx.syntax_env.is_crate_root(),
987 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
988 fn fold_crate(&mut self, c: Crate) -> Crate {
989 self.cx.filename = Some(self.cx.parse_sess.codemap().span_to_filename(c.span));
990 noop_fold_crate(c, self)
993 fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
994 expr.and_then(|expr| expand_expr(expr, self))
997 fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
998 expr.and_then(|expr| match expr.node {
999 ast::ExprKind::Mac(mac) =>
1000 expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, self),
1001 _ => Some(expand_expr(expr, self)),
1005 fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
1006 expand_pat(pat, self)
1009 fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
1010 use std::mem::replace;
1012 if let ast::ItemKind::Mod(ast::Mod { inner, .. }) = item.node {
1013 if item.span.contains(inner) {
1014 self.push_mod_path(item.ident, &item.attrs);
1015 result = expand_item(item, self);
1016 self.pop_mod_path();
1018 let filename = if inner != syntax_pos::DUMMY_SP {
1019 Some(self.cx.parse_sess.codemap().span_to_filename(inner))
1021 let orig_filename = replace(&mut self.cx.filename, filename);
1022 let orig_mod_path_stack = replace(&mut self.cx.mod_path_stack, Vec::new());
1023 result = expand_item(item, self);
1024 self.cx.filename = orig_filename;
1025 self.cx.mod_path_stack = orig_mod_path_stack;
1028 result = expand_item(item, self);
1033 fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
1034 expand_item_kind(item, self)
1037 fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
1038 expand_stmt(stmt, self)
1041 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
1042 let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true);
1043 let result = expand_block(block, self);
1044 self.cx.in_block = was_in_block;
1048 fn fold_arm(&mut self, arm: ast::Arm) -> ast::Arm {
1049 expand_arm(arm, self)
1052 fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
1053 expand_annotatable(Annotatable::TraitItem(P(i)), self)
1054 .into_iter().map(|i| i.expect_trait_item()).collect()
1057 fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
1058 expand_annotatable(Annotatable::ImplItem(P(i)), self)
1059 .into_iter().map(|i| i.expect_impl_item()).collect()
1062 fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
1063 expand_type(ty, self)
1067 impl<'a, 'b> MacroExpander<'a, 'b> {
1068 fn push_mod_path(&mut self, id: Ident, attrs: &[ast::Attribute]) {
1069 let default_path = id.name.as_str();
1070 let file_path = match ::attr::first_attr_value_str_by_name(attrs, "path") {
1072 None => default_path,
1074 self.cx.mod_path_stack.push(file_path)
1077 fn pop_mod_path(&mut self) {
1078 self.cx.mod_path_stack.pop().unwrap();
1082 pub struct ExpansionConfig<'feat> {
1083 pub crate_name: String,
1084 pub features: Option<&'feat Features>,
1085 pub recursion_limit: usize,
1086 pub trace_mac: bool,
1087 pub should_test: bool, // If false, strip `#[test]` nodes
1090 macro_rules! feature_tests {
1091 ($( fn $getter:ident = $field:ident, )*) => {
1093 pub fn $getter(&self) -> bool {
1094 match self.features {
1095 Some(&Features { $field: true, .. }) => true,
1103 impl<'feat> ExpansionConfig<'feat> {
1104 pub fn default(crate_name: String) -> ExpansionConfig<'static> {
1106 crate_name: crate_name,
1108 recursion_limit: 64,
1115 fn enable_quotes = quote,
1116 fn enable_asm = asm,
1117 fn enable_log_syntax = log_syntax,
1118 fn enable_concat_idents = concat_idents,
1119 fn enable_trace_macros = trace_macros,
1120 fn enable_allow_internal_unstable = allow_internal_unstable,
1121 fn enable_custom_derive = custom_derive,
1122 fn enable_pushpop_unsafe = pushpop_unsafe,
1126 pub fn expand_crate(mut cx: ExtCtxt,
1127 user_exts: Vec<NamedSyntaxExtension>,
1128 mut c: Crate) -> (Crate, HashSet<Name>) {
1129 if std_inject::no_core(&c) {
1130 cx.crate_root = None;
1131 } else if std_inject::no_std(&c) {
1132 cx.crate_root = Some("core");
1134 cx.crate_root = Some("std");
1137 let mut expander = MacroExpander::new(&mut cx);
1139 for (name, extension) in user_exts {
1140 expander.cx.syntax_env.insert(name, extension);
1143 let items = SmallVector::many(c.module.items);
1144 expander.load_macros(&items);
1145 c.module.items = items.into();
1147 let err_count = cx.parse_sess.span_diagnostic.err_count();
1148 let mut ret = expander.fold_crate(c);
1149 ret.exported_macros = expander.cx.exported_macros.clone();
1151 if cx.parse_sess.span_diagnostic.err_count() > err_count {
1152 cx.parse_sess.span_diagnostic.abort_if_errors();
1157 return (ret, cx.syntax_env.names);
1160 // HYGIENIC CONTEXT EXTENSION:
1161 // all of these functions are for walking over
1162 // ASTs and making some change to the context of every
1163 // element that has one. a CtxtFn is a trait-ified
1164 // version of a closure in (SyntaxContext -> SyntaxContext).
1165 // the ones defined here include:
1166 // Marker - add a mark to a context
1168 // A Marker adds the given mark to the syntax context and
1169 // sets spans' `expn_id` to the given expn_id (unless it is `None`).
1170 struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
1172 impl Folder for Marker {
1173 fn fold_ident(&mut self, id: Ident) -> Ident {
1174 ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt))
1176 fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
1179 path: self.fold_path(node.path),
1180 tts: self.fold_tts(node.tts),
1182 span: self.new_span(span),
1186 fn new_span(&mut self, mut span: Span) -> Span {
1187 if let Some(expn_id) = self.expn_id {
1188 span.expn_id = expn_id;
1194 // apply a given mark to the given token trees. Used prior to expansion of a macro.
1195 fn mark_tts(tts: Vec<TokenTree>, m: Mrk) -> Vec<TokenTree> {
1196 noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
1202 use super::{pattern_bindings, expand_crate};
1203 use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
1207 use ext::base::{ExtCtxt, DummyMacroLoader};
1212 use util::parser_testing::{string_to_parser};
1213 use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
1217 // a visitor that extracts the paths
1218 // from a given thingy and puts them in a mutable
1219 // array (passed in to the traversal)
1221 struct PathExprFinderContext {
1222 path_accumulator: Vec<ast::Path> ,
1225 impl Visitor for PathExprFinderContext {
1226 fn visit_expr(&mut self, expr: &ast::Expr) {
1227 if let ast::ExprKind::Path(None, ref p) = expr.node {
1228 self.path_accumulator.push(p.clone());
1230 visit::walk_expr(self, expr);
1234 // find the variable references in a crate
1235 fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
1236 let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
1237 visit::walk_crate(&mut path_finder, the_crate);
1238 path_finder.path_accumulator
1241 /// A Visitor that extracts the identifiers from a thingy.
1242 // as a side note, I'm starting to want to abstract over these....
1243 struct IdentFinder {
1244 ident_accumulator: Vec<ast::Ident>
1247 impl Visitor for IdentFinder {
1248 fn visit_ident(&mut self, _: syntax_pos::Span, id: ast::Ident){
1249 self.ident_accumulator.push(id);
1253 /// Find the idents in a crate
1254 fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
1255 let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
1256 visit::walk_crate(&mut ident_finder, the_crate);
1257 ident_finder.ident_accumulator
1260 // these following tests are quite fragile, in that they don't test what
1261 // *kind* of failure occurs.
1263 fn test_ecfg() -> ExpansionConfig<'static> {
1264 ExpansionConfig::default("test".to_string())
1267 // make sure that macros can't escape fns
1269 #[test] fn macros_cant_escape_fns_test () {
1270 let src = "fn bogus() {macro_rules! z (() => (3+4));}\
1271 fn inty() -> i32 { z!() }".to_string();
1272 let sess = parse::ParseSess::new();
1273 let crate_ast = parse::parse_crate_from_source_str(
1274 "<test>".to_string(),
1276 Vec::new(), &sess).unwrap();
1278 let mut loader = DummyMacroLoader;
1279 let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1280 expand_crate(ecx, vec![], crate_ast);
1283 // make sure that macros can't escape modules
1285 #[test] fn macros_cant_escape_mods_test () {
1286 let src = "mod foo {macro_rules! z (() => (3+4));}\
1287 fn inty() -> i32 { z!() }".to_string();
1288 let sess = parse::ParseSess::new();
1289 let crate_ast = parse::parse_crate_from_source_str(
1290 "<test>".to_string(),
1292 Vec::new(), &sess).unwrap();
1293 let mut loader = DummyMacroLoader;
1294 let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1295 expand_crate(ecx, vec![], crate_ast);
1298 // macro_use modules should allow macros to escape
1299 #[test] fn macros_can_escape_flattened_mods_test () {
1300 let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
1301 fn inty() -> i32 { z!() }".to_string();
1302 let sess = parse::ParseSess::new();
1303 let crate_ast = parse::parse_crate_from_source_str(
1304 "<test>".to_string(),
1306 Vec::new(), &sess).unwrap();
1307 let mut loader = DummyMacroLoader;
1308 let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1309 expand_crate(ecx, vec![], crate_ast);
1312 fn expand_crate_str(crate_str: String) -> ast::Crate {
1313 let ps = parse::ParseSess::new();
1314 let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
1315 // the cfg argument actually does matter, here...
1316 let mut loader = DummyMacroLoader;
1317 let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
1318 expand_crate(ecx, vec![], crate_ast).0
1321 // find the pat_ident paths in a crate
1322 fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
1323 let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
1324 visit::walk_crate(&mut name_finder, the_crate);
1325 name_finder.ident_accumulator
1328 #[test] fn macro_tokens_should_match(){
1330 "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
1333 // should be able to use a bound identifier as a literal in a macro definition:
1334 #[test] fn self_macro_parsing(){
1336 "macro_rules! foo ((zz) => (287;));
1337 fn f(zz: i32) {foo!(zz);}".to_string()
1341 // create a really evil test case where a $x appears inside a binding of $x
1342 // but *shouldn't* bind because it was inserted by a different macro....
1343 // can't write this test case until we have macro-generating macros.
1346 fn fmt_in_macro_used_inside_module_macro() {
1347 let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
1348 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
1351 let cr = expand_crate_str(crate_str);
1352 // find the xx binding
1353 let bindings = crate_bindings(&cr);
1354 let cxbinds: Vec<&ast::Ident> =
1355 bindings.iter().filter(|b| b.name.as_str() == "xx").collect();
1356 let cxbinds: &[&ast::Ident] = &cxbinds[..];
1357 let cxbind = match (cxbinds.len(), cxbinds.get(0)) {
1359 _ => panic!("expected just one binding for ext_cx")
1361 let resolved_binding = mtwt::resolve(*cxbind);
1362 let varrefs = crate_varrefs(&cr);
1364 // the xx binding should bind all of the xx varrefs:
1365 for (idx,v) in varrefs.iter().filter(|p| {
1366 p.segments.len() == 1
1367 && p.segments[0].identifier.name.as_str() == "xx"
1369 if mtwt::resolve(v.segments[0].identifier) != resolved_binding {
1370 println!("uh oh, xx binding didn't match xx varref:");
1371 println!("this is xx varref \\# {}", idx);
1372 println!("binding: {}", cxbind);
1373 println!("resolves to: {}", resolved_binding);
1374 println!("varref: {}", v.segments[0].identifier);
1375 println!("resolves to: {}",
1376 mtwt::resolve(v.segments[0].identifier));
1377 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1379 assert_eq!(mtwt::resolve(v.segments[0].identifier),
1386 let pat = string_to_pat(
1387 "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
1388 let idents = pattern_bindings(&pat);
1389 assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
1392 // test the list of identifier patterns gathered by the visitor. Note that
1393 // 'None' is listed as an identifier pattern because we don't yet know that
1394 // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
1396 fn crate_bindings_test(){
1397 let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| {
1398 match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
1399 let idents = crate_bindings(&the_crate);
1400 assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
1403 // test the IdentRenamer directly
1405 fn ident_renamer_test () {
1406 let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1407 let f_ident = token::str_to_ident("f");
1408 let x_ident = token::str_to_ident("x");
1409 let int_ident = token::str_to_ident("i32");
1410 let renames = vec!((x_ident,Name(16)));
1411 let mut renamer = IdentRenamer{renames: &renames};
1412 let renamed_crate = renamer.fold_crate(the_crate);
1413 let idents = crate_idents(&renamed_crate);
1414 let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1415 assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)]);
1418 // test the PatIdentRenamer; only PatIdents get renamed
1420 fn pat_ident_renamer_test () {
1421 let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1422 let f_ident = token::str_to_ident("f");
1423 let x_ident = token::str_to_ident("x");
1424 let int_ident = token::str_to_ident("i32");
1425 let renames = vec!((x_ident,Name(16)));
1426 let mut renamer = PatIdentRenamer{renames: &renames};
1427 let renamed_crate = renamer.fold_crate(the_crate);
1428 let idents = crate_idents(&renamed_crate);
1429 let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1430 let x_name = x_ident.name;
1431 assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name]);