]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
Auto merge of #34652 - jseyfried:fix_expansion_perf, r=nrc
[rust.git] / src / libsyntax / ext / expand.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 use ast::{Block, Crate, PatKind};
12 use ast::{Local, Ident, Mac_, Name, SpannedIdent};
13 use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
14 use ast;
15 use attr::HasAttrs;
16 use ext::mtwt;
17 use attr;
18 use attr::AttrMetaMethods;
19 use codemap::{Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
20 use syntax_pos::{self, Span, ExpnId};
21 use config::StripUnconfigured;
22 use ext::base::*;
23 use feature_gate::{self, Features};
24 use fold;
25 use fold::*;
26 use util::move_map::MoveMap;
27 use parse::token::{fresh_mark, fresh_name, intern, keywords};
28 use ptr::P;
29 use tokenstream::TokenTree;
30 use util::small_vector::SmallVector;
31 use visit;
32 use visit::Visitor;
33 use std_inject;
34
35 use std::collections::HashSet;
36
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>;
41
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);
45
46     // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics.
47     fn kind_name() -> &'static str;
48
49     // Return a placeholder expansion to allow compilation to continue after an erroring expansion.
50     fn dummy(span: Span) -> Self {
51         Self::make_with(DummyResult::any(span)).unwrap()
52     }
53 }
54
55 macro_rules! impl_macro_generable {
56     ($($ty:ty: $kind_name:expr, .$make:ident,
57                $(.$fold:ident)*  $(lift .$fold_elt:ident)*,
58                $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { $(
59         impl MacroGenerable for $ty {
60             fn kind_name() -> &'static str { $kind_name }
61             fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> { result.$make() }
62             fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
63                 $( folder.$fold(self) )*
64                 $( self.into_iter().flat_map(|item| folder. $fold_elt (item)).collect() )*
65             }
66             fn visit_with<V: Visitor>(&self, visitor: &mut V) {
67                 $( visitor.$visit(self) )*
68                 $( for item in self.as_slice() { visitor. $visit_elt (item) } )*
69             }
70         }
71     )* }
72 }
73
74 impl_macro_generable! {
75     P<ast::Expr>: "expression", .make_expr, .fold_expr, .visit_expr;
76     P<ast::Pat>:  "pattern",    .make_pat,  .fold_pat,  .visit_pat;
77     P<ast::Ty>:   "type",       .make_ty,   .fold_ty,   .visit_ty;
78     SmallVector<ast::Stmt>: "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt;
79     SmallVector<P<ast::Item>>: "item",   .make_items, lift .fold_item, lift .visit_item;
80     SmallVector<ast::TraitItem>:
81         "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item;
82     SmallVector<ast::ImplItem>:
83         "impl item",  .make_impl_items,  lift .fold_impl_item,  lift .visit_impl_item;
84 }
85
86 impl MacroGenerable for Option<P<ast::Expr>> {
87     fn kind_name() -> &'static str { "expression" }
88     fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> {
89         result.make_expr().map(Some)
90     }
91     fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
92         self.and_then(|expr| folder.fold_opt_expr(expr))
93     }
94     fn visit_with<V: Visitor>(&self, visitor: &mut V) {
95         self.as_ref().map(|expr| visitor.visit_expr(expr));
96     }
97 }
98
99 pub fn expand_expr(mut expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
100     match expr.node {
101         // expr_mac should really be expr_ext or something; it's the
102         // entry-point for all syntax extensions.
103         ast::ExprKind::Mac(mac) => {
104             return expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, fld);
105         }
106
107         ast::ExprKind::While(cond, body, opt_ident) => {
108             let cond = fld.fold_expr(cond);
109             let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
110             expr.node = ast::ExprKind::While(cond, body, opt_ident);
111         }
112
113         ast::ExprKind::WhileLet(pat, cond, body, opt_ident) => {
114             let pat = fld.fold_pat(pat);
115             let cond = fld.fold_expr(cond);
116
117             // Hygienic renaming of the body.
118             let ((body, opt_ident), mut rewritten_pats) =
119                 rename_in_scope(vec![pat],
120                                 fld,
121                                 (body, opt_ident),
122                                 |rename_fld, fld, (body, opt_ident)| {
123                 expand_loop_block(rename_fld.fold_block(body), opt_ident, fld)
124             });
125             assert!(rewritten_pats.len() == 1);
126
127             expr.node = ast::ExprKind::WhileLet(rewritten_pats.remove(0), cond, body, opt_ident);
128         }
129
130         ast::ExprKind::Loop(loop_block, opt_ident) => {
131             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
132             expr.node = ast::ExprKind::Loop(loop_block, opt_ident);
133         }
134
135         ast::ExprKind::ForLoop(pat, head, body, opt_ident) => {
136             let pat = fld.fold_pat(pat);
137
138             // Hygienic renaming of the for loop body (for loop binds its pattern).
139             let ((body, opt_ident), mut rewritten_pats) =
140                 rename_in_scope(vec![pat],
141                                 fld,
142                                 (body, opt_ident),
143                                 |rename_fld, fld, (body, opt_ident)| {
144                 expand_loop_block(rename_fld.fold_block(body), opt_ident, fld)
145             });
146             assert!(rewritten_pats.len() == 1);
147
148             let head = fld.fold_expr(head);
149             expr.node = ast::ExprKind::ForLoop(rewritten_pats.remove(0), head, body, opt_ident);
150         }
151
152         ast::ExprKind::IfLet(pat, sub_expr, body, else_opt) => {
153             let pat = fld.fold_pat(pat);
154
155             // Hygienic renaming of the body.
156             let (body, mut rewritten_pats) =
157                 rename_in_scope(vec![pat],
158                                 fld,
159                                 body,
160                                 |rename_fld, fld, body| {
161                 fld.fold_block(rename_fld.fold_block(body))
162             });
163             assert!(rewritten_pats.len() == 1);
164
165             let else_opt = else_opt.map(|else_opt| fld.fold_expr(else_opt));
166             let sub_expr = fld.fold_expr(sub_expr);
167             expr.node = ast::ExprKind::IfLet(rewritten_pats.remove(0), sub_expr, body, else_opt);
168         }
169
170         ast::ExprKind::Closure(capture_clause, fn_decl, block, fn_decl_span) => {
171             let (rewritten_fn_decl, rewritten_block)
172                 = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
173             expr.node = ast::ExprKind::Closure(capture_clause,
174                                                rewritten_fn_decl,
175                                                rewritten_block,
176                                                fn_decl_span);
177         }
178
179         _ => expr = noop_fold_expr(expr, fld),
180     };
181     P(expr)
182 }
183
184 /// Expand a macro invocation. Returns the result of expansion.
185 fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attribute>, span: Span,
186                        fld: &mut MacroExpander) -> T
187     where T: MacroGenerable,
188 {
189     // It would almost certainly be cleaner to pass the whole macro invocation in,
190     // rather than pulling it apart and marking the tts and the ctxt separately.
191     let Mac_ { path, tts, .. } = mac.node;
192     let mark = fresh_mark();
193
194     fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
195                       attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
196                       -> Option<Box<MacResult + 'a>> {
197         // Detect use of feature-gated or invalid attributes on macro invoations
198         // since they will not be detected after macro expansion.
199         for attr in attrs.iter() {
200             feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic,
201                                           &fld.cx.parse_sess.codemap(),
202                                           &fld.cx.ecfg.features.unwrap());
203         }
204
205         if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
206             fld.cx.span_err(path.span, "expected macro name without module separators");
207             return None;
208         }
209
210         let extname = path.segments[0].identifier.name;
211         let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) {
212             extension
213         } else {
214             let mut err = fld.cx.struct_span_err(path.span,
215                                                  &format!("macro undefined: '{}!'", &extname));
216             fld.cx.suggest_macro_name(&extname.as_str(), &mut err);
217             err.emit();
218             return None;
219         };
220
221         let ident = ident.unwrap_or(keywords::Invalid.ident());
222         match *extension {
223             NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
224                 if ident.name != keywords::Invalid.name() {
225                     let msg =
226                         format!("macro {}! expects no ident argument, given '{}'", extname, ident);
227                     fld.cx.span_err(path.span, &msg);
228                     return None;
229                 }
230
231                 fld.cx.bt_push(ExpnInfo {
232                     call_site: call_site,
233                     callee: NameAndSpan {
234                         format: MacroBang(extname),
235                         span: exp_span,
236                         allow_internal_unstable: allow_internal_unstable,
237                     },
238                 });
239
240                 let marked_tts = mark_tts(&tts, mark);
241                 Some(expandfun.expand(fld.cx, call_site, &marked_tts))
242             }
243
244             IdentTT(ref expander, tt_span, allow_internal_unstable) => {
245                 if ident.name == keywords::Invalid.name() {
246                     fld.cx.span_err(path.span,
247                                     &format!("macro {}! expects an ident argument", extname));
248                     return None;
249                 };
250
251                 fld.cx.bt_push(ExpnInfo {
252                     call_site: call_site,
253                     callee: NameAndSpan {
254                         format: MacroBang(extname),
255                         span: tt_span,
256                         allow_internal_unstable: allow_internal_unstable,
257                     }
258                 });
259
260                 let marked_tts = mark_tts(&tts, mark);
261                 Some(expander.expand(fld.cx, call_site, ident, marked_tts))
262             }
263
264             MacroRulesTT => {
265                 if ident.name == keywords::Invalid.name() {
266                     fld.cx.span_err(path.span,
267                                     &format!("macro {}! expects an ident argument", extname));
268                     return None;
269                 };
270
271                 fld.cx.bt_push(ExpnInfo {
272                     call_site: call_site,
273                     callee: NameAndSpan {
274                         format: MacroBang(extname),
275                         span: None,
276                         // `macro_rules!` doesn't directly allow unstable
277                         // (this is orthogonal to whether the macro it creates allows it)
278                         allow_internal_unstable: false,
279                     }
280                 });
281
282                 // DON'T mark before expansion.
283                 fld.cx.insert_macro(ast::MacroDef {
284                     ident: ident,
285                     id: ast::DUMMY_NODE_ID,
286                     span: call_site,
287                     imported_from: None,
288                     use_locally: true,
289                     body: tts,
290                     export: attr::contains_name(&attrs, "macro_export"),
291                     allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
292                     attrs: attrs,
293                 });
294
295                 // macro_rules! has a side effect but expands to nothing.
296                 fld.cx.bt_pop();
297                 None
298             }
299
300             MultiDecorator(..) | MultiModifier(..) => {
301                 fld.cx.span_err(path.span,
302                                 &format!("`{}` can only be used in attributes", extname));
303                 None
304             }
305         }
306     }
307
308     let opt_expanded = T::make_with(match mac_result(&path, ident, tts, mark, attrs, span, fld) {
309         Some(result) => result,
310         None => return T::dummy(span),
311     });
312
313     let expanded = if let Some(expanded) = opt_expanded {
314         expanded
315     } else {
316         let msg = format!("non-{kind} macro in {kind} position: {name}",
317                           name = path.segments[0].identifier.name, kind = T::kind_name());
318         fld.cx.span_err(path.span, &msg);
319         return T::dummy(span);
320     };
321
322     let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) });
323     let configured = marked.fold_with(&mut fld.strip_unconfigured());
324     fld.load_macros(&configured);
325     let fully_expanded = configured.fold_with(fld);
326     fld.cx.bt_pop();
327     fully_expanded
328 }
329
330 /// Rename loop label and expand its loop body
331 ///
332 /// The renaming procedure for loop is different in the sense that the loop
333 /// body is in a block enclosed by loop head so the renaming of loop label
334 /// must be propagated to the enclosed context.
335 fn expand_loop_block(loop_block: P<Block>,
336                      opt_ident: Option<SpannedIdent>,
337                      fld: &mut MacroExpander) -> (P<Block>, Option<SpannedIdent>) {
338     match opt_ident {
339         Some(label) => {
340             let new_label = fresh_name(label.node);
341             let rename = (label.node, new_label);
342
343             // The rename *must not* be added to the pending list of current
344             // syntax context otherwise an unrelated `break` or `continue` in
345             // the same context will pick that up in the deferred renaming pass
346             // and be renamed incorrectly.
347             let mut rename_list = vec!(rename);
348             let mut rename_fld = IdentRenamer{renames: &mut rename_list};
349             let renamed_ident = rename_fld.fold_ident(label.node);
350
351             // The rename *must* be added to the enclosed syntax context for
352             // `break` or `continue` to pick up because by definition they are
353             // in a block enclosed by loop head.
354             fld.cx.syntax_env.push_frame();
355             fld.cx.syntax_env.info().pending_renames.push(rename);
356             let expanded_block = expand_block_elts(loop_block, fld);
357             fld.cx.syntax_env.pop_frame();
358
359             (expanded_block, Some(Spanned { node: renamed_ident, span: label.span }))
360         }
361         None => (fld.fold_block(loop_block), opt_ident)
362     }
363 }
364
365 // eval $e with a new exts frame.
366 // must be a macro so that $e isn't evaluated too early.
367 macro_rules! with_exts_frame {
368     ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
369     ({$extsboxexpr.push_frame();
370       $extsboxexpr.info().macros_escape = $macros_escape;
371       let result = $e;
372       $extsboxexpr.pop_frame();
373       result
374      })
375 }
376
377 // When we enter a module, record it, for the sake of `module!`
378 pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
379                    -> SmallVector<P<ast::Item>> {
380     expand_annotatable(Annotatable::Item(it), fld)
381         .into_iter().map(|i| i.expect_item()).collect()
382 }
383
384 /// Expand item_kind
385 fn expand_item_kind(item: ast::ItemKind, fld: &mut MacroExpander) -> ast::ItemKind {
386     match item {
387         ast::ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
388             let (rewritten_fn_decl, rewritten_body)
389                 = expand_and_rename_fn_decl_and_block(decl, body, fld);
390             let expanded_generics = fold::noop_fold_generics(generics,fld);
391             ast::ItemKind::Fn(rewritten_fn_decl, unsafety, constness, abi,
392                         expanded_generics, rewritten_body)
393         }
394         _ => noop_fold_item_kind(item, fld)
395     }
396 }
397
398 // does this attribute list contain "macro_use" ?
399 fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
400     for attr in attrs {
401         let mut is_use = attr.check_name("macro_use");
402         if attr.check_name("macro_escape") {
403             let mut err =
404                 fld.cx.struct_span_warn(attr.span,
405                                         "macro_escape is a deprecated synonym for macro_use");
406             is_use = true;
407             if let ast::AttrStyle::Inner = attr.node.style {
408                 err.help("consider an outer attribute, \
409                           #[macro_use] mod ...").emit();
410             } else {
411                 err.emit();
412             }
413         };
414
415         if is_use {
416             match attr.node.value.node {
417                 ast::MetaItemKind::Word(..) => (),
418                 _ => fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"),
419             }
420             return true;
421         }
422     }
423     false
424 }
425
426 /// Expand a stmt
427 fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
428     // perform all pending renames
429     let stmt = {
430         let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
431         let mut rename_fld = IdentRenamer{renames:pending_renames};
432         rename_fld.fold_stmt(stmt).expect_one("rename_fold didn't return one value")
433     };
434
435     let (mac, style, attrs) = match stmt.node {
436         StmtKind::Mac(mac) => mac.unwrap(),
437         _ => return expand_non_macro_stmt(stmt, fld)
438     };
439
440     let mut fully_expanded: SmallVector<ast::Stmt> =
441         expand_mac_invoc(mac, None, attrs.into(), stmt.span, fld);
442
443     // If this is a macro invocation with a semicolon, then apply that
444     // semicolon to the final statement produced by expansion.
445     if style == MacStmtStyle::Semicolon {
446         if let Some(stmt) = fully_expanded.pop() {
447             fully_expanded.push(Stmt {
448                 id: stmt.id,
449                 node: match stmt.node {
450                     StmtKind::Expr(expr) => StmtKind::Semi(expr),
451                     _ => stmt.node /* might already have a semi */
452                 },
453                 span: stmt.span,
454             });
455         }
456     }
457
458     fully_expanded
459 }
460
461 // expand a non-macro stmt. this is essentially the fallthrough for
462 // expand_stmt, above.
463 fn expand_non_macro_stmt(stmt: Stmt, fld: &mut MacroExpander)
464                          -> SmallVector<Stmt> {
465     // is it a let?
466     match stmt.node {
467         StmtKind::Local(local) => {
468             // take it apart:
469             let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| {
470                 // expand the ty since TyKind::FixedLengthVec contains an Expr
471                 // and thus may have a macro use
472                 let expanded_ty = ty.map(|t| fld.fold_ty(t));
473                 // expand the pat (it might contain macro uses):
474                 let expanded_pat = fld.fold_pat(pat);
475                 // find the PatIdents in the pattern:
476                 // oh dear heaven... this is going to include the enum
477                 // names, as well... but that should be okay, as long as
478                 // the new names are gensyms for the old ones.
479                 // generate fresh names, push them to a new pending list
480                 let idents = pattern_bindings(&expanded_pat);
481                 let mut new_pending_renames =
482                     idents.iter().map(|ident| (*ident, fresh_name(*ident))).collect();
483                 // rewrite the pattern using the new names (the old
484                 // ones have already been applied):
485                 let rewritten_pat = {
486                     // nested binding to allow borrow to expire:
487                     let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
488                     rename_fld.fold_pat(expanded_pat)
489                 };
490                 // add them to the existing pending renames:
491                 fld.cx.syntax_env.info().pending_renames
492                       .extend(new_pending_renames);
493                 Local {
494                     id: id,
495                     ty: expanded_ty,
496                     pat: rewritten_pat,
497                     // also, don't forget to expand the init:
498                     init: init.map(|e| fld.fold_expr(e)),
499                     span: span,
500                     attrs: fold::fold_thin_attrs(attrs, fld),
501                 }
502             });
503             SmallVector::one(Stmt {
504                 id: stmt.id,
505                 node: StmtKind::Local(rewritten_local),
506                 span: stmt.span,
507             })
508         }
509         _ => noop_fold_stmt(stmt, fld),
510     }
511 }
512
513 // expand the arm of a 'match', renaming for macro hygiene
514 fn expand_arm(arm: ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
515     // expand pats... they might contain macro uses:
516     let expanded_pats = arm.pats.move_map(|pat| fld.fold_pat(pat));
517     if expanded_pats.is_empty() {
518         panic!("encountered match arm with 0 patterns");
519     }
520
521     // apply renaming and then expansion to the guard and the body:
522     let ((rewritten_guard, rewritten_body), rewritten_pats) =
523         rename_in_scope(expanded_pats,
524                         fld,
525                         (arm.guard, arm.body),
526                         |rename_fld, fld, (ag, ab)|{
527         let rewritten_guard = ag.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
528         let rewritten_body = fld.fold_expr(rename_fld.fold_expr(ab));
529         (rewritten_guard, rewritten_body)
530     });
531
532     ast::Arm {
533         attrs: fold::fold_attrs(arm.attrs, fld),
534         pats: rewritten_pats,
535         guard: rewritten_guard,
536         body: rewritten_body,
537     }
538 }
539
540 fn rename_in_scope<X, F>(pats: Vec<P<ast::Pat>>,
541                          fld: &mut MacroExpander,
542                          x: X,
543                          f: F)
544                          -> (X, Vec<P<ast::Pat>>)
545     where F: Fn(&mut IdentRenamer, &mut MacroExpander, X) -> X
546 {
547     // all of the pats must have the same set of bindings, so use the
548     // first one to extract them and generate new names:
549     let idents = pattern_bindings(&pats[0]);
550     let new_renames = idents.into_iter().map(|id| (id, fresh_name(id))).collect();
551     // apply the renaming, but only to the PatIdents:
552     let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
553     let rewritten_pats = pats.move_map(|pat| rename_pats_fld.fold_pat(pat));
554
555     let mut rename_fld = IdentRenamer{ renames:&new_renames };
556     (f(&mut rename_fld, fld, x), rewritten_pats)
557 }
558
559 /// A visitor that extracts the PatKind::Ident (binding) paths
560 /// from a given thingy and puts them in a mutable
561 /// array
562 #[derive(Clone)]
563 struct PatIdentFinder {
564     ident_accumulator: Vec<ast::Ident>
565 }
566
567 impl Visitor for PatIdentFinder {
568     fn visit_pat(&mut self, pattern: &ast::Pat) {
569         match *pattern {
570             ast::Pat { id: _, node: PatKind::Ident(_, ref path1, ref inner), span: _ } => {
571                 self.ident_accumulator.push(path1.node);
572                 // visit optional subpattern of PatKind::Ident:
573                 if let Some(ref subpat) = *inner {
574                     self.visit_pat(subpat)
575                 }
576             }
577             // use the default traversal for non-PatIdents
578             _ => visit::walk_pat(self, pattern)
579         }
580     }
581 }
582
583 /// find the PatKind::Ident paths in a pattern
584 fn pattern_bindings(pat: &ast::Pat) -> Vec<ast::Ident> {
585     let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
586     name_finder.visit_pat(pat);
587     name_finder.ident_accumulator
588 }
589
590 /// find the PatKind::Ident paths in a
591 fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
592     let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
593     for arg in &fn_decl.inputs {
594         pat_idents.visit_pat(&arg.pat);
595     }
596     pat_idents.ident_accumulator
597 }
598
599 // expand a block. pushes a new exts_frame, then calls expand_block_elts
600 pub fn expand_block(blk: P<Block>, fld: &mut MacroExpander) -> P<Block> {
601     // see note below about treatment of exts table
602     with_exts_frame!(fld.cx.syntax_env,false,
603                      expand_block_elts(blk, fld))
604 }
605
606 // expand the elements of a block.
607 pub fn expand_block_elts(b: P<Block>, fld: &mut MacroExpander) -> P<Block> {
608     b.map(|Block {id, stmts, rules, span}| {
609         let new_stmts = stmts.into_iter().flat_map(|x| {
610             // perform pending renames and expand macros in the statement
611             fld.fold_stmt(x).into_iter()
612         }).collect();
613         Block {
614             id: fld.new_id(id),
615             stmts: new_stmts,
616             rules: rules,
617             span: span
618         }
619     })
620 }
621
622 fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
623     match p.node {
624         PatKind::Mac(_) => {}
625         _ => return noop_fold_pat(p, fld)
626     }
627     p.and_then(|ast::Pat {node, span, ..}| {
628         match node {
629             PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld),
630             _ => unreachable!()
631         }
632     })
633 }
634
635 /// A tree-folder that applies every rename in its (mutable) list
636 /// to every identifier, including both bindings and varrefs
637 /// (and lots of things that will turn out to be neither)
638 pub struct IdentRenamer<'a> {
639     renames: &'a mtwt::RenameList,
640 }
641
642 impl<'a> Folder for IdentRenamer<'a> {
643     fn fold_ident(&mut self, id: Ident) -> Ident {
644         mtwt::apply_renames(self.renames, id)
645     }
646     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
647         fold::noop_fold_mac(mac, self)
648     }
649 }
650
651 /// A tree-folder that applies every rename in its list to
652 /// the idents that are in PatKind::Ident patterns. This is more narrowly
653 /// focused than IdentRenamer, and is needed for FnDecl,
654 /// where we want to rename the args but not the fn name or the generics etc.
655 pub struct PatIdentRenamer<'a> {
656     renames: &'a mtwt::RenameList,
657 }
658
659 impl<'a> Folder for PatIdentRenamer<'a> {
660     fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
661         match pat.node {
662             PatKind::Ident(..) => {},
663             _ => return noop_fold_pat(pat, self)
664         }
665
666         pat.map(|ast::Pat {id, node, span}| match node {
667             PatKind::Ident(binding_mode, Spanned{span: sp, node: ident}, sub) => {
668                 let new_ident = mtwt::apply_renames(self.renames, ident);
669                 let new_node =
670                     PatKind::Ident(binding_mode,
671                                   Spanned{span: sp, node: new_ident},
672                                   sub.map(|p| self.fold_pat(p)));
673                 ast::Pat {
674                     id: id,
675                     node: new_node,
676                     span: span,
677                 }
678             },
679             _ => unreachable!()
680         })
681     }
682     fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
683         fold::noop_fold_mac(mac, self)
684     }
685 }
686
687 fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
688     match a {
689         Annotatable::Item(it) => match it.node {
690             ast::ItemKind::Mac(..) => {
691                 it.and_then(|it| match it.node {
692                     ItemKind::Mac(mac) =>
693                         expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld),
694                     _ => unreachable!(),
695                 })
696             }
697             ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => {
698                 let valid_ident =
699                     it.ident.name != keywords::Invalid.name();
700
701                 if valid_ident {
702                     fld.cx.mod_push(it.ident);
703                 }
704                 let macro_use = contains_macro_use(fld, &it.attrs);
705                 let result = with_exts_frame!(fld.cx.syntax_env,
706                                               macro_use,
707                                               noop_fold_item(it, fld));
708                 if valid_ident {
709                     fld.cx.mod_pop();
710                 }
711                 result
712             },
713             _ => noop_fold_item(it, fld),
714         }.into_iter().map(|i| Annotatable::Item(i)).collect(),
715
716         Annotatable::TraitItem(it) => {
717             expand_trait_item(it.unwrap(), fld).into_iter().
718                 map(|it| Annotatable::TraitItem(P(it))).collect()
719         }
720
721         Annotatable::ImplItem(ii) => {
722             expand_impl_item(ii.unwrap(), fld).into_iter().
723                 map(|ii| Annotatable::ImplItem(P(ii))).collect()
724         }
725     }
726 }
727
728 fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
729     let mut multi_modifier = None;
730     item = item.map_attrs(|mut attrs| {
731         for i in 0..attrs.len() {
732             if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
733                 match *extension {
734                     MultiModifier(..) | MultiDecorator(..) => {
735                         multi_modifier = Some((attrs.remove(i), extension));
736                         break;
737                     }
738                     _ => {}
739                 }
740             }
741         }
742         attrs
743     });
744
745     match multi_modifier {
746         None => expand_multi_modified(item, fld),
747         Some((attr, extension)) => {
748             attr::mark_used(&attr);
749             fld.cx.bt_push(ExpnInfo {
750                 call_site: attr.span,
751                 callee: NameAndSpan {
752                     format: MacroAttribute(intern(&attr.name())),
753                     span: Some(attr.span),
754                     // attributes can do whatever they like, for now
755                     allow_internal_unstable: true,
756                 }
757             });
758
759             let modified = match *extension {
760                 MultiModifier(ref mac) => mac.expand(fld.cx, attr.span, &attr.node.value, item),
761                 MultiDecorator(ref mac) => {
762                     let mut items = Vec::new();
763                     mac.expand(fld.cx, attr.span, &attr.node.value, &item,
764                                &mut |item| items.push(item));
765                     items.push(item);
766                     items
767                 }
768                 _ => unreachable!(),
769             };
770
771             fld.cx.bt_pop();
772             let configured = modified.into_iter().flat_map(|it| {
773                 it.fold_with(&mut fld.strip_unconfigured())
774             }).collect::<SmallVector<_>>();
775
776             configured.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
777         }
778     }
779 }
780
781 fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
782                  -> SmallVector<ast::ImplItem> {
783     match ii.node {
784         ast::ImplItemKind::Method(..) => SmallVector::one(ast::ImplItem {
785             id: ii.id,
786             ident: ii.ident,
787             attrs: ii.attrs,
788             vis: ii.vis,
789             defaultness: ii.defaultness,
790             node: match ii.node {
791                 ast::ImplItemKind::Method(sig, body) => {
792                     let (sig, body) = expand_and_rename_method(sig, body, fld);
793                     ast::ImplItemKind::Method(sig, body)
794                 }
795                 _ => unreachable!()
796             },
797             span: ii.span,
798         }),
799         ast::ImplItemKind::Macro(mac) => {
800             expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
801         }
802         _ => fold::noop_fold_impl_item(ii, fld)
803     }
804 }
805
806 fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
807                      -> SmallVector<ast::TraitItem> {
808     match ti.node {
809         ast::TraitItemKind::Method(_, Some(_)) => {
810             SmallVector::one(ast::TraitItem {
811                 id: ti.id,
812                 ident: ti.ident,
813                 attrs: ti.attrs,
814                 node: match ti.node  {
815                     ast::TraitItemKind::Method(sig, Some(body)) => {
816                         let (sig, body) = expand_and_rename_method(sig, body, fld);
817                         ast::TraitItemKind::Method(sig, Some(body))
818                     }
819                     _ => unreachable!()
820                 },
821                 span: ti.span,
822             })
823         }
824         ast::TraitItemKind::Macro(mac) => {
825             expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
826         }
827         _ => fold::noop_fold_trait_item(ti, fld)
828     }
829 }
830
831 /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
832 /// PatIdents in its arguments to perform renaming in the FnDecl and
833 /// the block, returning both the new FnDecl and the new Block.
834 fn expand_and_rename_fn_decl_and_block(fn_decl: P<ast::FnDecl>, block: P<ast::Block>,
835                                        fld: &mut MacroExpander)
836                                        -> (P<ast::FnDecl>, P<ast::Block>) {
837     let expanded_decl = fld.fold_fn_decl(fn_decl);
838     let idents = fn_decl_arg_bindings(&expanded_decl);
839     let renames =
840         idents.iter().map(|id| (*id,fresh_name(*id))).collect();
841     // first, a renamer for the PatIdents, for the fn_decl:
842     let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
843     let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
844     // now, a renamer for *all* idents, for the body:
845     let mut rename_fld = IdentRenamer{renames: &renames};
846     let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
847     (rewritten_fn_decl,rewritten_body)
848 }
849
850 fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
851                             fld: &mut MacroExpander)
852                             -> (ast::MethodSig, P<ast::Block>) {
853     let (rewritten_fn_decl, rewritten_body)
854         = expand_and_rename_fn_decl_and_block(sig.decl, body, fld);
855     (ast::MethodSig {
856         generics: fld.fold_generics(sig.generics),
857         abi: sig.abi,
858         unsafety: sig.unsafety,
859         constness: sig.constness,
860         decl: rewritten_fn_decl
861     }, rewritten_body)
862 }
863
864 pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
865     let t = match t.node.clone() {
866         ast::TyKind::Mac(mac) => {
867             if fld.cx.ecfg.features.unwrap().type_macros {
868                 expand_mac_invoc(mac, None, Vec::new(), t.span, fld)
869             } else {
870                 feature_gate::emit_feature_err(
871                     &fld.cx.parse_sess.span_diagnostic,
872                     "type_macros",
873                     t.span,
874                     feature_gate::GateIssue::Language,
875                     "type macros are experimental");
876
877                 DummyResult::raw_ty(t.span)
878             }
879         }
880         _ => t
881     };
882
883     fold::noop_fold_ty(t, fld)
884 }
885
886 /// A tree-folder that performs macro expansion
887 pub struct MacroExpander<'a, 'b:'a> {
888     pub cx: &'a mut ExtCtxt<'b>,
889 }
890
891 impl<'a, 'b> MacroExpander<'a, 'b> {
892     pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
893         MacroExpander { cx: cx }
894     }
895
896     fn strip_unconfigured(&mut self) -> StripUnconfigured {
897         StripUnconfigured {
898             config: &self.cx.cfg,
899             should_test: self.cx.ecfg.should_test,
900             sess: self.cx.parse_sess,
901             features: self.cx.ecfg.features,
902         }
903     }
904
905     fn load_macros<T: MacroGenerable>(&mut self, node: &T) {
906         struct MacroLoadingVisitor<'a, 'b: 'a>{
907             cx: &'a mut ExtCtxt<'b>,
908             at_crate_root: bool,
909         }
910
911         impl<'a, 'b> Visitor for MacroLoadingVisitor<'a, 'b> {
912             fn visit_mac(&mut self, _: &ast::Mac) {}
913             fn visit_item(&mut self, item: &ast::Item) {
914                 if let ast::ItemKind::ExternCrate(..) = item.node {
915                     // We need to error on `#[macro_use] extern crate` when it isn't at the
916                     // crate root, because `$crate` won't work properly.
917                     for def in self.cx.loader.load_crate(item, self.at_crate_root) {
918                         self.cx.insert_macro(def);
919                     }
920                 } else {
921                     let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
922                     visit::walk_item(self, item);
923                     self.at_crate_root = at_crate_root;
924                 }
925             }
926             fn visit_block(&mut self, block: &ast::Block) {
927                 let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
928                 visit::walk_block(self, block);
929                 self.at_crate_root = at_crate_root;
930             }
931         }
932
933         node.visit_with(&mut MacroLoadingVisitor {
934             at_crate_root: self.cx.syntax_env.is_crate_root(),
935             cx: self.cx,
936         });
937     }
938 }
939
940 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
941     fn fold_crate(&mut self, c: Crate) -> Crate {
942         self.cx.filename = Some(self.cx.parse_sess.codemap().span_to_filename(c.span));
943         noop_fold_crate(c, self)
944     }
945
946     fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
947         expr.and_then(|expr| expand_expr(expr, self))
948     }
949
950     fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
951         expr.and_then(|expr| match expr.node {
952             ast::ExprKind::Mac(mac) =>
953                 expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, self),
954             _ => Some(expand_expr(expr, self)),
955         })
956     }
957
958     fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
959         expand_pat(pat, self)
960     }
961
962     fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
963         use std::mem::replace;
964         let result;
965         if let ast::ItemKind::Mod(ast::Mod { inner, .. }) = item.node {
966             if item.span.contains(inner) {
967                 self.push_mod_path(item.ident, &item.attrs);
968                 result = expand_item(item, self);
969                 self.pop_mod_path();
970             } else {
971                 let filename = if inner != syntax_pos::DUMMY_SP {
972                     Some(self.cx.parse_sess.codemap().span_to_filename(inner))
973                 } else { None };
974                 let orig_filename = replace(&mut self.cx.filename, filename);
975                 let orig_mod_path_stack = replace(&mut self.cx.mod_path_stack, Vec::new());
976                 result = expand_item(item, self);
977                 self.cx.filename = orig_filename;
978                 self.cx.mod_path_stack = orig_mod_path_stack;
979             }
980         } else {
981             result = expand_item(item, self);
982         }
983         result
984     }
985
986     fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
987         expand_item_kind(item, self)
988     }
989
990     fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
991         expand_stmt(stmt, self)
992     }
993
994     fn fold_block(&mut self, block: P<Block>) -> P<Block> {
995         let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true);
996         let result = expand_block(block, self);
997         self.cx.in_block = was_in_block;
998         result
999     }
1000
1001     fn fold_arm(&mut self, arm: ast::Arm) -> ast::Arm {
1002         expand_arm(arm, self)
1003     }
1004
1005     fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
1006         expand_annotatable(Annotatable::TraitItem(P(i)), self)
1007             .into_iter().map(|i| i.expect_trait_item()).collect()
1008     }
1009
1010     fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
1011         expand_annotatable(Annotatable::ImplItem(P(i)), self)
1012             .into_iter().map(|i| i.expect_impl_item()).collect()
1013     }
1014
1015     fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
1016         expand_type(ty, self)
1017     }
1018 }
1019
1020 impl<'a, 'b> MacroExpander<'a, 'b> {
1021     fn push_mod_path(&mut self, id: Ident, attrs: &[ast::Attribute]) {
1022         let default_path = id.name.as_str();
1023         let file_path = match ::attr::first_attr_value_str_by_name(attrs, "path") {
1024             Some(d) => d,
1025             None => default_path,
1026         };
1027         self.cx.mod_path_stack.push(file_path)
1028     }
1029
1030     fn pop_mod_path(&mut self) {
1031         self.cx.mod_path_stack.pop().unwrap();
1032     }
1033 }
1034
1035 pub struct ExpansionConfig<'feat> {
1036     pub crate_name: String,
1037     pub features: Option<&'feat Features>,
1038     pub recursion_limit: usize,
1039     pub trace_mac: bool,
1040     pub should_test: bool, // If false, strip `#[test]` nodes
1041 }
1042
1043 macro_rules! feature_tests {
1044     ($( fn $getter:ident = $field:ident, )*) => {
1045         $(
1046             pub fn $getter(&self) -> bool {
1047                 match self.features {
1048                     Some(&Features { $field: true, .. }) => true,
1049                     _ => false,
1050                 }
1051             }
1052         )*
1053     }
1054 }
1055
1056 impl<'feat> ExpansionConfig<'feat> {
1057     pub fn default(crate_name: String) -> ExpansionConfig<'static> {
1058         ExpansionConfig {
1059             crate_name: crate_name,
1060             features: None,
1061             recursion_limit: 64,
1062             trace_mac: false,
1063             should_test: false,
1064         }
1065     }
1066
1067     feature_tests! {
1068         fn enable_quotes = quote,
1069         fn enable_asm = asm,
1070         fn enable_log_syntax = log_syntax,
1071         fn enable_concat_idents = concat_idents,
1072         fn enable_trace_macros = trace_macros,
1073         fn enable_allow_internal_unstable = allow_internal_unstable,
1074         fn enable_custom_derive = custom_derive,
1075         fn enable_pushpop_unsafe = pushpop_unsafe,
1076     }
1077 }
1078
1079 pub fn expand_crate(mut cx: ExtCtxt,
1080                     user_exts: Vec<NamedSyntaxExtension>,
1081                     mut c: Crate) -> (Crate, HashSet<Name>) {
1082     if std_inject::no_core(&c) {
1083         cx.crate_root = None;
1084     } else if std_inject::no_std(&c) {
1085         cx.crate_root = Some("core");
1086     } else {
1087         cx.crate_root = Some("std");
1088     }
1089     let ret = {
1090         let mut expander = MacroExpander::new(&mut cx);
1091
1092         for (name, extension) in user_exts {
1093             expander.cx.syntax_env.insert(name, extension);
1094         }
1095
1096         let items = SmallVector::many(c.module.items);
1097         expander.load_macros(&items);
1098         c.module.items = items.into();
1099
1100         let err_count = cx.parse_sess.span_diagnostic.err_count();
1101         let mut ret = expander.fold_crate(c);
1102         ret.exported_macros = expander.cx.exported_macros.clone();
1103
1104         if cx.parse_sess.span_diagnostic.err_count() > err_count {
1105             cx.parse_sess.span_diagnostic.abort_if_errors();
1106         }
1107
1108         ret
1109     };
1110     return (ret, cx.syntax_env.names);
1111 }
1112
1113 // HYGIENIC CONTEXT EXTENSION:
1114 // all of these functions are for walking over
1115 // ASTs and making some change to the context of every
1116 // element that has one. a CtxtFn is a trait-ified
1117 // version of a closure in (SyntaxContext -> SyntaxContext).
1118 // the ones defined here include:
1119 // Marker - add a mark to a context
1120
1121 // A Marker adds the given mark to the syntax context and
1122 // sets spans' `expn_id` to the given expn_id (unless it is `None`).
1123 struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
1124
1125 impl Folder for Marker {
1126     fn fold_ident(&mut self, id: Ident) -> Ident {
1127         ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt))
1128     }
1129     fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
1130         Spanned {
1131             node: Mac_ {
1132                 path: self.fold_path(node.path),
1133                 tts: self.fold_tts(&node.tts),
1134             },
1135             span: self.new_span(span),
1136         }
1137     }
1138
1139     fn new_span(&mut self, mut span: Span) -> Span {
1140         if let Some(expn_id) = self.expn_id {
1141             span.expn_id = expn_id;
1142         }
1143         span
1144     }
1145 }
1146
1147 // apply a given mark to the given token trees. Used prior to expansion of a macro.
1148 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
1149     noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
1150 }
1151
1152
1153 #[cfg(test)]
1154 mod tests {
1155     use super::{pattern_bindings, expand_crate};
1156     use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
1157     use ast;
1158     use ast::Name;
1159     use syntax_pos;
1160     use ext::base::{ExtCtxt, DummyMacroLoader};
1161     use ext::mtwt;
1162     use fold::Folder;
1163     use parse;
1164     use parse::token;
1165     use util::parser_testing::{string_to_parser};
1166     use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
1167     use visit;
1168     use visit::Visitor;
1169
1170     // a visitor that extracts the paths
1171     // from a given thingy and puts them in a mutable
1172     // array (passed in to the traversal)
1173     #[derive(Clone)]
1174     struct PathExprFinderContext {
1175         path_accumulator: Vec<ast::Path> ,
1176     }
1177
1178     impl Visitor for PathExprFinderContext {
1179         fn visit_expr(&mut self, expr: &ast::Expr) {
1180             if let ast::ExprKind::Path(None, ref p) = expr.node {
1181                 self.path_accumulator.push(p.clone());
1182             }
1183             visit::walk_expr(self, expr);
1184         }
1185     }
1186
1187     // find the variable references in a crate
1188     fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
1189         let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
1190         visit::walk_crate(&mut path_finder, the_crate);
1191         path_finder.path_accumulator
1192     }
1193
1194     /// A Visitor that extracts the identifiers from a thingy.
1195     // as a side note, I'm starting to want to abstract over these....
1196     struct IdentFinder {
1197         ident_accumulator: Vec<ast::Ident>
1198     }
1199
1200     impl Visitor for IdentFinder {
1201         fn visit_ident(&mut self, _: syntax_pos::Span, id: ast::Ident){
1202             self.ident_accumulator.push(id);
1203         }
1204     }
1205
1206     /// Find the idents in a crate
1207     fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
1208         let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
1209         visit::walk_crate(&mut ident_finder, the_crate);
1210         ident_finder.ident_accumulator
1211     }
1212
1213     // these following tests are quite fragile, in that they don't test what
1214     // *kind* of failure occurs.
1215
1216     fn test_ecfg() -> ExpansionConfig<'static> {
1217         ExpansionConfig::default("test".to_string())
1218     }
1219
1220     // make sure that macros can't escape fns
1221     #[should_panic]
1222     #[test] fn macros_cant_escape_fns_test () {
1223         let src = "fn bogus() {macro_rules! z (() => (3+4));}\
1224                    fn inty() -> i32 { z!() }".to_string();
1225         let sess = parse::ParseSess::new();
1226         let crate_ast = parse::parse_crate_from_source_str(
1227             "<test>".to_string(),
1228             src,
1229             Vec::new(), &sess).unwrap();
1230         // should fail:
1231         let mut loader = DummyMacroLoader;
1232         let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1233         expand_crate(ecx, vec![], crate_ast);
1234     }
1235
1236     // make sure that macros can't escape modules
1237     #[should_panic]
1238     #[test] fn macros_cant_escape_mods_test () {
1239         let src = "mod foo {macro_rules! z (() => (3+4));}\
1240                    fn inty() -> i32 { z!() }".to_string();
1241         let sess = parse::ParseSess::new();
1242         let crate_ast = parse::parse_crate_from_source_str(
1243             "<test>".to_string(),
1244             src,
1245             Vec::new(), &sess).unwrap();
1246         let mut loader = DummyMacroLoader;
1247         let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1248         expand_crate(ecx, vec![], crate_ast);
1249     }
1250
1251     // macro_use modules should allow macros to escape
1252     #[test] fn macros_can_escape_flattened_mods_test () {
1253         let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
1254                    fn inty() -> i32 { z!() }".to_string();
1255         let sess = parse::ParseSess::new();
1256         let crate_ast = parse::parse_crate_from_source_str(
1257             "<test>".to_string(),
1258             src,
1259             Vec::new(), &sess).unwrap();
1260         let mut loader = DummyMacroLoader;
1261         let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1262         expand_crate(ecx, vec![], crate_ast);
1263     }
1264
1265     fn expand_crate_str(crate_str: String) -> ast::Crate {
1266         let ps = parse::ParseSess::new();
1267         let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
1268         // the cfg argument actually does matter, here...
1269         let mut loader = DummyMacroLoader;
1270         let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
1271         expand_crate(ecx, vec![], crate_ast).0
1272     }
1273
1274     // find the pat_ident paths in a crate
1275     fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
1276         let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
1277         visit::walk_crate(&mut name_finder, the_crate);
1278         name_finder.ident_accumulator
1279     }
1280
1281     #[test] fn macro_tokens_should_match(){
1282         expand_crate_str(
1283             "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
1284     }
1285
1286     // should be able to use a bound identifier as a literal in a macro definition:
1287     #[test] fn self_macro_parsing(){
1288         expand_crate_str(
1289             "macro_rules! foo ((zz) => (287;));
1290             fn f(zz: i32) {foo!(zz);}".to_string()
1291             );
1292     }
1293
1294     // create a really evil test case where a $x appears inside a binding of $x
1295     // but *shouldn't* bind because it was inserted by a different macro....
1296     // can't write this test case until we have macro-generating macros.
1297
1298     #[test]
1299     fn fmt_in_macro_used_inside_module_macro() {
1300         let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
1301 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
1302 foo_module!();
1303 ".to_string();
1304         let cr = expand_crate_str(crate_str);
1305         // find the xx binding
1306         let bindings = crate_bindings(&cr);
1307         let cxbinds: Vec<&ast::Ident> =
1308             bindings.iter().filter(|b| b.name.as_str() == "xx").collect();
1309         let cxbinds: &[&ast::Ident] = &cxbinds[..];
1310         let cxbind = match (cxbinds.len(), cxbinds.get(0)) {
1311             (1, Some(b)) => *b,
1312             _ => panic!("expected just one binding for ext_cx")
1313         };
1314         let resolved_binding = mtwt::resolve(*cxbind);
1315         let varrefs = crate_varrefs(&cr);
1316
1317         // the xx binding should bind all of the xx varrefs:
1318         for (idx,v) in varrefs.iter().filter(|p| {
1319             p.segments.len() == 1
1320             && p.segments[0].identifier.name.as_str() == "xx"
1321         }).enumerate() {
1322             if mtwt::resolve(v.segments[0].identifier) != resolved_binding {
1323                 println!("uh oh, xx binding didn't match xx varref:");
1324                 println!("this is xx varref \\# {}", idx);
1325                 println!("binding: {}", cxbind);
1326                 println!("resolves to: {}", resolved_binding);
1327                 println!("varref: {}", v.segments[0].identifier);
1328                 println!("resolves to: {}",
1329                          mtwt::resolve(v.segments[0].identifier));
1330                 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1331             }
1332             assert_eq!(mtwt::resolve(v.segments[0].identifier),
1333                        resolved_binding);
1334         };
1335     }
1336
1337     #[test]
1338     fn pat_idents(){
1339         let pat = string_to_pat(
1340             "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
1341         let idents = pattern_bindings(&pat);
1342         assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
1343     }
1344
1345     // test the list of identifier patterns gathered by the visitor. Note that
1346     // 'None' is listed as an identifier pattern because we don't yet know that
1347     // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
1348     #[test]
1349     fn crate_bindings_test(){
1350         let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| {
1351         match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
1352         let idents = crate_bindings(&the_crate);
1353         assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
1354     }
1355
1356     // test the IdentRenamer directly
1357     #[test]
1358     fn ident_renamer_test () {
1359         let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1360         let f_ident = token::str_to_ident("f");
1361         let x_ident = token::str_to_ident("x");
1362         let int_ident = token::str_to_ident("i32");
1363         let renames = vec!((x_ident,Name(16)));
1364         let mut renamer = IdentRenamer{renames: &renames};
1365         let renamed_crate = renamer.fold_crate(the_crate);
1366         let idents = crate_idents(&renamed_crate);
1367         let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1368         assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)]);
1369     }
1370
1371     // test the PatIdentRenamer; only PatIdents get renamed
1372     #[test]
1373     fn pat_ident_renamer_test () {
1374         let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1375         let f_ident = token::str_to_ident("f");
1376         let x_ident = token::str_to_ident("x");
1377         let int_ident = token::str_to_ident("i32");
1378         let renames = vec!((x_ident,Name(16)));
1379         let mut renamer = PatIdentRenamer{renames: &renames};
1380         let renamed_crate = renamer.fold_crate(the_crate);
1381         let idents = crate_idents(&renamed_crate);
1382         let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1383         let x_name = x_ident.name;
1384         assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name]);
1385     }
1386 }