]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
Ignore tests broken by failing on ICE
[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::{P, Block, Crate, DeclLocal, ExprMac};
12 use ast::{Local, Ident, MacInvocTT};
13 use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
14 use ast::TokenTree;
15 use ast;
16 use ext::mtwt;
17 use ext::build::AstBuilder;
18 use attr;
19 use attr::AttrMetaMethods;
20 use codemap;
21 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
22 use crateid::CrateId;
23 use ext::base::*;
24 use fold::*;
25 use parse;
26 use parse::token::{fresh_mark, fresh_name, intern};
27 use parse::token;
28 use visit;
29 use visit::Visitor;
30 use util::small_vector::SmallVector;
31
32 use std::cast;
33 use std::unstable::dynamic_lib::DynamicLibrary;
34 use std::os;
35
36 pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
37     match e.node {
38         // expr_mac should really be expr_ext or something; it's the
39         // entry-point for all syntax extensions.
40         ExprMac(ref mac) => {
41             match (*mac).node {
42                 // it would almost certainly be cleaner to pass the whole
43                 // macro invocation in, rather than pulling it apart and
44                 // marking the tts and the ctxt separately. This also goes
45                 // for the other three macro invocation chunks of code
46                 // in this file.
47                 // Token-tree macros:
48                 MacInvocTT(ref pth, ref tts, _) => {
49                     if pth.segments.len() > 1u {
50                         fld.cx.span_err(
51                             pth.span,
52                             format!("expected macro name without module \
53                                   separators"));
54                         // let compilation continue
55                         return DummyResult::raw_expr(e.span);
56                     }
57                     let extname = pth.segments.get(0).identifier;
58                     let extnamestr = token::get_ident(extname);
59                     // leaving explicit deref here to highlight unbox op:
60                     let marked_after = match fld.extsbox.find(&extname.name) {
61                         None => {
62                             fld.cx.span_err(
63                                 pth.span,
64                                 format!("macro undefined: '{}'",
65                                         extnamestr.get()));
66
67                             // let compilation continue
68                             return DummyResult::raw_expr(e.span);
69                         }
70                         Some(&NormalTT(ref expandfun, exp_span)) => {
71                             fld.cx.bt_push(ExpnInfo {
72                                 call_site: e.span,
73                                 callee: NameAndSpan {
74                                     name: extnamestr.get().to_str(),
75                                     format: MacroBang,
76                                     span: exp_span,
77                                 },
78                             });
79                             let fm = fresh_mark();
80                             // mark before:
81                             let marked_before = mark_tts(tts.as_slice(), fm);
82
83                             // The span that we pass to the expanders we want to
84                             // be the root of the call stack. That's the most
85                             // relevant span and it's the actual invocation of
86                             // the macro.
87                             let mac_span = original_span(fld.cx);
88
89                             let expanded = match expandfun.expand(fld.cx,
90                                                    mac_span.call_site,
91                                                    marked_before.as_slice()).make_expr() {
92                                 Some(e) => e,
93                                 None => {
94                                     fld.cx.span_err(
95                                         pth.span,
96                                         format!(
97                                             "non-expr macro in expr pos: {}",
98                                             extnamestr.get()
99                                         )
100                                     );
101                                     return DummyResult::raw_expr(e.span);
102                                 }
103                             };
104
105                             // mark after:
106                             mark_expr(expanded,fm)
107                         }
108                         _ => {
109                             fld.cx.span_err(
110                                 pth.span,
111                                 format!("'{}' is not a tt-style macro",
112                                         extnamestr.get())
113                             );
114                             return DummyResult::raw_expr(e.span);
115                         }
116                     };
117
118                     // Keep going, outside-in.
119                     //
120                     // FIXME(pcwalton): Is it necessary to clone the
121                     // node here?
122                     let fully_expanded =
123                         fld.fold_expr(marked_after).node.clone();
124                     fld.cx.bt_pop();
125
126                     @ast::Expr {
127                         id: ast::DUMMY_NODE_ID,
128                         node: fully_expanded,
129                         span: e.span,
130                     }
131                 }
132             }
133         }
134
135         // Desugar expr_for_loop
136         // From: `['<ident>:] for <src_pat> in <src_expr> <src_loop_block>`
137         // FIXME #6993: change type of opt_ident to Option<Name>
138         ast::ExprForLoop(src_pat, src_expr, src_loop_block, opt_ident) => {
139             // Expand any interior macros etc.
140             // NB: we don't fold pats yet. Curious.
141             let src_expr = fld.fold_expr(src_expr).clone();
142             let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
143
144             let span = e.span;
145
146             // to:
147             //
148             //   match &mut <src_expr> {
149             //     i => {
150             //       ['<ident>:] loop {
151             //         match i.next() {
152             //           None => break,
153             //           Some(<src_pat>) => <src_loop_block>
154             //         }
155             //       }
156             //     }
157             //   }
158
159             let local_ident = token::gensym_ident("i");
160             let next_ident = fld.cx.ident_of("next");
161             let none_ident = fld.cx.ident_of("None");
162
163             let local_path = fld.cx.path_ident(span, local_ident);
164             let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some"));
165
166             // `None => break ['<ident>];`
167             let none_arm = {
168                 let break_expr = fld.cx.expr(span, ast::ExprBreak(opt_ident));
169                 let none_pat = fld.cx.pat_ident(span, none_ident);
170                 fld.cx.arm(span, vec!(none_pat), break_expr)
171             };
172
173             // `Some(<src_pat>) => <src_loop_block>`
174             let some_arm =
175                 fld.cx.arm(span,
176                            vec!(fld.cx.pat_enum(span, some_path, vec!(src_pat))),
177                            fld.cx.expr_block(src_loop_block));
178
179             // `match i.next() { ... }`
180             let match_expr = {
181                 let next_call_expr =
182                     fld.cx.expr_method_call(span,
183                                             fld.cx.expr_path(local_path),
184                                             next_ident,
185                                             Vec::new());
186
187                 fld.cx.expr_match(span, next_call_expr, vec!(none_arm, some_arm))
188             };
189
190             // ['ident:] loop { ... }
191             let loop_expr = fld.cx.expr(span,
192                                         ast::ExprLoop(fld.cx.block_expr(match_expr),
193                                                       opt_ident));
194
195             // `i => loop { ... }`
196
197             // `match &mut <src_expr> { i => loop { ... } }`
198             let discrim = fld.cx.expr_mut_addr_of(span, src_expr);
199             let i_pattern = fld.cx.pat_ident(span, local_ident);
200             let arm = fld.cx.arm(span, vec!(i_pattern), loop_expr);
201             fld.cx.expr_match(span, discrim, vec!(arm))
202         }
203
204         ast::ExprLoop(loop_block, opt_ident) => {
205             let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
206             fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
207         }
208
209         _ => noop_fold_expr(e, fld)
210     }
211 }
212
213 // Rename loop label and expand its loop body
214 //
215 // The renaming procedure for loop is different in the sense that the loop
216 // body is in a block enclosed by loop head so the renaming of loop label
217 // must be propagated to the enclosed context.
218 fn expand_loop_block(loop_block: P<Block>,
219                      opt_ident: Option<Ident>,
220                      fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
221     match opt_ident {
222         Some(label) => {
223             let new_label = fresh_name(&label);
224             let rename = (label, new_label);
225
226             // The rename *must not* be added to the pending list of current
227             // syntax context otherwise an unrelated `break` or `continue` in
228             // the same context will pick that up in the deferred renaming pass
229             // and be renamed incorrectly.
230             let mut rename_list = vec!(rename);
231             let mut rename_fld = renames_to_fold(&mut rename_list);
232             let renamed_ident = rename_fld.fold_ident(label);
233
234             // The rename *must* be added to the enclosed syntax context for
235             // `break` or `continue` to pick up because by definition they are
236             // in a block enclosed by loop head.
237             fld.extsbox.push_frame();
238             fld.extsbox.info().pending_renames.push(rename);
239             let expanded_block = expand_block_elts(loop_block, fld);
240             fld.extsbox.pop_frame();
241
242             (expanded_block, Some(renamed_ident))
243         }
244         None => (fld.fold_block(loop_block), opt_ident)
245     }
246 }
247
248 // eval $e with a new exts frame:
249 macro_rules! with_exts_frame (
250     ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
251     ({$extsboxexpr.push_frame();
252       $extsboxexpr.info().macros_escape = $macros_escape;
253       let result = $e;
254       $extsboxexpr.pop_frame();
255       result
256      })
257 )
258
259 // When we enter a module, record it, for the sake of `module!`
260 pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
261                    -> SmallVector<@ast::Item> {
262     let it = expand_item_modifiers(it, fld);
263
264     let mut decorator_items = SmallVector::zero();
265     for attr in it.attrs.iter().rev() {
266         let mname = attr.name();
267
268         match fld.extsbox.find(&intern(mname.get())) {
269             Some(&ItemDecorator(dec_fn)) => {
270                 fld.cx.bt_push(ExpnInfo {
271                     call_site: attr.span,
272                     callee: NameAndSpan {
273                         name: mname.get().to_str(),
274                         format: MacroAttribute,
275                         span: None
276                     }
277                 });
278
279                 // we'd ideally decorator_items.push_all(expand_item(item, fld)),
280                 // but that double-mut-borrows fld
281                 let mut items: SmallVector<@ast::Item> = SmallVector::zero();
282                 dec_fn(fld.cx, attr.span, attr.node.value, it,
283                        |item| items.push(item));
284                 decorator_items.extend(items.move_iter()
285                     .flat_map(|item| expand_item(item, fld).move_iter()));
286
287                 fld.cx.bt_pop();
288             }
289             _ => {}
290         }
291     }
292
293     let mut new_items = match it.node {
294         ast::ItemMac(..) => expand_item_mac(it, fld),
295         ast::ItemMod(_) | ast::ItemForeignMod(_) => {
296             fld.cx.mod_push(it.ident);
297             let macro_escape = contains_macro_escape(it.attrs.as_slice());
298             let result = with_exts_frame!(fld.extsbox,
299                                           macro_escape,
300                                           noop_fold_item(it, fld));
301             fld.cx.mod_pop();
302             result
303         },
304         _ => noop_fold_item(it, fld)
305     };
306
307     new_items.push_all(decorator_items);
308     new_items
309 }
310
311 fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
312                          -> @ast::Item {
313     let (modifiers, attrs) = it.attrs.partitioned(|attr| {
314         match fld.extsbox.find(&intern(attr.name().get())) {
315             Some(&ItemModifier(_)) => true,
316             _ => false
317         }
318     });
319
320     it = @ast::Item {
321         attrs: attrs,
322         ..(*it).clone()
323     };
324
325     if modifiers.is_empty() {
326         return it;
327     }
328
329     for attr in modifiers.iter() {
330         let mname = attr.name();
331
332         match fld.extsbox.find(&intern(mname.get())) {
333             Some(&ItemModifier(dec_fn)) => {
334                 fld.cx.bt_push(ExpnInfo {
335                     call_site: attr.span,
336                     callee: NameAndSpan {
337                         name: mname.get().to_str(),
338                         format: MacroAttribute,
339                         span: None,
340                     }
341                 });
342                 it = dec_fn(fld.cx, attr.span, attr.node.value, it);
343                 fld.cx.bt_pop();
344             }
345             _ => unreachable!()
346         }
347     }
348
349     // expansion may have added new ItemModifiers
350     expand_item_modifiers(it, fld)
351 }
352
353 // does this attribute list contain "macro_escape" ?
354 pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
355     attr::contains_name(attrs, "macro_escape")
356 }
357
358 // Support for item-position macro invocations, exactly the same
359 // logic as for expression-position macro invocations.
360 pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
361                        -> SmallVector<@ast::Item> {
362     let (pth, tts) = match it.node {
363         ItemMac(codemap::Spanned {
364             node: MacInvocTT(ref pth, ref tts, _),
365             ..
366         }) => {
367             (pth, (*tts).clone())
368         }
369         _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
370     };
371
372     let extname = pth.segments.get(0).identifier;
373     let extnamestr = token::get_ident(extname);
374     let fm = fresh_mark();
375     let expanded = match fld.extsbox.find(&extname.name) {
376         None => {
377             fld.cx.span_err(pth.span,
378                             format!("macro undefined: '{}!'",
379                                     extnamestr));
380             // let compilation continue
381             return SmallVector::zero();
382         }
383
384         Some(&NormalTT(ref expander, span)) => {
385             if it.ident.name != parse::token::special_idents::invalid.name {
386                 fld.cx.span_err(pth.span,
387                                 format!("macro {}! expects no ident argument, \
388                                         given '{}'",
389                                         extnamestr,
390                                         token::get_ident(it.ident)));
391                 return SmallVector::zero();
392             }
393             fld.cx.bt_push(ExpnInfo {
394                 call_site: it.span,
395                 callee: NameAndSpan {
396                     name: extnamestr.get().to_str(),
397                     format: MacroBang,
398                     span: span
399                 }
400             });
401             // mark before expansion:
402             let marked_before = mark_tts(tts.as_slice(), fm);
403             expander.expand(fld.cx, it.span, marked_before.as_slice())
404         }
405         Some(&IdentTT(ref expander, span)) => {
406             if it.ident.name == parse::token::special_idents::invalid.name {
407                 fld.cx.span_err(pth.span,
408                                 format!("macro {}! expects an ident argument",
409                                         extnamestr.get()));
410                 return SmallVector::zero();
411             }
412             fld.cx.bt_push(ExpnInfo {
413                 call_site: it.span,
414                 callee: NameAndSpan {
415                     name: extnamestr.get().to_str(),
416                     format: MacroBang,
417                     span: span
418                 }
419             });
420             // mark before expansion:
421             let marked_tts = mark_tts(tts.as_slice(), fm);
422             expander.expand(fld.cx, it.span, it.ident, marked_tts)
423         }
424         _ => {
425             fld.cx.span_err(it.span,
426                             format!("{}! is not legal in item position",
427                                     extnamestr.get()));
428             return SmallVector::zero();
429         }
430     };
431
432     let items = match expanded.make_def() {
433         Some(MacroDef { name, ext }) => {
434             // yikes... no idea how to apply the mark to this. I'm afraid
435             // we're going to have to wait-and-see on this one.
436             fld.extsbox.insert(intern(name), ext);
437             if attr::contains_name(it.attrs.as_slice(), "macro_export") {
438                 SmallVector::one(it)
439             } else {
440                 SmallVector::zero()
441             }
442         }
443         None => {
444             match expanded.make_items() {
445                 Some(items) => {
446                     items.move_iter()
447                         .flat_map(|i| mark_item(i, fm).move_iter())
448                         .flat_map(|i| fld.fold_item(i).move_iter())
449                         .collect()
450                 }
451                 None => {
452                     fld.cx.span_err(pth.span,
453                                     format!("expr macro in item position: {}",
454                                             extnamestr.get()));
455                     return SmallVector::zero();
456                 }
457             }
458         }
459     };
460     fld.cx.bt_pop();
461     return items;
462 }
463
464 // load macros from syntax-phase crates
465 pub fn expand_view_item(vi: &ast::ViewItem,
466                         fld: &mut MacroExpander)
467                         -> ast::ViewItem {
468     match vi.node {
469         ast::ViewItemExternCrate(..) => {
470             let should_load = vi.attrs.iter().any(|attr| {
471                 attr.name().get() == "phase" &&
472                     attr.meta_item_list().map_or(false, |phases| {
473                         attr::contains_name(phases, "syntax")
474                     })
475             });
476
477             if should_load {
478                 load_extern_macros(vi, fld);
479             }
480         }
481         ast::ViewItemUse(_) => {}
482     }
483
484     noop_fold_view_item(vi, fld)
485 }
486
487 fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
488     let MacroCrate { lib, macros, registrar_symbol } =
489         fld.cx.ecfg.loader.load_crate(krate);
490
491     let crate_name = match krate.node {
492         ast::ViewItemExternCrate(name, _, _) => name,
493         _ => unreachable!()
494     };
495     let name = format!("<{} macros>", token::get_ident(crate_name));
496
497     for source in macros.iter() {
498         let item = parse::parse_item_from_source_str(name.clone(),
499                                                      (*source).clone(),
500                                                      fld.cx.cfg(),
501                                                      fld.cx.parse_sess())
502                 .expect("expected a serialized item");
503         expand_item_mac(item, fld);
504     }
505
506     let path = match lib {
507         Some(path) => path,
508         None => return
509     };
510     // Make sure the path contains a / or the linker will search for it.
511     let path = os::make_absolute(&path);
512
513     let registrar = match registrar_symbol {
514         Some(registrar) => registrar,
515         None => return
516     };
517
518     let lib = match DynamicLibrary::open(Some(&path)) {
519         Ok(lib) => lib,
520         // this is fatal: there are almost certainly macros we need
521         // inside this crate, so continue would spew "macro undefined"
522         // errors
523         Err(err) => fld.cx.span_fatal(krate.span, err)
524     };
525
526     unsafe {
527         let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) {
528             Ok(registrar) => registrar,
529             // again fatal if we can't register macros
530             Err(err) => fld.cx.span_fatal(krate.span, err)
531         };
532         registrar(|name, extension| {
533             let extension = match extension {
534                 NormalTT(ext, _) => NormalTT(ext, Some(krate.span)),
535                 IdentTT(ext, _) => IdentTT(ext, Some(krate.span)),
536                 ItemDecorator(ext) => ItemDecorator(ext),
537                 ItemModifier(ext) => ItemModifier(ext),
538             };
539             fld.extsbox.insert(name, extension);
540         });
541
542         // Intentionally leak the dynamic library. We can't ever unload it
543         // since the library can do things that will outlive the expansion
544         // phase (e.g. make an @-box cycle or launch a task).
545         cast::forget(lib);
546     }
547 }
548
549 // expand a stmt
550 pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
551     // why the copying here and not in expand_expr?
552     // looks like classic changed-in-only-one-place
553     let (pth, tts, semi) = match s.node {
554         StmtMac(ref mac, semi) => {
555             match mac.node {
556                 MacInvocTT(ref pth, ref tts, _) => {
557                     (pth, (*tts).clone(), semi)
558                 }
559             }
560         }
561         _ => return expand_non_macro_stmt(s, fld)
562     };
563     if pth.segments.len() > 1u {
564         fld.cx.span_err(pth.span, "expected macro name without module separators");
565         return SmallVector::zero();
566     }
567     let extname = pth.segments.get(0).identifier;
568     let extnamestr = token::get_ident(extname);
569     let marked_after = match fld.extsbox.find(&extname.name) {
570         None => {
571             fld.cx.span_err(pth.span, format!("macro undefined: '{}'", extnamestr));
572             return SmallVector::zero();
573         }
574
575         Some(&NormalTT(ref expandfun, exp_span)) => {
576             fld.cx.bt_push(ExpnInfo {
577                 call_site: s.span,
578                 callee: NameAndSpan {
579                     name: extnamestr.get().to_str(),
580                     format: MacroBang,
581                     span: exp_span,
582                 }
583             });
584             let fm = fresh_mark();
585             // mark before expansion:
586             let marked_tts = mark_tts(tts.as_slice(), fm);
587
588             // See the comment in expand_expr for why we want the original span,
589             // not the current mac.span.
590             let mac_span = original_span(fld.cx);
591
592             let expanded = match expandfun.expand(fld.cx,
593                                                   mac_span.call_site,
594                                                   marked_tts.as_slice()).make_stmt() {
595                 Some(stmt) => stmt,
596                 None => {
597                     fld.cx.span_err(pth.span,
598                                     format!("non-stmt macro in stmt pos: {}",
599                                             extnamestr));
600                     return SmallVector::zero();
601                 }
602             };
603
604             mark_stmt(expanded,fm)
605         }
606
607         _ => {
608             fld.cx.span_err(pth.span, format!("'{}' is not a tt-style macro",
609                                               extnamestr));
610             return SmallVector::zero();
611         }
612     };
613
614     // Keep going, outside-in.
615     let fully_expanded = fld.fold_stmt(marked_after);
616     if fully_expanded.is_empty() {
617         fld.cx.span_err(pth.span, "macro didn't expand to a statement");
618         return SmallVector::zero();
619     }
620     fld.cx.bt_pop();
621     let fully_expanded: SmallVector<@Stmt> = fully_expanded.move_iter()
622             .map(|s| @Spanned { span: s.span, node: s.node.clone() })
623             .collect();
624
625     fully_expanded.move_iter().map(|s| {
626         match s.node {
627             StmtExpr(e, stmt_id) if semi => {
628                 @Spanned {
629                     span: s.span,
630                     node: StmtSemi(e, stmt_id)
631                 }
632             }
633             _ => s /* might already have a semi */
634         }
635     }).collect()
636 }
637
638 // expand a non-macro stmt. this is essentially the fallthrough for
639 // expand_stmt, above.
640 fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
641                          -> SmallVector<@Stmt> {
642     // is it a let?
643     match s.node {
644         StmtDecl(decl, node_id) => {
645             match *decl {
646                 Spanned {
647                     node: DeclLocal(ref local),
648                     span: stmt_span
649                 } => {
650                     // take it apart:
651                     let Local {
652                         ty: _,
653                         pat: pat,
654                         init: init,
655                         id: id,
656                         span: span
657                     } = **local;
658                     // expand the pat (it might contain exprs... #:(o)>
659                     let expanded_pat = fld.fold_pat(pat);
660                     // find the pat_idents in the pattern:
661                     // oh dear heaven... this is going to include the enum
662                     // names, as well... but that should be okay, as long as
663                     // the new names are gensyms for the old ones.
664                     let mut name_finder = new_name_finder(Vec::new());
665                     name_finder.visit_pat(expanded_pat,());
666                     // generate fresh names, push them to a new pending list
667                     let mut new_pending_renames = Vec::new();
668                     for ident in name_finder.ident_accumulator.iter() {
669                         let new_name = fresh_name(ident);
670                         new_pending_renames.push((*ident,new_name));
671                     }
672                     let rewritten_pat = {
673                         let mut rename_fld =
674                             renames_to_fold(&mut new_pending_renames);
675                         // rewrite the pattern using the new names (the old
676                         // ones have already been applied):
677                         rename_fld.fold_pat(expanded_pat)
678                     };
679                     // add them to the existing pending renames:
680                     fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
681                     // also, don't forget to expand the init:
682                     let new_init_opt = init.map(|e| fld.fold_expr(e));
683                     let rewritten_local =
684                         @Local {
685                             ty: local.ty,
686                             pat: rewritten_pat,
687                             init: new_init_opt,
688                             id: id,
689                             span: span,
690                         };
691                     SmallVector::one(@Spanned {
692                         node: StmtDecl(@Spanned {
693                                 node: DeclLocal(rewritten_local),
694                                 span: stmt_span
695                             },
696                             node_id),
697                         span: span
698                     })
699                 }
700                 _ => noop_fold_stmt(s, fld),
701             }
702         },
703         _ => noop_fold_stmt(s, fld),
704     }
705 }
706
707 // a visitor that extracts the pat_ident paths
708 // from a given thingy and puts them in a mutable
709 // array (passed in to the traversal)
710 #[deriving(Clone)]
711 pub struct NewNameFinderContext {
712     ident_accumulator: Vec<ast::Ident> ,
713 }
714
715 impl Visitor<()> for NewNameFinderContext {
716     fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
717         match *pattern {
718             // we found a pat_ident!
719             ast::Pat {
720                 id: _,
721                 node: ast::PatIdent(_, ref path, ref inner),
722                 span: _
723             } => {
724                 match path {
725                     // a path of length one:
726                     &ast::Path {
727                         global: false,
728                         span: _,
729                         segments: ref segments
730                     } if segments.len() == 1 => {
731                         self.ident_accumulator.push(segments.get(0)
732                                                             .identifier)
733                     }
734                     // I believe these must be enums...
735                     _ => ()
736                 }
737                 // visit optional subpattern of pat_ident:
738                 for subpat in inner.iter() {
739                     self.visit_pat(*subpat, ())
740                 }
741             }
742             // use the default traversal for non-pat_idents
743             _ => visit::walk_pat(self, pattern, ())
744         }
745     }
746
747     fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
748         visit::walk_ty(self, typ, ())
749     }
750
751 }
752
753 // return a visitor that extracts the pat_ident paths
754 // from a given thingy and puts them in a mutable
755 // array (passed in to the traversal)
756 pub fn new_name_finder(idents: Vec<ast::Ident> ) -> NewNameFinderContext {
757     NewNameFinderContext {
758         ident_accumulator: idents,
759     }
760 }
761
762 // expand a block. pushes a new exts_frame, then calls expand_block_elts
763 pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
764     // see note below about treatment of exts table
765     with_exts_frame!(fld.extsbox,false,
766                      expand_block_elts(blk, fld))
767 }
768
769 // expand the elements of a block.
770 pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
771     let new_view_items = b.view_items.iter().map(|x| fld.fold_view_item(x)).collect();
772     let new_stmts =
773         b.stmts.iter().flat_map(|x| {
774             let renamed_stmt = {
775                 let pending_renames = &mut fld.extsbox.info().pending_renames;
776                 let mut rename_fld = renames_to_fold(pending_renames);
777                 rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value")
778             };
779             fld.fold_stmt(renamed_stmt).move_iter()
780         }).collect();
781     let new_expr = b.expr.map(|x| {
782         let expr = {
783             let pending_renames = &mut fld.extsbox.info().pending_renames;
784             let mut rename_fld = renames_to_fold(pending_renames);
785             rename_fld.fold_expr(x)
786         };
787         fld.fold_expr(expr)
788     });
789     P(Block {
790         view_items: new_view_items,
791         stmts: new_stmts,
792         expr: new_expr,
793         id: fld.new_id(b.id),
794         rules: b.rules,
795         span: b.span,
796     })
797 }
798
799 pub struct IdentRenamer<'a> {
800     renames: &'a mut RenameList,
801 }
802
803 impl<'a> Folder for IdentRenamer<'a> {
804     fn fold_ident(&mut self, id: Ident) -> Ident {
805         let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
806             mtwt::new_rename(from, to, ctxt)
807         });
808         Ident {
809             name: id.name,
810             ctxt: new_ctxt,
811         }
812     }
813 }
814
815 // given a mutable list of renames, return a tree-folder that applies those
816 // renames.
817 pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
818     IdentRenamer {
819         renames: renames,
820     }
821 }
822
823 pub fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
824     /* this discards information in the case of macro-defining macros */
825     Span {
826         lo: sp.lo,
827         hi: sp.hi,
828         expn_info: cx.backtrace(),
829     }
830 }
831
832 pub struct MacroExpander<'a, 'b> {
833     pub extsbox: SyntaxEnv,
834     pub cx: &'a mut ExtCtxt<'b>,
835 }
836
837 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
838     fn fold_expr(&mut self, expr: @ast::Expr) -> @ast::Expr {
839         expand_expr(expr, self)
840     }
841
842     fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
843         expand_item(item, self)
844     }
845
846     fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem {
847         expand_view_item(vi, self)
848     }
849
850     fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
851         expand_stmt(stmt, self)
852     }
853
854     fn fold_block(&mut self, block: P<Block>) -> P<Block> {
855         expand_block(block, self)
856     }
857
858     fn new_span(&mut self, span: Span) -> Span {
859         new_span(self.cx, span)
860     }
861 }
862
863 pub struct ExpansionConfig<'a> {
864     pub loader: &'a mut CrateLoader,
865     pub deriving_hash_type_parameter: bool,
866     pub crate_id: CrateId,
867 }
868
869 pub fn expand_crate(parse_sess: &parse::ParseSess,
870                     cfg: ExpansionConfig,
871                     c: Crate) -> Crate {
872     let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
873     let mut expander = MacroExpander {
874         extsbox: syntax_expander_table(),
875         cx: &mut cx,
876     };
877
878     let ret = expander.fold_crate(c);
879     parse_sess.span_diagnostic.handler().abort_if_errors();
880     return ret;
881 }
882
883 // HYGIENIC CONTEXT EXTENSION:
884 // all of these functions are for walking over
885 // ASTs and making some change to the context of every
886 // element that has one. a CtxtFn is a trait-ified
887 // version of a closure in (SyntaxContext -> SyntaxContext).
888 // the ones defined here include:
889 // Marker - add a mark to a context
890
891 // A Marker adds the given mark to the syntax context
892 struct Marker { mark: Mrk }
893
894 impl Folder for Marker {
895     fn fold_ident(&mut self, id: Ident) -> Ident {
896         ast::Ident {
897             name: id.name,
898             ctxt: mtwt::new_mark(self.mark, id.ctxt)
899         }
900     }
901     fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
902         let macro = match m.node {
903             MacInvocTT(ref path, ref tts, ctxt) => {
904                 MacInvocTT(self.fold_path(path),
905                            fold_tts(tts.as_slice(), self),
906                            mtwt::new_mark(self.mark, ctxt))
907             }
908         };
909         Spanned {
910             node: macro,
911             span: m.span,
912         }
913     }
914 }
915
916 // just a convenience:
917 fn new_mark_folder(m: Mrk) -> Marker {
918     Marker {mark: m}
919 }
920
921 // apply a given mark to the given token trees. Used prior to expansion of a macro.
922 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
923     fold_tts(tts, &mut new_mark_folder(m))
924 }
925
926 // apply a given mark to the given expr. Used following the expansion of a macro.
927 fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
928     new_mark_folder(m).fold_expr(expr)
929 }
930
931 // apply a given mark to the given stmt. Used following the expansion of a macro.
932 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
933     new_mark_folder(m).fold_stmt(expr)
934             .expect_one("marking a stmt didn't return a stmt")
935 }
936
937 // apply a given mark to the given item. Used following the expansion of a macro.
938 fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
939     new_mark_folder(m).fold_item(expr)
940 }
941
942 fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
943     let mut relevant_info = cx.backtrace();
944     let mut einfo = relevant_info.unwrap();
945     loop {
946         match relevant_info {
947             None => { break }
948             Some(e) => {
949                 einfo = e;
950                 relevant_info = einfo.call_site.expn_info;
951             }
952         }
953     }
954     return einfo;
955 }
956
957 #[cfg(test)]
958 mod test {
959     use super::*;
960     use ast;
961     use ast::{Attribute_, AttrOuter, MetaWord};
962     use codemap;
963     use codemap::Spanned;
964     use ext::base::{CrateLoader, MacroCrate};
965     use ext::mtwt;
966     use parse;
967     use parse::token;
968     use util::parser_testing::{string_to_parser};
969     use util::parser_testing::{string_to_pat, strs_to_idents};
970     use visit;
971     use visit::Visitor;
972
973     // a visitor that extracts the paths
974     // from a given thingy and puts them in a mutable
975     // array (passed in to the traversal)
976     #[deriving(Clone)]
977     struct NewPathExprFinderContext {
978         path_accumulator: Vec<ast::Path> ,
979     }
980
981     impl Visitor<()> for NewPathExprFinderContext {
982
983         fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
984             match *expr {
985                 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
986                     self.path_accumulator.push(p.clone());
987                     // not calling visit_path, should be fine.
988                 }
989                 _ => visit::walk_expr(self,expr,())
990             }
991         }
992
993         fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
994             visit::walk_ty(self, typ, ())
995         }
996
997     }
998
999     // return a visitor that extracts the paths
1000     // from a given pattern and puts them in a mutable
1001     // array (passed in to the traversal)
1002     pub fn new_path_finder(paths: Vec<ast::Path> ) -> NewPathExprFinderContext {
1003         NewPathExprFinderContext {
1004             path_accumulator: paths
1005         }
1006     }
1007
1008     struct ErrLoader;
1009
1010     impl CrateLoader for ErrLoader {
1011         fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
1012             fail!("lolwut")
1013         }
1014     }
1015
1016     // these following tests are quite fragile, in that they don't test what
1017     // *kind* of failure occurs.
1018
1019     // make sure that macros can leave scope
1020     #[should_fail]
1021     #[test] fn macros_cant_escape_fns_test () {
1022         let src = "fn bogus() {macro_rules! z (() => (3+4))}\
1023                    fn inty() -> int { z!() }".to_owned();
1024         let sess = parse::new_parse_sess();
1025         let crate_ast = parse::parse_crate_from_source_str(
1026             "<test>".to_owned(),
1027             src,
1028             Vec::new(), &sess);
1029         // should fail:
1030         let mut loader = ErrLoader;
1031         let cfg = ::syntax::ext::expand::ExpansionConfig {
1032             loader: &mut loader,
1033             deriving_hash_type_parameter: false,
1034             crate_id: from_str("test").unwrap(),
1035         };
1036         expand_crate(&sess,cfg,crate_ast);
1037     }
1038
1039     // make sure that macros can leave scope for modules
1040     #[should_fail]
1041     #[test] fn macros_cant_escape_mods_test () {
1042         let src = "mod foo {macro_rules! z (() => (3+4))}\
1043                    fn inty() -> int { z!() }".to_owned();
1044         let sess = parse::new_parse_sess();
1045         let crate_ast = parse::parse_crate_from_source_str(
1046             "<test>".to_owned(),
1047             src,
1048             Vec::new(), &sess);
1049         // should fail:
1050         let mut loader = ErrLoader;
1051         let cfg = ::syntax::ext::expand::ExpansionConfig {
1052             loader: &mut loader,
1053             deriving_hash_type_parameter: false,
1054             crate_id: from_str("test").unwrap(),
1055         };
1056         expand_crate(&sess,cfg,crate_ast);
1057     }
1058
1059     // macro_escape modules shouldn't cause macros to leave scope
1060     #[test] fn macros_can_escape_flattened_mods_test () {
1061         let src = "#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1062                    fn inty() -> int { z!() }".to_owned();
1063         let sess = parse::new_parse_sess();
1064         let crate_ast = parse::parse_crate_from_source_str(
1065             "<test>".to_owned(),
1066             src,
1067             Vec::new(), &sess);
1068         // should fail:
1069         let mut loader = ErrLoader;
1070         let cfg = ::syntax::ext::expand::ExpansionConfig {
1071             loader: &mut loader,
1072             deriving_hash_type_parameter: false,
1073             crate_id: from_str("test").unwrap(),
1074         };
1075         expand_crate(&sess, cfg, crate_ast);
1076     }
1077
1078     #[test] fn test_contains_flatten (){
1079         let attr1 = make_dummy_attr ("foo");
1080         let attr2 = make_dummy_attr ("bar");
1081         let escape_attr = make_dummy_attr ("macro_escape");
1082         let attrs1 = vec!(attr1, escape_attr, attr2);
1083         assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1084         let attrs2 = vec!(attr1,attr2);
1085         assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1086     }
1087
1088     // make a MetaWord outer attribute with the given name
1089     fn make_dummy_attr(s: &str) -> ast::Attribute {
1090         Spanned {
1091             span:codemap::DUMMY_SP,
1092             node: Attribute_ {
1093                 style: AttrOuter,
1094                 value: @Spanned {
1095                     node: MetaWord(token::intern_and_get_ident(s)),
1096                     span: codemap::DUMMY_SP,
1097                 },
1098                 is_sugared_doc: false,
1099             }
1100         }
1101     }
1102
1103     //fn fake_print_crate(krate: &ast::Crate) {
1104     //    let mut out = ~std::io::stderr() as ~std::io::Writer;
1105     //    let mut s = pprust::rust_printer(out, get_ident_interner());
1106     //    pprust::print_crate_(&mut s, krate);
1107     //}
1108
1109     fn expand_crate_str(crate_str: ~str) -> ast::Crate {
1110         let ps = parse::new_parse_sess();
1111         let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
1112         // the cfg argument actually does matter, here...
1113         let mut loader = ErrLoader;
1114         let cfg = ::syntax::ext::expand::ExpansionConfig {
1115             loader: &mut loader,
1116             deriving_hash_type_parameter: false,
1117             crate_id: from_str("test").unwrap(),
1118         };
1119         expand_crate(&ps,cfg,crate_ast)
1120     }
1121
1122     //fn expand_and_resolve(crate_str: @str) -> ast::crate {
1123         //let expanded_ast = expand_crate_str(crate_str);
1124         // println!("expanded: {:?}\n",expanded_ast);
1125         //mtwt_resolve_crate(expanded_ast)
1126     //}
1127     //fn expand_and_resolve_and_pretty_print (crate_str: @str) -> ~str {
1128         //let resolved_ast = expand_and_resolve(crate_str);
1129         //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
1130     //}
1131
1132     #[test] fn macro_tokens_should_match(){
1133         expand_crate_str("macro_rules! m((a)=>(13)) fn main(){m!(a);}".to_owned());
1134     }
1135
1136     // renaming tests expand a crate and then check that the bindings match
1137     // the right varrefs. The specification of the test case includes the
1138     // text of the crate, and also an array of arrays.  Each element in the
1139     // outer array corresponds to a binding in the traversal of the AST
1140     // induced by visit.  Each of these arrays contains a list of indexes,
1141     // interpreted as the varrefs in the varref traversal that this binding
1142     // should match.  So, for instance, in a program with two bindings and
1143     // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1144     // binding should match the second two varrefs, and the second binding
1145     // should match the first varref.
1146     //
1147     // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1148     // names; differences in marks don't matter any more.
1149     //
1150     // oog... I also want tests that check "binding-identifier-=?". That is,
1151     // not just "do these have the same name", but "do they have the same
1152     // name *and* the same marks"? Understanding this is really pretty painful.
1153     // in principle, you might want to control this boolean on a per-varref basis,
1154     // but that would make things even harder to understand, and might not be
1155     // necessary for thorough testing.
1156     type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1157
1158     #[test]
1159     fn automatic_renaming () {
1160         let tests: Vec<RenamingTest> =
1161             vec!(// b & c should get new names throughout, in the expr too:
1162                 ("fn a() -> int { let b = 13; let c = b; b+c }",
1163                  vec!(vec!(0,1),vec!(2)), false),
1164                 // both x's should be renamed (how is this causing a bug?)
1165                 ("fn main () {let x: int = 13;x;}",
1166                  vec!(vec!(0)), false),
1167                 // the use of b after the + should be renamed, the other one not:
1168                 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1169                  vec!(vec!(1)), false),
1170                 // the b before the plus should not be renamed (requires marks)
1171                 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1172                  vec!(vec!(1)), false),
1173                 // the marks going in and out of letty should cancel, allowing that $x to
1174                 // capture the one following the semicolon.
1175                 // this was an awesome test case, and caught a *lot* of bugs.
1176                 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1177                   macro_rules! user(($x:ident) => ({letty!($x); $x}))
1178                   fn main() -> int {user!(z)}",
1179                  vec!(vec!(0)), false));
1180         for (idx,s) in tests.iter().enumerate() {
1181             run_renaming_test(s,idx);
1182         }
1183     }
1184
1185     // run one of the renaming tests
1186     fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1187         let invalid_name = token::special_idents::invalid.name;
1188         let (teststr, bound_connections, bound_ident_check) = match *t {
1189             (ref str,ref conns, bic) => (str.to_owned(), conns.clone(), bic)
1190         };
1191         let cr = expand_crate_str(teststr.to_owned());
1192         // find the bindings:
1193         let mut name_finder = new_name_finder(Vec::new());
1194         visit::walk_crate(&mut name_finder,&cr,());
1195         let bindings = name_finder.ident_accumulator;
1196
1197         // find the varrefs:
1198         let mut path_finder = new_path_finder(Vec::new());
1199         visit::walk_crate(&mut path_finder,&cr,());
1200         let varrefs = path_finder.path_accumulator;
1201
1202         // must be one check clause for each binding:
1203         assert_eq!(bindings.len(),bound_connections.len());
1204         for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1205             let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1206             let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1207             // shouldmatch can't name varrefs that don't exist:
1208             assert!((shouldmatch.len() == 0) ||
1209                     (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1210             for (idx,varref) in varrefs.iter().enumerate() {
1211                 if shouldmatch.contains(&idx) {
1212                     // it should be a path of length 1, and it should
1213                     // be free-identifier=? or bound-identifier=? to the given binding
1214                     assert_eq!(varref.segments.len(),1);
1215                     let varref_name = mtwt::resolve(varref.segments
1216                                                           .get(0)
1217                                                           .identifier);
1218                     let varref_marks = mtwt::marksof(varref.segments
1219                                                            .get(0)
1220                                                            .identifier
1221                                                            .ctxt,
1222                                                      invalid_name);
1223                     if !(varref_name==binding_name) {
1224                         println!("uh oh, should match but doesn't:");
1225                         println!("varref: {:?}",varref);
1226                         println!("binding: {:?}", *bindings.get(binding_idx));
1227                         mtwt::with_sctable(|x| mtwt::display_sctable(x));
1228                     }
1229                     assert_eq!(varref_name,binding_name);
1230                     if bound_ident_check {
1231                         // we're checking bound-identifier=?, and the marks
1232                         // should be the same, too:
1233                         assert_eq!(varref_marks,binding_marks.clone());
1234                     }
1235                 } else {
1236                     let fail = (varref.segments.len() == 1)
1237                         && (mtwt::resolve(varref.segments.get(0).identifier)
1238                             == binding_name);
1239                     // temp debugging:
1240                     if fail {
1241                         println!("failure on test {}",test_idx);
1242                         println!("text of test case: \"{}\"", teststr);
1243                         println!("");
1244                         println!("uh oh, matches but shouldn't:");
1245                         println!("varref: {:?}",varref);
1246                         // good lord, you can't make a path with 0 segments, can you?
1247                         let string = token::get_ident(varref.segments
1248                                                             .get(0)
1249                                                             .identifier);
1250                         println!("varref's first segment's uint: {}, and string: \"{}\"",
1251                                  varref.segments.get(0).identifier.name,
1252                                  string.get());
1253                         println!("binding: {:?}", *bindings.get(binding_idx));
1254                         mtwt::with_sctable(|x| mtwt::display_sctable(x));
1255                     }
1256                     assert!(!fail);
1257                 }
1258             }
1259         }
1260     }
1261
1262     #[test] fn fmt_in_macro_used_inside_module_macro() {
1263         let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
1264 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1265 foo_module!()
1266 ".to_owned();
1267         let cr = expand_crate_str(crate_str);
1268         // find the xx binding
1269         let mut name_finder = new_name_finder(Vec::new());
1270         visit::walk_crate(&mut name_finder, &cr, ());
1271         let bindings = name_finder.ident_accumulator;
1272
1273         let cxbinds: Vec<&ast::Ident> =
1274             bindings.iter().filter(|b| {
1275                 let ident = token::get_ident(**b);
1276                 let string = ident.get();
1277                 "xx" == string
1278             }).collect();
1279         let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1280         let cxbind = match cxbinds {
1281             [b] => b,
1282             _ => fail!("expected just one binding for ext_cx")
1283         };
1284         let resolved_binding = mtwt::resolve(*cxbind);
1285         // find all the xx varrefs:
1286         let mut path_finder = new_path_finder(Vec::new());
1287         visit::walk_crate(&mut path_finder, &cr, ());
1288         let varrefs = path_finder.path_accumulator;
1289
1290         // the xx binding should bind all of the xx varrefs:
1291         for (idx,v) in varrefs.iter().filter(|p| {
1292             p.segments.len() == 1
1293             && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1294         }).enumerate() {
1295             if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1296                 println!("uh oh, xx binding didn't match xx varref:");
1297                 println!("this is xx varref \\# {:?}",idx);
1298                 println!("binding: {:?}",cxbind);
1299                 println!("resolves to: {:?}",resolved_binding);
1300                 println!("varref: {:?}",v.segments.get(0).identifier);
1301                 println!("resolves to: {:?}",
1302                          mtwt::resolve(v.segments.get(0).identifier));
1303                 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1304             }
1305             assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1306                        resolved_binding);
1307         };
1308     }
1309
1310     #[test]
1311     fn pat_idents(){
1312         let pat = string_to_pat("(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_owned());
1313         let mut pat_idents = new_name_finder(Vec::new());
1314         pat_idents.visit_pat(pat, ());
1315         assert_eq!(pat_idents.ident_accumulator,
1316                    strs_to_idents(vec!("a","c","b","d")));
1317     }
1318
1319 }