1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use ast::{P, Block, Crate, DeclLocal, ExprMac};
12 use ast::{Local, Ident, MacInvocTT};
13 use ast::{ItemMac, Mrk, Stmt, StmtDecl, StmtMac, StmtExpr, StmtSemi};
17 use ext::build::AstBuilder;
19 use attr::AttrMetaMethods;
21 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
26 use parse::token::{fresh_mark, fresh_name, intern};
30 use util::small_vector::SmallVector;
33 use std::unstable::dynamic_lib::DynamicLibrary;
37 pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
39 // expr_mac should really be expr_ext or something; it's the
40 // entry-point for all syntax extensions.
43 // it would almost certainly be cleaner to pass the whole
44 // macro invocation in, rather than pulling it apart and
45 // marking the tts and the ctxt separately. This also goes
46 // for the other three macro invocation chunks of code
49 MacInvocTT(ref pth, ref tts, _) => {
50 if pth.segments.len() > 1u {
53 format!("expected macro name without module \
55 // let compilation continue
56 return MacResult::raw_dummy_expr(e.span);
58 let extname = pth.segments.get(0).identifier;
59 let extnamestr = token::get_ident(extname);
60 // leaving explicit deref here to highlight unbox op:
61 let marked_after = match fld.extsbox.find(&extname.name) {
65 format!("macro undefined: '{}'",
68 // let compilation continue
69 return MacResult::raw_dummy_expr(e.span);
71 Some(&NormalTT(ref expandfun, exp_span)) => {
72 fld.cx.bt_push(ExpnInfo {
75 name: extnamestr.get().to_str(),
80 let fm = fresh_mark();
82 let marked_before = mark_tts(tts.as_slice(), fm);
84 // The span that we pass to the expanders we want to
85 // be the root of the call stack. That's the most
86 // relevant span and it's the actual invocation of
88 let mac_span = original_span(fld.cx);
90 let expanded = match expandfun.expand(fld.cx,
92 marked_before.as_slice()) {
94 MRAny(any_macro) => any_macro.make_expr(),
99 "non-expr macro in expr pos: {}",
103 return MacResult::raw_dummy_expr(e.span);
108 mark_expr(expanded,fm)
113 format!("'{}' is not a tt-style macro",
116 return MacResult::raw_dummy_expr(e.span);
120 // Keep going, outside-in.
122 // FIXME(pcwalton): Is it necessary to clone the
125 fld.fold_expr(marked_after).node.clone();
129 id: ast::DUMMY_NODE_ID,
130 node: fully_expanded,
137 // Desugar expr_for_loop
138 // From: `['<ident>:] for <src_pat> in <src_expr> <src_loop_block>`
139 // FIXME #6993: change type of opt_ident to Option<Name>
140 ast::ExprForLoop(src_pat, src_expr, src_loop_block, opt_ident) => {
141 // Expand any interior macros etc.
142 // NB: we don't fold pats yet. Curious.
143 let src_expr = fld.fold_expr(src_expr).clone();
144 let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
150 // match &mut <src_expr> {
152 // ['<ident>:] loop {
155 // Some(<src_pat>) => <src_loop_block>
161 let local_ident = token::gensym_ident("i");
162 let next_ident = fld.cx.ident_of("next");
163 let none_ident = fld.cx.ident_of("None");
165 let local_path = fld.cx.path_ident(span, local_ident);
166 let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some"));
168 // `None => break ['<ident>];`
170 let break_expr = fld.cx.expr(span, ast::ExprBreak(opt_ident));
171 let none_pat = fld.cx.pat_ident(span, none_ident);
172 fld.cx.arm(span, vec!(none_pat), break_expr)
175 // `Some(<src_pat>) => <src_loop_block>`
178 vec!(fld.cx.pat_enum(span, some_path, vec!(src_pat))),
179 fld.cx.expr_block(src_loop_block));
181 // `match i.next() { ... }`
184 fld.cx.expr_method_call(span,
185 fld.cx.expr_path(local_path),
189 fld.cx.expr_match(span, next_call_expr, vec!(none_arm, some_arm))
192 // ['ident:] loop { ... }
193 let loop_expr = fld.cx.expr(span,
194 ast::ExprLoop(fld.cx.block_expr(match_expr),
197 // `i => loop { ... }`
199 // `match &mut <src_expr> { i => loop { ... } }`
200 let discrim = fld.cx.expr_mut_addr_of(span, src_expr);
201 let i_pattern = fld.cx.pat_ident(span, local_ident);
202 let arm = fld.cx.arm(span, vec!(i_pattern), loop_expr);
203 fld.cx.expr_match(span, discrim, vec!(arm))
206 ast::ExprLoop(loop_block, opt_ident) => {
207 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
208 fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
211 _ => noop_fold_expr(e, fld)
215 // Rename loop label and expand its loop body
217 // The renaming procedure for loop is different in the sense that the loop
218 // body is in a block enclosed by loop head so the renaming of loop label
219 // must be propagated to the enclosed context.
220 fn expand_loop_block(loop_block: P<Block>,
221 opt_ident: Option<Ident>,
222 fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
225 let new_label = fresh_name(&label);
226 let rename = (label, new_label);
228 // The rename *must not* be added to the pending list of current
229 // syntax context otherwise an unrelated `break` or `continue` in
230 // the same context will pick that up in the deferred renaming pass
231 // and be renamed incorrectly.
232 let mut rename_list = vec!(rename);
233 let mut rename_fld = renames_to_fold(&mut rename_list);
234 let renamed_ident = rename_fld.fold_ident(label);
236 // The rename *must* be added to the enclosed syntax context for
237 // `break` or `continue` to pick up because by definition they are
238 // in a block enclosed by loop head.
239 fld.extsbox.push_frame();
240 fld.extsbox.info().pending_renames.push(rename);
241 let expanded_block = expand_block_elts(loop_block, fld);
242 fld.extsbox.pop_frame();
244 (expanded_block, Some(renamed_ident))
246 None => (fld.fold_block(loop_block), opt_ident)
250 // eval $e with a new exts frame:
251 macro_rules! with_exts_frame (
252 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
253 ({$extsboxexpr.push_frame();
254 $extsboxexpr.info().macros_escape = $macros_escape;
256 $extsboxexpr.pop_frame();
261 // When we enter a module, record it, for the sake of `module!`
262 pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
263 -> SmallVector<@ast::Item> {
264 let it = expand_item_modifiers(it, fld);
266 let mut decorator_items = SmallVector::zero();
267 for attr in it.attrs.rev_iter() {
268 let mname = attr.name();
270 match fld.extsbox.find(&intern(mname.get())) {
271 Some(&ItemDecorator(dec_fn)) => {
272 fld.cx.bt_push(ExpnInfo {
273 call_site: attr.span,
274 callee: NameAndSpan {
275 name: mname.get().to_str(),
276 format: MacroAttribute,
281 // we'd ideally decorator_items.push_all(expand_item(item, fld)),
282 // but that double-mut-borrows fld
283 let mut items: SmallVector<@ast::Item> = SmallVector::zero();
284 dec_fn(fld.cx, attr.span, attr.node.value, it,
285 |item| items.push(item));
286 decorator_items.extend(&mut items.move_iter()
287 .flat_map(|item| expand_item(item, fld).move_iter()));
295 let mut new_items = match it.node {
296 ast::ItemMac(..) => expand_item_mac(it, fld),
297 ast::ItemMod(_) | ast::ItemForeignMod(_) => {
298 fld.cx.mod_push(it.ident);
299 let macro_escape = contains_macro_escape(it.attrs.as_slice());
300 let result = with_exts_frame!(fld.extsbox,
302 noop_fold_item(it, fld));
306 _ => noop_fold_item(it, fld)
309 new_items.push_all(decorator_items);
313 fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
315 let (modifiers, attrs) = it.attrs.partitioned(|attr| {
316 match fld.extsbox.find(&intern(attr.name().get())) {
317 Some(&ItemModifier(_)) => true,
327 if modifiers.is_empty() {
331 for attr in modifiers.iter() {
332 let mname = attr.name();
334 match fld.extsbox.find(&intern(mname.get())) {
335 Some(&ItemModifier(dec_fn)) => {
336 fld.cx.bt_push(ExpnInfo {
337 call_site: attr.span,
338 callee: NameAndSpan {
339 name: mname.get().to_str(),
340 format: MacroAttribute,
344 it = dec_fn(fld.cx, attr.span, attr.node.value, it);
351 // expansion may have added new ItemModifiers
352 expand_item_modifiers(it, fld)
355 // does this attribute list contain "macro_escape" ?
356 pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
357 attr::contains_name(attrs, "macro_escape")
360 // Support for item-position macro invocations, exactly the same
361 // logic as for expression-position macro invocations.
362 pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
363 -> SmallVector<@ast::Item> {
364 let (pth, tts) = match it.node {
365 ItemMac(codemap::Spanned {
366 node: MacInvocTT(ref pth, ref tts, _),
369 (pth, (*tts).clone())
371 _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
374 let extname = pth.segments.get(0).identifier;
375 let extnamestr = token::get_ident(extname);
376 let fm = fresh_mark();
377 let expanded = match fld.extsbox.find(&extname.name) {
379 fld.cx.span_err(pth.span,
380 format!("macro undefined: '{}!'",
382 // let compilation continue
383 return SmallVector::zero();
386 Some(&NormalTT(ref expander, span)) => {
387 if it.ident.name != parse::token::special_idents::invalid.name {
388 fld.cx.span_err(pth.span,
389 format!("macro {}! expects no ident argument, \
392 token::get_ident(it.ident)));
393 return SmallVector::zero();
395 fld.cx.bt_push(ExpnInfo {
397 callee: NameAndSpan {
398 name: extnamestr.get().to_str(),
403 // mark before expansion:
404 let marked_before = mark_tts(tts.as_slice(), fm);
405 expander.expand(fld.cx, it.span, marked_before.as_slice())
407 Some(&IdentTT(ref expander, span)) => {
408 if it.ident.name == parse::token::special_idents::invalid.name {
409 fld.cx.span_err(pth.span,
410 format!("macro {}! expects an ident argument",
412 return SmallVector::zero();
414 fld.cx.bt_push(ExpnInfo {
416 callee: NameAndSpan {
417 name: extnamestr.get().to_str(),
422 // mark before expansion:
423 let marked_tts = mark_tts(tts.as_slice(), fm);
424 expander.expand(fld.cx, it.span, it.ident, marked_tts)
427 fld.cx.span_err(it.span,
428 format!("{}! is not legal in item position",
430 return SmallVector::zero();
434 let items = match expanded {
436 mark_item(it,fm).move_iter()
437 .flat_map(|i| fld.fold_item(i).move_iter())
441 fld.cx.span_err(pth.span,
442 format!("expr macro in item position: {}",
444 return SmallVector::zero();
446 MRAny(any_macro) => {
447 any_macro.make_items().move_iter()
448 .flat_map(|i| mark_item(i, fm).move_iter())
449 .flat_map(|i| fld.fold_item(i).move_iter())
452 MRDef(MacroDef { name, ext }) => {
453 // yikes... no idea how to apply the mark to this. I'm afraid
454 // we're going to have to wait-and-see on this one.
455 fld.extsbox.insert(intern(name), ext);
456 if attr::contains_name(it.attrs.as_slice(), "macro_export") {
467 // load macros from syntax-phase crates
468 pub fn expand_view_item(vi: &ast::ViewItem,
469 fld: &mut MacroExpander)
472 ast::ViewItemExternCrate(..) => {
473 let should_load = vi.attrs.iter().any(|attr| {
474 attr.name().get() == "phase" &&
475 attr.meta_item_list().map_or(false, |phases| {
476 attr::contains_name(phases, "syntax")
481 load_extern_macros(vi, fld);
484 ast::ViewItemUse(_) => {}
487 noop_fold_view_item(vi, fld)
490 fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
491 let MacroCrate { lib, cnum } = fld.cx.ecfg.loader.load_crate(krate);
493 let crate_name = match krate.node {
494 ast::ViewItemExternCrate(name, _, _) => name,
497 let name = format!("<{} macros>", token::get_ident(crate_name));
499 let exported_macros = fld.cx.ecfg.loader.get_exported_macros(cnum);
500 for source in exported_macros.iter() {
501 let item = parse::parse_item_from_source_str(name.clone(),
505 .expect("expected a serialized item");
506 expand_item_mac(item, fld);
509 let path = match lib {
513 // Make sure the path contains a / or the linker will search for it.
514 let path = os::make_absolute(&path);
516 let registrar = match fld.cx.ecfg.loader.get_registrar_symbol(cnum) {
517 Some(registrar) => registrar,
521 let lib = match DynamicLibrary::open(Some(&path)) {
523 // this is fatal: there are almost certainly macros we need
524 // inside this crate, so continue would spew "macro undefined"
526 Err(err) => fld.cx.span_fatal(krate.span, err)
530 let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) {
531 Ok(registrar) => registrar,
532 // again fatal if we can't register macros
533 Err(err) => fld.cx.span_fatal(krate.span, err)
535 registrar(|name, extension| {
536 let extension = match extension {
537 NormalTT(ext, _) => NormalTT(ext, Some(krate.span)),
538 IdentTT(ext, _) => IdentTT(ext, Some(krate.span)),
539 ItemDecorator(ext) => ItemDecorator(ext),
540 ItemModifier(ext) => ItemModifier(ext),
542 fld.extsbox.insert(name, extension);
545 // Intentionally leak the dynamic library. We can't ever unload it
546 // since the library can do things that will outlive the expansion
547 // phase (e.g. make an @-box cycle or launch a task).
553 pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
554 // why the copying here and not in expand_expr?
555 // looks like classic changed-in-only-one-place
556 let (pth, tts, semi) = match s.node {
557 StmtMac(ref mac, semi) => {
559 MacInvocTT(ref pth, ref tts, _) => {
560 (pth, (*tts).clone(), semi)
564 _ => return expand_non_macro_stmt(s, fld)
566 if pth.segments.len() > 1u {
567 fld.cx.span_err(pth.span, "expected macro name without module separators");
568 return SmallVector::zero();
570 let extname = pth.segments.get(0).identifier;
571 let extnamestr = token::get_ident(extname);
572 let marked_after = match fld.extsbox.find(&extname.name) {
574 fld.cx.span_err(pth.span, format!("macro undefined: '{}'", extnamestr));
575 return SmallVector::zero();
578 Some(&NormalTT(ref expandfun, exp_span)) => {
579 fld.cx.bt_push(ExpnInfo {
581 callee: NameAndSpan {
582 name: extnamestr.get().to_str(),
587 let fm = fresh_mark();
588 // mark before expansion:
589 let marked_tts = mark_tts(tts.as_slice(), fm);
591 // See the comment in expand_expr for why we want the original span,
592 // not the current mac.span.
593 let mac_span = original_span(fld.cx);
595 let expanded = match expandfun.expand(fld.cx,
597 marked_tts.as_slice()) {
600 node: StmtExpr(e, ast::DUMMY_NODE_ID),
604 MRAny(any_macro) => any_macro.make_stmt(),
606 fld.cx.span_err(pth.span,
607 format!("non-stmt macro in stmt pos: {}",
609 return SmallVector::zero();
613 mark_stmt(expanded,fm)
617 fld.cx.span_err(pth.span, format!("'{}' is not a tt-style macro",
619 return SmallVector::zero();
623 // Keep going, outside-in.
624 let fully_expanded = fld.fold_stmt(marked_after);
625 if fully_expanded.is_empty() {
626 fld.cx.span_err(pth.span, "macro didn't expand to a statement");
627 return SmallVector::zero();
630 let fully_expanded: SmallVector<@Stmt> = fully_expanded.move_iter()
631 .map(|s| @Spanned { span: s.span, node: s.node.clone() })
634 fully_expanded.move_iter().map(|s| {
636 StmtExpr(e, stmt_id) if semi => {
639 node: StmtSemi(e, stmt_id)
642 _ => s /* might already have a semi */
647 // expand a non-macro stmt. this is essentially the fallthrough for
648 // expand_stmt, above.
649 fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
650 -> SmallVector<@Stmt> {
653 StmtDecl(decl, node_id) => {
656 node: DeclLocal(ref local),
667 // expand the pat (it might contain exprs... #:(o)>
668 let expanded_pat = fld.fold_pat(pat);
669 // find the pat_idents in the pattern:
670 // oh dear heaven... this is going to include the enum
671 // names, as well... but that should be okay, as long as
672 // the new names are gensyms for the old ones.
673 let mut name_finder = new_name_finder(Vec::new());
674 name_finder.visit_pat(expanded_pat,());
675 // generate fresh names, push them to a new pending list
676 let mut new_pending_renames = Vec::new();
677 for ident in name_finder.ident_accumulator.iter() {
678 let new_name = fresh_name(ident);
679 new_pending_renames.push((*ident,new_name));
681 let rewritten_pat = {
683 renames_to_fold(&mut new_pending_renames);
684 // rewrite the pattern using the new names (the old
685 // ones have already been applied):
686 rename_fld.fold_pat(expanded_pat)
688 // add them to the existing pending renames:
689 fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
690 // also, don't forget to expand the init:
691 let new_init_opt = init.map(|e| fld.fold_expr(e));
692 let rewritten_local =
700 SmallVector::one(@Spanned {
701 node: StmtDecl(@Spanned {
702 node: DeclLocal(rewritten_local),
709 _ => noop_fold_stmt(s, fld),
712 _ => noop_fold_stmt(s, fld),
716 // a visitor that extracts the pat_ident paths
717 // from a given thingy and puts them in a mutable
718 // array (passed in to the traversal)
720 pub struct NewNameFinderContext {
721 ident_accumulator: Vec<ast::Ident> ,
724 impl Visitor<()> for NewNameFinderContext {
725 fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
727 // we found a pat_ident!
730 node: ast::PatIdent(_, ref path, ref inner),
734 // a path of length one:
738 segments: ref segments
739 } if segments.len() == 1 => {
740 self.ident_accumulator.push(segments.get(0)
743 // I believe these must be enums...
746 // visit optional subpattern of pat_ident:
747 for subpat in inner.iter() {
748 self.visit_pat(*subpat, ())
751 // use the default traversal for non-pat_idents
752 _ => visit::walk_pat(self, pattern, ())
756 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
757 visit::walk_ty(self, typ, ())
762 // return a visitor that extracts the pat_ident paths
763 // from a given thingy and puts them in a mutable
764 // array (passed in to the traversal)
765 pub fn new_name_finder(idents: Vec<ast::Ident> ) -> NewNameFinderContext {
766 NewNameFinderContext {
767 ident_accumulator: idents,
771 // expand a block. pushes a new exts_frame, then calls expand_block_elts
772 pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
773 // see note below about treatment of exts table
774 with_exts_frame!(fld.extsbox,false,
775 expand_block_elts(blk, fld))
778 // expand the elements of a block.
779 pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
780 let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
782 b.stmts.iter().flat_map(|x| {
784 let pending_renames = &mut fld.extsbox.info().pending_renames;
785 let mut rename_fld = renames_to_fold(pending_renames);
786 rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value")
788 fld.fold_stmt(renamed_stmt).move_iter()
790 let new_expr = b.expr.map(|x| {
792 let pending_renames = &mut fld.extsbox.info().pending_renames;
793 let mut rename_fld = renames_to_fold(pending_renames);
794 rename_fld.fold_expr(x)
799 view_items: new_view_items,
802 id: fld.new_id(b.id),
808 pub struct IdentRenamer<'a> {
809 renames: &'a mut RenameList,
812 impl<'a> Folder for IdentRenamer<'a> {
813 fn fold_ident(&mut self, id: Ident) -> Ident {
814 let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
815 mtwt::new_rename(from, to, ctxt)
824 // given a mutable list of renames, return a tree-folder that applies those
826 pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
832 pub fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
833 /* this discards information in the case of macro-defining macros */
837 expn_info: cx.backtrace(),
841 pub struct MacroExpander<'a, 'b> {
843 cx: &'a mut ExtCtxt<'b>,
846 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
847 fn fold_expr(&mut self, expr: @ast::Expr) -> @ast::Expr {
848 expand_expr(expr, self)
851 fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
852 expand_item(item, self)
855 fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem {
856 expand_view_item(vi, self)
859 fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
860 expand_stmt(stmt, self)
863 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
864 expand_block(block, self)
867 fn new_span(&mut self, span: Span) -> Span {
868 new_span(self.cx, span)
872 pub struct ExpansionConfig<'a> {
873 loader: &'a mut CrateLoader,
874 deriving_hash_type_parameter: bool,
878 pub fn expand_crate(parse_sess: &parse::ParseSess,
879 cfg: ExpansionConfig,
881 let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
882 let mut expander = MacroExpander {
883 extsbox: syntax_expander_table(),
887 let ret = expander.fold_crate(c);
888 parse_sess.span_diagnostic.handler().abort_if_errors();
892 // HYGIENIC CONTEXT EXTENSION:
893 // all of these functions are for walking over
894 // ASTs and making some change to the context of every
895 // element that has one. a CtxtFn is a trait-ified
896 // version of a closure in (SyntaxContext -> SyntaxContext).
897 // the ones defined here include:
898 // Marker - add a mark to a context
900 // A Marker adds the given mark to the syntax context
901 struct Marker { mark: Mrk }
903 impl Folder for Marker {
904 fn fold_ident(&mut self, id: Ident) -> Ident {
907 ctxt: mtwt::new_mark(self.mark, id.ctxt)
910 fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
911 let macro = match m.node {
912 MacInvocTT(ref path, ref tts, ctxt) => {
913 MacInvocTT(self.fold_path(path),
914 fold_tts(tts.as_slice(), self),
915 mtwt::new_mark(self.mark, ctxt))
925 // just a convenience:
926 fn new_mark_folder(m: Mrk) -> Marker {
930 // apply a given mark to the given token trees. Used prior to expansion of a macro.
931 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
932 fold_tts(tts, &mut new_mark_folder(m))
935 // apply a given mark to the given expr. Used following the expansion of a macro.
936 fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
937 new_mark_folder(m).fold_expr(expr)
940 // apply a given mark to the given stmt. Used following the expansion of a macro.
941 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
942 new_mark_folder(m).fold_stmt(expr)
943 .expect_one("marking a stmt didn't return a stmt")
946 // apply a given mark to the given item. Used following the expansion of a macro.
947 fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
948 new_mark_folder(m).fold_item(expr)
951 fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
952 let mut relevant_info = cx.backtrace();
953 let mut einfo = relevant_info.unwrap();
955 match relevant_info {
959 relevant_info = einfo.call_site.expn_info;
970 use ast::{Attribute_, AttrOuter, MetaWord};
972 use codemap::Spanned;
973 use ext::base::{CrateLoader, MacroCrate};
977 use util::parser_testing::{string_to_parser};
978 use util::parser_testing::{string_to_pat, strs_to_idents};
982 use std::vec_ng::Vec;
984 // a visitor that extracts the paths
985 // from a given thingy and puts them in a mutable
986 // array (passed in to the traversal)
988 struct NewPathExprFinderContext {
989 path_accumulator: Vec<ast::Path> ,
992 impl Visitor<()> for NewPathExprFinderContext {
994 fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
996 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
997 self.path_accumulator.push(p.clone());
998 // not calling visit_path, should be fine.
1000 _ => visit::walk_expr(self,expr,())
1004 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
1005 visit::walk_ty(self, typ, ())
1010 // return a visitor that extracts the paths
1011 // from a given pattern and puts them in a mutable
1012 // array (passed in to the traversal)
1013 pub fn new_path_finder(paths: Vec<ast::Path> ) -> NewPathExprFinderContext {
1014 NewPathExprFinderContext {
1015 path_accumulator: paths
1021 impl CrateLoader for ErrLoader {
1022 fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
1026 fn get_exported_macros(&mut self, _: ast::CrateNum) -> Vec<~str> {
1030 fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> {
1035 // these following tests are quite fragile, in that they don't test what
1036 // *kind* of failure occurs.
1038 // make sure that macros can leave scope
1040 #[test] fn macros_cant_escape_fns_test () {
1041 let src = ~"fn bogus() {macro_rules! z (() => (3+4))}\
1042 fn inty() -> int { z!() }";
1043 let sess = parse::new_parse_sess();
1044 let crate_ast = parse::parse_crate_from_source_str(
1049 let mut loader = ErrLoader;
1050 let cfg = ::syntax::ext::expand::ExpansionConfig {
1051 loader: &mut loader,
1052 deriving_hash_type_parameter: false,
1053 crate_id: from_str("test").unwrap(),
1055 expand_crate(&sess,cfg,crate_ast);
1058 // make sure that macros can leave scope for modules
1060 #[test] fn macros_cant_escape_mods_test () {
1061 let src = ~"mod foo {macro_rules! z (() => (3+4))}\
1062 fn inty() -> int { z!() }";
1063 let sess = parse::new_parse_sess();
1064 let crate_ast = parse::parse_crate_from_source_str(
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(),
1075 expand_crate(&sess,cfg,crate_ast);
1078 // macro_escape modules shouldn't cause macros to leave scope
1079 #[test] fn macros_can_escape_flattened_mods_test () {
1080 let src = ~"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1081 fn inty() -> int { z!() }";
1082 let sess = parse::new_parse_sess();
1083 let crate_ast = parse::parse_crate_from_source_str(
1088 let mut loader = ErrLoader;
1089 let cfg = ::syntax::ext::expand::ExpansionConfig {
1090 loader: &mut loader,
1091 deriving_hash_type_parameter: false,
1092 crate_id: from_str("test").unwrap(),
1094 expand_crate(&sess, cfg, crate_ast);
1097 #[test] fn test_contains_flatten (){
1098 let attr1 = make_dummy_attr ("foo");
1099 let attr2 = make_dummy_attr ("bar");
1100 let escape_attr = make_dummy_attr ("macro_escape");
1101 let attrs1 = vec!(attr1, escape_attr, attr2);
1102 assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1103 let attrs2 = vec!(attr1,attr2);
1104 assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1107 // make a MetaWord outer attribute with the given name
1108 fn make_dummy_attr(s: &str) -> ast::Attribute {
1110 span:codemap::DUMMY_SP,
1114 node: MetaWord(token::intern_and_get_ident(s)),
1115 span: codemap::DUMMY_SP,
1117 is_sugared_doc: false,
1122 //fn fake_print_crate(krate: &ast::Crate) {
1123 // let mut out = ~std::io::stderr() as ~std::io::Writer;
1124 // let mut s = pprust::rust_printer(out, get_ident_interner());
1125 // pprust::print_crate_(&mut s, krate);
1128 fn expand_crate_str(crate_str: ~str) -> ast::Crate {
1129 let ps = parse::new_parse_sess();
1130 let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
1131 // the cfg argument actually does matter, here...
1132 let mut loader = ErrLoader;
1133 let cfg = ::syntax::ext::expand::ExpansionConfig {
1134 loader: &mut loader,
1135 deriving_hash_type_parameter: false,
1136 crate_id: from_str("test").unwrap(),
1138 expand_crate(&ps,cfg,crate_ast)
1141 //fn expand_and_resolve(crate_str: @str) -> ast::crate {
1142 //let expanded_ast = expand_crate_str(crate_str);
1143 // println!("expanded: {:?}\n",expanded_ast);
1144 //mtwt_resolve_crate(expanded_ast)
1146 //fn expand_and_resolve_and_pretty_print (crate_str: @str) -> ~str {
1147 //let resolved_ast = expand_and_resolve(crate_str);
1148 //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
1151 #[test] fn macro_tokens_should_match(){
1152 expand_crate_str(~"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
1155 // renaming tests expand a crate and then check that the bindings match
1156 // the right varrefs. The specification of the test case includes the
1157 // text of the crate, and also an array of arrays. Each element in the
1158 // outer array corresponds to a binding in the traversal of the AST
1159 // induced by visit. Each of these arrays contains a list of indexes,
1160 // interpreted as the varrefs in the varref traversal that this binding
1161 // should match. So, for instance, in a program with two bindings and
1162 // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1163 // binding should match the second two varrefs, and the second binding
1164 // should match the first varref.
1166 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1167 // names; differences in marks don't matter any more.
1169 // oog... I also want tests that check "binding-identifier-=?". That is,
1170 // not just "do these have the same name", but "do they have the same
1171 // name *and* the same marks"? Understanding this is really pretty painful.
1172 // in principle, you might want to control this boolean on a per-varref basis,
1173 // but that would make things even harder to understand, and might not be
1174 // necessary for thorough testing.
1175 type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1178 fn automatic_renaming () {
1179 let tests: Vec<RenamingTest> =
1180 vec!(// b & c should get new names throughout, in the expr too:
1181 ("fn a() -> int { let b = 13; let c = b; b+c }",
1182 vec!(vec!(0,1),vec!(2)), false),
1183 // both x's should be renamed (how is this causing a bug?)
1184 ("fn main () {let x: int = 13;x;}",
1185 vec!(vec!(0)), false),
1186 // the use of b after the + should be renamed, the other one not:
1187 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1188 vec!(vec!(1)), false),
1189 // the b before the plus should not be renamed (requires marks)
1190 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1191 vec!(vec!(1)), false),
1192 // the marks going in and out of letty should cancel, allowing that $x to
1193 // capture the one following the semicolon.
1194 // this was an awesome test case, and caught a *lot* of bugs.
1195 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1196 macro_rules! user(($x:ident) => ({letty!($x); $x}))
1197 fn main() -> int {user!(z)}",
1198 vec!(vec!(0)), false));
1199 for (idx,s) in tests.iter().enumerate() {
1200 run_renaming_test(s,idx);
1204 // run one of the renaming tests
1205 fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1206 let invalid_name = token::special_idents::invalid.name;
1207 let (teststr, bound_connections, bound_ident_check) = match *t {
1208 (ref str,ref conns, bic) => (str.to_owned(), conns.clone(), bic)
1210 let cr = expand_crate_str(teststr.to_owned());
1211 // find the bindings:
1212 let mut name_finder = new_name_finder(Vec::new());
1213 visit::walk_crate(&mut name_finder,&cr,());
1214 let bindings = name_finder.ident_accumulator;
1216 // find the varrefs:
1217 let mut path_finder = new_path_finder(Vec::new());
1218 visit::walk_crate(&mut path_finder,&cr,());
1219 let varrefs = path_finder.path_accumulator;
1221 // must be one check clause for each binding:
1222 assert_eq!(bindings.len(),bound_connections.len());
1223 for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1224 let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1225 let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1226 // shouldmatch can't name varrefs that don't exist:
1227 assert!((shouldmatch.len() == 0) ||
1228 (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1229 for (idx,varref) in varrefs.iter().enumerate() {
1230 if shouldmatch.contains(&idx) {
1231 // it should be a path of length 1, and it should
1232 // be free-identifier=? or bound-identifier=? to the given binding
1233 assert_eq!(varref.segments.len(),1);
1234 let varref_name = mtwt::resolve(varref.segments
1237 let varref_marks = mtwt::marksof(varref.segments
1242 if !(varref_name==binding_name) {
1243 println!("uh oh, should match but doesn't:");
1244 println!("varref: {:?}",varref);
1245 println!("binding: {:?}", *bindings.get(binding_idx));
1246 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1248 assert_eq!(varref_name,binding_name);
1249 if bound_ident_check {
1250 // we're checking bound-identifier=?, and the marks
1251 // should be the same, too:
1252 assert_eq!(varref_marks,binding_marks.clone());
1255 let fail = (varref.segments.len() == 1)
1256 && (mtwt::resolve(varref.segments.get(0).identifier)
1260 println!("failure on test {}",test_idx);
1261 println!("text of test case: \"{}\"", teststr);
1263 println!("uh oh, matches but shouldn't:");
1264 println!("varref: {:?}",varref);
1265 // good lord, you can't make a path with 0 segments, can you?
1266 let string = token::get_ident(varref.segments
1269 println!("varref's first segment's uint: {}, and string: \"{}\"",
1270 varref.segments.get(0).identifier.name,
1272 println!("binding: {:?}", *bindings.get(binding_idx));
1273 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1281 #[test] fn fmt_in_macro_used_inside_module_macro() {
1282 let crate_str = ~"macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
1283 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1286 let cr = expand_crate_str(crate_str);
1287 // find the xx binding
1288 let mut name_finder = new_name_finder(Vec::new());
1289 visit::walk_crate(&mut name_finder, &cr, ());
1290 let bindings = name_finder.ident_accumulator;
1292 let cxbinds: Vec<&ast::Ident> =
1293 bindings.iter().filter(|b| {
1294 let ident = token::get_ident(**b);
1295 let string = ident.get();
1298 let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1299 let cxbind = match cxbinds {
1301 _ => fail!("expected just one binding for ext_cx")
1303 let resolved_binding = mtwt::resolve(*cxbind);
1304 // find all the xx varrefs:
1305 let mut path_finder = new_path_finder(Vec::new());
1306 visit::walk_crate(&mut path_finder, &cr, ());
1307 let varrefs = path_finder.path_accumulator;
1309 // the xx binding should bind all of the xx varrefs:
1310 for (idx,v) in varrefs.iter().filter(|p| {
1311 p.segments.len() == 1
1312 && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1314 if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1315 println!("uh oh, xx binding didn't match xx varref:");
1316 println!("this is xx varref \\# {:?}",idx);
1317 println!("binding: {:?}",cxbind);
1318 println!("resolves to: {:?}",resolved_binding);
1319 println!("varref: {:?}",v.segments.get(0).identifier);
1320 println!("resolves to: {:?}",
1321 mtwt::resolve(v.segments.get(0).identifier));
1322 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1324 assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1331 let pat = string_to_pat(~"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
1332 let mut pat_idents = new_name_finder(Vec::new());
1333 pat_idents.visit_pat(pat, ());
1334 assert_eq!(pat_idents.ident_accumulator,
1335 strs_to_idents(vec!("a","c","b","d")));