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};
25 use parse::token::{fresh_mark, fresh_name, intern};
29 use util::small_vector::SmallVector;
32 use std::unstable::dynamic_lib::DynamicLibrary;
36 pub fn expand_expr(e: @ast::Expr, fld: &mut MacroExpander) -> @ast::Expr {
38 // expr_mac should really be expr_ext or something; it's the
39 // entry-point for all syntax extensions.
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
48 MacInvocTT(ref pth, ref tts, _) => {
49 if pth.segments.len() > 1u {
52 format!("expected macro name without module \
54 // let compilation continue
55 return MacResult::raw_dummy_expr(e.span);
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) {
64 format!("macro undefined: '{}'",
67 // let compilation continue
68 return MacResult::raw_dummy_expr(e.span);
70 Some(&NormalTT(ref expandfun, exp_span)) => {
71 fld.cx.bt_push(ExpnInfo {
74 name: extnamestr.get().to_str(),
79 let fm = fresh_mark();
81 let marked_before = mark_tts(tts.as_slice(), fm);
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
87 let mac_span = original_span(fld.cx);
89 let expanded = match expandfun.expand(fld.cx,
91 marked_before.as_slice()) {
93 MRAny(any_macro) => any_macro.make_expr(),
98 "non-expr macro in expr pos: {}",
102 return MacResult::raw_dummy_expr(e.span);
107 mark_expr(expanded,fm)
112 format!("'{}' is not a tt-style macro",
115 return MacResult::raw_dummy_expr(e.span);
119 // Keep going, outside-in.
121 // FIXME(pcwalton): Is it necessary to clone the
124 fld.fold_expr(marked_after).node.clone();
128 id: ast::DUMMY_NODE_ID,
129 node: fully_expanded,
136 // Desugar expr_for_loop
137 // From: `['<ident>:] for <src_pat> in <src_expr> <src_loop_block>`
138 // FIXME #6993: change type of opt_ident to Option<Name>
139 ast::ExprForLoop(src_pat, src_expr, src_loop_block, opt_ident) => {
140 // Expand any interior macros etc.
141 // NB: we don't fold pats yet. Curious.
142 let src_expr = fld.fold_expr(src_expr).clone();
143 let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
149 // match &mut <src_expr> {
151 // ['<ident>:] loop {
154 // Some(<src_pat>) => <src_loop_block>
160 let local_ident = token::gensym_ident("i");
161 let next_ident = fld.cx.ident_of("next");
162 let none_ident = fld.cx.ident_of("None");
164 let local_path = fld.cx.path_ident(span, local_ident);
165 let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some"));
167 // `None => break ['<ident>];`
169 let break_expr = fld.cx.expr(span, ast::ExprBreak(opt_ident));
170 let none_pat = fld.cx.pat_ident(span, none_ident);
171 fld.cx.arm(span, vec!(none_pat), break_expr)
174 // `Some(<src_pat>) => <src_loop_block>`
177 vec!(fld.cx.pat_enum(span, some_path, vec!(src_pat))),
178 fld.cx.expr_block(src_loop_block));
180 // `match i.next() { ... }`
183 fld.cx.expr_method_call(span,
184 fld.cx.expr_path(local_path),
188 fld.cx.expr_match(span, next_call_expr, vec!(none_arm, some_arm))
191 // ['ident:] loop { ... }
192 let loop_expr = fld.cx.expr(span,
193 ast::ExprLoop(fld.cx.block_expr(match_expr),
196 // `i => loop { ... }`
198 // `match &mut <src_expr> { i => loop { ... } }`
199 let discrim = fld.cx.expr_mut_addr_of(span, src_expr);
200 let i_pattern = fld.cx.pat_ident(span, local_ident);
201 let arm = fld.cx.arm(span, vec!(i_pattern), loop_expr);
202 fld.cx.expr_match(span, discrim, vec!(arm))
205 ast::ExprLoop(loop_block, opt_ident) => {
206 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
207 fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
210 _ => noop_fold_expr(e, fld)
214 // Rename loop label and expand its loop body
216 // The renaming procedure for loop is different in the sense that the loop
217 // body is in a block enclosed by loop head so the renaming of loop label
218 // must be propagated to the enclosed context.
219 fn expand_loop_block(loop_block: P<Block>,
220 opt_ident: Option<Ident>,
221 fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
224 let new_label = fresh_name(&label);
225 let rename = (label, new_label);
227 // The rename *must not* be added to the pending list of current
228 // syntax context otherwise an unrelated `break` or `continue` in
229 // the same context will pick that up in the deferred renaming pass
230 // and be renamed incorrectly.
231 let mut rename_list = vec!(rename);
232 let mut rename_fld = renames_to_fold(&mut rename_list);
233 let renamed_ident = rename_fld.fold_ident(label);
235 // The rename *must* be added to the enclosed syntax context for
236 // `break` or `continue` to pick up because by definition they are
237 // in a block enclosed by loop head.
238 fld.extsbox.push_frame();
239 fld.extsbox.info().pending_renames.push(rename);
240 let expanded_block = expand_block_elts(loop_block, fld);
241 fld.extsbox.pop_frame();
243 (expanded_block, Some(renamed_ident))
245 None => (fld.fold_block(loop_block), opt_ident)
249 // eval $e with a new exts frame:
250 macro_rules! with_exts_frame (
251 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
252 ({$extsboxexpr.push_frame();
253 $extsboxexpr.info().macros_escape = $macros_escape;
255 $extsboxexpr.pop_frame();
260 // When we enter a module, record it, for the sake of `module!`
261 pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
262 -> SmallVector<@ast::Item> {
263 let it = expand_item_modifiers(it, fld);
265 let mut decorator_items = SmallVector::zero();
266 for attr in it.attrs.rev_iter() {
267 let mname = attr.name();
269 match fld.extsbox.find(&intern(mname.get())) {
270 Some(&ItemDecorator(dec_fn)) => {
271 fld.cx.bt_push(ExpnInfo {
272 call_site: attr.span,
273 callee: NameAndSpan {
274 name: mname.get().to_str(),
275 format: MacroAttribute,
280 // we'd ideally decorator_items.push_all(expand_item(item, fld)),
281 // but that double-mut-borrows fld
282 let mut items: SmallVector<@ast::Item> = SmallVector::zero();
283 dec_fn(fld.cx, attr.span, attr.node.value, it,
284 |item| items.push(item));
285 decorator_items.extend(&mut items.move_iter()
286 .flat_map(|item| expand_item(item, fld).move_iter()));
294 let mut new_items = match it.node {
295 ast::ItemMac(..) => expand_item_mac(it, fld),
296 ast::ItemMod(_) | ast::ItemForeignMod(_) => {
297 fld.cx.mod_push(it.ident);
298 let macro_escape = contains_macro_escape(it.attrs.as_slice());
299 let result = with_exts_frame!(fld.extsbox,
301 noop_fold_item(it, fld));
305 _ => noop_fold_item(it, fld)
308 new_items.push_all(decorator_items);
312 fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
314 let (modifiers, attrs) = it.attrs.partitioned(|attr| {
315 match fld.extsbox.find(&intern(attr.name().get())) {
316 Some(&ItemModifier(_)) => true,
326 if modifiers.is_empty() {
330 for attr in modifiers.iter() {
331 let mname = attr.name();
333 match fld.extsbox.find(&intern(mname.get())) {
334 Some(&ItemModifier(dec_fn)) => {
335 fld.cx.bt_push(ExpnInfo {
336 call_site: attr.span,
337 callee: NameAndSpan {
338 name: mname.get().to_str(),
339 format: MacroAttribute,
343 it = dec_fn(fld.cx, attr.span, attr.node.value, it);
350 // expansion may have added new ItemModifiers
351 expand_item_modifiers(it, fld)
354 // does this attribute list contain "macro_escape" ?
355 pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
356 attr::contains_name(attrs, "macro_escape")
359 // Support for item-position macro invocations, exactly the same
360 // logic as for expression-position macro invocations.
361 pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
362 -> SmallVector<@ast::Item> {
363 let (pth, tts) = match it.node {
364 ItemMac(codemap::Spanned {
365 node: MacInvocTT(ref pth, ref tts, _),
368 (pth, (*tts).clone())
370 _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
373 let extname = pth.segments.get(0).identifier;
374 let extnamestr = token::get_ident(extname);
375 let fm = fresh_mark();
376 let expanded = match fld.extsbox.find(&extname.name) {
378 fld.cx.span_err(pth.span,
379 format!("macro undefined: '{}!'",
381 // let compilation continue
382 return SmallVector::zero();
385 Some(&NormalTT(ref expander, span)) => {
386 if it.ident.name != parse::token::special_idents::invalid.name {
387 fld.cx.span_err(pth.span,
388 format!("macro {}! expects no ident argument, \
391 token::get_ident(it.ident)));
392 return SmallVector::zero();
394 fld.cx.bt_push(ExpnInfo {
396 callee: NameAndSpan {
397 name: extnamestr.get().to_str(),
402 // mark before expansion:
403 let marked_before = mark_tts(tts.as_slice(), fm);
404 expander.expand(fld.cx, it.span, marked_before.as_slice())
406 Some(&IdentTT(ref expander, span)) => {
407 if it.ident.name == parse::token::special_idents::invalid.name {
408 fld.cx.span_err(pth.span,
409 format!("macro {}! expects an ident argument",
411 return SmallVector::zero();
413 fld.cx.bt_push(ExpnInfo {
415 callee: NameAndSpan {
416 name: extnamestr.get().to_str(),
421 // mark before expansion:
422 let marked_tts = mark_tts(tts.as_slice(), fm);
423 expander.expand(fld.cx, it.span, it.ident, marked_tts)
426 fld.cx.span_err(it.span,
427 format!("{}! is not legal in item position",
429 return SmallVector::zero();
433 let items = match expanded {
435 mark_item(it,fm).move_iter()
436 .flat_map(|i| fld.fold_item(i).move_iter())
440 fld.cx.span_err(pth.span,
441 format!("expr macro in item position: {}",
443 return SmallVector::zero();
445 MRAny(any_macro) => {
446 any_macro.make_items().move_iter()
447 .flat_map(|i| mark_item(i, fm).move_iter())
448 .flat_map(|i| fld.fold_item(i).move_iter())
451 MRDef(MacroDef { name, ext }) => {
452 // yikes... no idea how to apply the mark to this. I'm afraid
453 // we're going to have to wait-and-see on this one.
454 fld.extsbox.insert(intern(name), ext);
455 if attr::contains_name(it.attrs.as_slice(), "macro_export") {
466 // load macros from syntax-phase crates
467 pub fn expand_view_item(vi: &ast::ViewItem,
468 fld: &mut MacroExpander)
471 ast::ViewItemExternCrate(..) => {
472 let should_load = vi.attrs.iter().any(|attr| {
473 attr.name().get() == "phase" &&
474 attr.meta_item_list().map_or(false, |phases| {
475 attr::contains_name(phases, "syntax")
480 load_extern_macros(vi, fld);
483 ast::ViewItemUse(_) => {}
486 noop_fold_view_item(vi, fld)
489 fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
490 let MacroCrate { lib, cnum } = fld.cx.ecfg.loader.load_crate(krate);
492 let crate_name = match krate.node {
493 ast::ViewItemExternCrate(name, _, _) => name,
496 let name = format!("<{} macros>", token::get_ident(crate_name));
498 let exported_macros = fld.cx.ecfg.loader.get_exported_macros(cnum);
499 for source in exported_macros.iter() {
500 let item = parse::parse_item_from_source_str(name.clone(),
504 .expect("expected a serialized item");
505 expand_item_mac(item, fld);
508 let path = match lib {
512 // Make sure the path contains a / or the linker will search for it.
513 let path = os::make_absolute(&path);
515 let registrar = match fld.cx.ecfg.loader.get_registrar_symbol(cnum) {
516 Some(registrar) => registrar,
520 let lib = match DynamicLibrary::open(Some(&path)) {
522 // this is fatal: there are almost certainly macros we need
523 // inside this crate, so continue would spew "macro undefined"
525 Err(err) => fld.cx.span_fatal(krate.span, err)
529 let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) {
530 Ok(registrar) => registrar,
531 // again fatal if we can't register macros
532 Err(err) => fld.cx.span_fatal(krate.span, err)
534 registrar(|name, extension| {
535 let extension = match extension {
536 NormalTT(ext, _) => NormalTT(ext, Some(krate.span)),
537 IdentTT(ext, _) => IdentTT(ext, Some(krate.span)),
538 ItemDecorator(ext) => ItemDecorator(ext),
539 ItemModifier(ext) => ItemModifier(ext),
541 fld.extsbox.insert(name, extension);
544 // Intentionally leak the dynamic library. We can't ever unload it
545 // since the library can do things that will outlive the expansion
546 // phase (e.g. make an @-box cycle or launch a task).
552 pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
553 // why the copying here and not in expand_expr?
554 // looks like classic changed-in-only-one-place
555 let (pth, tts, semi) = match s.node {
556 StmtMac(ref mac, semi) => {
558 MacInvocTT(ref pth, ref tts, _) => {
559 (pth, (*tts).clone(), semi)
563 _ => return expand_non_macro_stmt(s, fld)
565 if pth.segments.len() > 1u {
566 fld.cx.span_err(pth.span, "expected macro name without module separators");
567 return SmallVector::zero();
569 let extname = pth.segments.get(0).identifier;
570 let extnamestr = token::get_ident(extname);
571 let marked_after = match fld.extsbox.find(&extname.name) {
573 fld.cx.span_err(pth.span, format!("macro undefined: '{}'", extnamestr));
574 return SmallVector::zero();
577 Some(&NormalTT(ref expandfun, exp_span)) => {
578 fld.cx.bt_push(ExpnInfo {
580 callee: NameAndSpan {
581 name: extnamestr.get().to_str(),
586 let fm = fresh_mark();
587 // mark before expansion:
588 let marked_tts = mark_tts(tts.as_slice(), fm);
590 // See the comment in expand_expr for why we want the original span,
591 // not the current mac.span.
592 let mac_span = original_span(fld.cx);
594 let expanded = match expandfun.expand(fld.cx,
596 marked_tts.as_slice()) {
599 node: StmtExpr(e, ast::DUMMY_NODE_ID),
603 MRAny(any_macro) => any_macro.make_stmt(),
605 fld.cx.span_err(pth.span,
606 format!("non-stmt macro in stmt pos: {}",
608 return SmallVector::zero();
612 mark_stmt(expanded,fm)
616 fld.cx.span_err(pth.span, format!("'{}' is not a tt-style macro",
618 return SmallVector::zero();
622 // Keep going, outside-in.
623 let fully_expanded = fld.fold_stmt(marked_after);
624 if fully_expanded.is_empty() {
625 fld.cx.span_err(pth.span, "macro didn't expand to a statement");
626 return SmallVector::zero();
629 let fully_expanded: SmallVector<@Stmt> = fully_expanded.move_iter()
630 .map(|s| @Spanned { span: s.span, node: s.node.clone() })
633 fully_expanded.move_iter().map(|s| {
635 StmtExpr(e, stmt_id) if semi => {
638 node: StmtSemi(e, stmt_id)
641 _ => s /* might already have a semi */
646 // expand a non-macro stmt. this is essentially the fallthrough for
647 // expand_stmt, above.
648 fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
649 -> SmallVector<@Stmt> {
652 StmtDecl(decl, node_id) => {
655 node: DeclLocal(ref local),
666 // expand the pat (it might contain exprs... #:(o)>
667 let expanded_pat = fld.fold_pat(pat);
668 // find the pat_idents in the pattern:
669 // oh dear heaven... this is going to include the enum
670 // names, as well... but that should be okay, as long as
671 // the new names are gensyms for the old ones.
672 let mut name_finder = new_name_finder(Vec::new());
673 name_finder.visit_pat(expanded_pat,());
674 // generate fresh names, push them to a new pending list
675 let mut new_pending_renames = Vec::new();
676 for ident in name_finder.ident_accumulator.iter() {
677 let new_name = fresh_name(ident);
678 new_pending_renames.push((*ident,new_name));
680 let rewritten_pat = {
682 renames_to_fold(&mut new_pending_renames);
683 // rewrite the pattern using the new names (the old
684 // ones have already been applied):
685 rename_fld.fold_pat(expanded_pat)
687 // add them to the existing pending renames:
688 fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
689 // also, don't forget to expand the init:
690 let new_init_opt = init.map(|e| fld.fold_expr(e));
691 let rewritten_local =
699 SmallVector::one(@Spanned {
700 node: StmtDecl(@Spanned {
701 node: DeclLocal(rewritten_local),
708 _ => noop_fold_stmt(s, fld),
711 _ => noop_fold_stmt(s, fld),
715 // a visitor that extracts the pat_ident paths
716 // from a given thingy and puts them in a mutable
717 // array (passed in to the traversal)
719 pub struct NewNameFinderContext {
720 ident_accumulator: Vec<ast::Ident> ,
723 impl Visitor<()> for NewNameFinderContext {
724 fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
726 // we found a pat_ident!
729 node: ast::PatIdent(_, ref path, ref inner),
733 // a path of length one:
737 segments: ref segments
738 } if segments.len() == 1 => {
739 self.ident_accumulator.push(segments.get(0)
742 // I believe these must be enums...
745 // visit optional subpattern of pat_ident:
746 for subpat in inner.iter() {
747 self.visit_pat(*subpat, ())
750 // use the default traversal for non-pat_idents
751 _ => visit::walk_pat(self, pattern, ())
755 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
756 visit::walk_ty(self, typ, ())
761 // return a visitor that extracts the pat_ident paths
762 // from a given thingy and puts them in a mutable
763 // array (passed in to the traversal)
764 pub fn new_name_finder(idents: Vec<ast::Ident> ) -> NewNameFinderContext {
765 NewNameFinderContext {
766 ident_accumulator: idents,
770 // expand a block. pushes a new exts_frame, then calls expand_block_elts
771 pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
772 // see note below about treatment of exts table
773 with_exts_frame!(fld.extsbox,false,
774 expand_block_elts(blk, fld))
777 // expand the elements of a block.
778 pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
779 let new_view_items = b.view_items.map(|x| fld.fold_view_item(x));
781 b.stmts.iter().flat_map(|x| {
783 let pending_renames = &mut fld.extsbox.info().pending_renames;
784 let mut rename_fld = renames_to_fold(pending_renames);
785 rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value")
787 fld.fold_stmt(renamed_stmt).move_iter()
789 let new_expr = b.expr.map(|x| {
791 let pending_renames = &mut fld.extsbox.info().pending_renames;
792 let mut rename_fld = renames_to_fold(pending_renames);
793 rename_fld.fold_expr(x)
798 view_items: new_view_items,
801 id: fld.new_id(b.id),
807 pub struct IdentRenamer<'a> {
808 renames: &'a mut RenameList,
811 impl<'a> Folder for IdentRenamer<'a> {
812 fn fold_ident(&mut self, id: Ident) -> Ident {
813 let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
814 mtwt::new_rename(from, to, ctxt)
823 // given a mutable list of renames, return a tree-folder that applies those
825 pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
831 pub fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
832 /* this discards information in the case of macro-defining macros */
836 expn_info: cx.backtrace(),
840 pub struct MacroExpander<'a> {
842 cx: &'a mut ExtCtxt<'a>,
845 impl<'a> Folder for MacroExpander<'a> {
846 fn fold_expr(&mut self, expr: @ast::Expr) -> @ast::Expr {
847 expand_expr(expr, self)
850 fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
851 expand_item(item, self)
854 fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem {
855 expand_view_item(vi, self)
858 fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
859 expand_stmt(stmt, self)
862 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
863 expand_block(block, self)
866 fn new_span(&mut self, span: Span) -> Span {
867 new_span(self.cx, span)
871 pub struct ExpansionConfig<'a> {
872 loader: &'a mut CrateLoader,
873 deriving_hash_type_parameter: bool,
876 pub fn expand_crate(parse_sess: @parse::ParseSess,
877 cfg: ExpansionConfig,
879 let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
880 let mut expander = MacroExpander {
881 extsbox: syntax_expander_table(),
885 let ret = expander.fold_crate(c);
886 parse_sess.span_diagnostic.handler().abort_if_errors();
890 // HYGIENIC CONTEXT EXTENSION:
891 // all of these functions are for walking over
892 // ASTs and making some change to the context of every
893 // element that has one. a CtxtFn is a trait-ified
894 // version of a closure in (SyntaxContext -> SyntaxContext).
895 // the ones defined here include:
896 // Marker - add a mark to a context
898 // A Marker adds the given mark to the syntax context
899 struct Marker { mark: Mrk }
901 impl Folder for Marker {
902 fn fold_ident(&mut self, id: Ident) -> Ident {
905 ctxt: mtwt::new_mark(self.mark, id.ctxt)
908 fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
909 let macro = match m.node {
910 MacInvocTT(ref path, ref tts, ctxt) => {
911 MacInvocTT(self.fold_path(path),
912 fold_tts(tts.as_slice(), self),
913 mtwt::new_mark(self.mark, ctxt))
923 // just a convenience:
924 fn new_mark_folder(m: Mrk) -> Marker {
928 // apply a given mark to the given token trees. Used prior to expansion of a macro.
929 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
930 fold_tts(tts, &mut new_mark_folder(m))
933 // apply a given mark to the given expr. Used following the expansion of a macro.
934 fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
935 new_mark_folder(m).fold_expr(expr)
938 // apply a given mark to the given stmt. Used following the expansion of a macro.
939 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
940 new_mark_folder(m).fold_stmt(expr)
941 .expect_one("marking a stmt didn't return a stmt")
944 // apply a given mark to the given item. Used following the expansion of a macro.
945 fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
946 new_mark_folder(m).fold_item(expr)
949 fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
950 let mut relevant_info = cx.backtrace();
951 let mut einfo = relevant_info.unwrap();
953 match relevant_info {
957 relevant_info = einfo.call_site.expn_info;
968 use ast::{Attribute_, AttrOuter, MetaWord};
970 use codemap::Spanned;
971 use ext::base::{CrateLoader, MacroCrate};
975 use util::parser_testing::{string_to_crate_and_sess};
976 use util::parser_testing::{string_to_pat, strs_to_idents};
980 use std::vec_ng::Vec;
982 // a visitor that extracts the paths
983 // from a given thingy and puts them in a mutable
984 // array (passed in to the traversal)
986 struct NewPathExprFinderContext {
987 path_accumulator: Vec<ast::Path> ,
990 impl Visitor<()> for NewPathExprFinderContext {
992 fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
994 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
995 self.path_accumulator.push(p.clone());
996 // not calling visit_path, should be fine.
998 _ => visit::walk_expr(self,expr,())
1002 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
1003 visit::walk_ty(self, typ, ())
1008 // return a visitor that extracts the paths
1009 // from a given pattern and puts them in a mutable
1010 // array (passed in to the traversal)
1011 pub fn new_path_finder(paths: Vec<ast::Path> ) -> NewPathExprFinderContext {
1012 NewPathExprFinderContext {
1013 path_accumulator: paths
1019 impl CrateLoader for ErrLoader {
1020 fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
1024 fn get_exported_macros(&mut self, _: ast::CrateNum) -> Vec<~str> {
1028 fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> {
1033 // these following tests are quite fragile, in that they don't test what
1034 // *kind* of failure occurs.
1036 // make sure that macros can leave scope
1038 #[test] fn macros_cant_escape_fns_test () {
1039 let src = ~"fn bogus() {macro_rules! z (() => (3+4))}\
1040 fn inty() -> int { z!() }";
1041 let sess = parse::new_parse_sess();
1042 let crate_ast = parse::parse_crate_from_source_str(
1047 let mut loader = ErrLoader;
1048 let cfg = ::syntax::ext::expand::ExpansionConfig {
1049 loader: &mut loader,
1050 deriving_hash_type_parameter: false,
1052 expand_crate(sess,cfg,crate_ast);
1055 // make sure that macros can leave scope for modules
1057 #[test] fn macros_cant_escape_mods_test () {
1058 let src = ~"mod foo {macro_rules! z (() => (3+4))}\
1059 fn inty() -> int { z!() }";
1060 let sess = parse::new_parse_sess();
1061 let crate_ast = parse::parse_crate_from_source_str(
1066 let mut loader = ErrLoader;
1067 let cfg = ::syntax::ext::expand::ExpansionConfig {
1068 loader: &mut loader,
1069 deriving_hash_type_parameter: false,
1071 expand_crate(sess,cfg,crate_ast);
1074 // macro_escape modules shouldn't cause macros to leave scope
1075 #[test] fn macros_can_escape_flattened_mods_test () {
1076 let src = ~"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1077 fn inty() -> int { z!() }";
1078 let sess = parse::new_parse_sess();
1079 let crate_ast = parse::parse_crate_from_source_str(
1084 let mut loader = ErrLoader;
1085 let cfg = ::syntax::ext::expand::ExpansionConfig {
1086 loader: &mut loader,
1087 deriving_hash_type_parameter: false,
1089 expand_crate(sess, cfg, crate_ast);
1092 #[test] fn test_contains_flatten (){
1093 let attr1 = make_dummy_attr ("foo");
1094 let attr2 = make_dummy_attr ("bar");
1095 let escape_attr = make_dummy_attr ("macro_escape");
1096 let attrs1 = vec!(attr1, escape_attr, attr2);
1097 assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1098 let attrs2 = vec!(attr1,attr2);
1099 assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1102 // make a MetaWord outer attribute with the given name
1103 fn make_dummy_attr(s: &str) -> ast::Attribute {
1105 span:codemap::DUMMY_SP,
1109 node: MetaWord(token::intern_and_get_ident(s)),
1110 span: codemap::DUMMY_SP,
1112 is_sugared_doc: false,
1117 //fn fake_print_crate(krate: &ast::Crate) {
1118 // let mut out = ~std::io::stderr() as ~std::io::Writer;
1119 // let mut s = pprust::rust_printer(out, get_ident_interner());
1120 // pprust::print_crate_(&mut s, krate);
1123 fn expand_crate_str(crate_str: ~str) -> ast::Crate {
1124 let (crate_ast,ps) = string_to_crate_and_sess(crate_str);
1125 // the cfg argument actually does matter, here...
1126 let mut loader = ErrLoader;
1127 let cfg = ::syntax::ext::expand::ExpansionConfig {
1128 loader: &mut loader,
1129 deriving_hash_type_parameter: false,
1131 expand_crate(ps,cfg,crate_ast)
1134 //fn expand_and_resolve(crate_str: @str) -> ast::crate {
1135 //let expanded_ast = expand_crate_str(crate_str);
1136 // println!("expanded: {:?}\n",expanded_ast);
1137 //mtwt_resolve_crate(expanded_ast)
1139 //fn expand_and_resolve_and_pretty_print (crate_str: @str) -> ~str {
1140 //let resolved_ast = expand_and_resolve(crate_str);
1141 //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
1144 #[test] fn macro_tokens_should_match(){
1145 expand_crate_str(~"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
1148 // renaming tests expand a crate and then check that the bindings match
1149 // the right varrefs. The specification of the test case includes the
1150 // text of the crate, and also an array of arrays. Each element in the
1151 // outer array corresponds to a binding in the traversal of the AST
1152 // induced by visit. Each of these arrays contains a list of indexes,
1153 // interpreted as the varrefs in the varref traversal that this binding
1154 // should match. So, for instance, in a program with two bindings and
1155 // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1156 // binding should match the second two varrefs, and the second binding
1157 // should match the first varref.
1159 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1160 // names; differences in marks don't matter any more.
1162 // oog... I also want tests that check "binding-identifier-=?". That is,
1163 // not just "do these have the same name", but "do they have the same
1164 // name *and* the same marks"? Understanding this is really pretty painful.
1165 // in principle, you might want to control this boolean on a per-varref basis,
1166 // but that would make things even harder to understand, and might not be
1167 // necessary for thorough testing.
1168 type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1171 fn automatic_renaming () {
1172 let tests: Vec<RenamingTest> =
1173 vec!(// b & c should get new names throughout, in the expr too:
1174 ("fn a() -> int { let b = 13; let c = b; b+c }",
1175 vec!(vec!(0,1),vec!(2)), false),
1176 // both x's should be renamed (how is this causing a bug?)
1177 ("fn main () {let x: int = 13;x;}",
1178 vec!(vec!(0)), false),
1179 // the use of b after the + should be renamed, the other one not:
1180 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1181 vec!(vec!(1)), false),
1182 // the b before the plus should not be renamed (requires marks)
1183 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1184 vec!(vec!(1)), false),
1185 // the marks going in and out of letty should cancel, allowing that $x to
1186 // capture the one following the semicolon.
1187 // this was an awesome test case, and caught a *lot* of bugs.
1188 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1189 macro_rules! user(($x:ident) => ({letty!($x); $x}))
1190 fn main() -> int {user!(z)}",
1191 vec!(vec!(0)), false));
1192 for (idx,s) in tests.iter().enumerate() {
1193 run_renaming_test(s,idx);
1197 // run one of the renaming tests
1198 fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1199 let invalid_name = token::special_idents::invalid.name;
1200 let (teststr, bound_connections, bound_ident_check) = match *t {
1201 (ref str,ref conns, bic) => (str.to_owned(), conns.clone(), bic)
1203 let cr = expand_crate_str(teststr.to_owned());
1204 // find the bindings:
1205 let mut name_finder = new_name_finder(Vec::new());
1206 visit::walk_crate(&mut name_finder,&cr,());
1207 let bindings = name_finder.ident_accumulator;
1209 // find the varrefs:
1210 let mut path_finder = new_path_finder(Vec::new());
1211 visit::walk_crate(&mut path_finder,&cr,());
1212 let varrefs = path_finder.path_accumulator;
1214 // must be one check clause for each binding:
1215 assert_eq!(bindings.len(),bound_connections.len());
1216 for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1217 let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1218 let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1219 // shouldmatch can't name varrefs that don't exist:
1220 assert!((shouldmatch.len() == 0) ||
1221 (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1222 for (idx,varref) in varrefs.iter().enumerate() {
1223 if shouldmatch.contains(&idx) {
1224 // it should be a path of length 1, and it should
1225 // be free-identifier=? or bound-identifier=? to the given binding
1226 assert_eq!(varref.segments.len(),1);
1227 let varref_name = mtwt::resolve(varref.segments
1230 let varref_marks = mtwt::marksof(varref.segments
1235 if !(varref_name==binding_name) {
1236 println!("uh oh, should match but doesn't:");
1237 println!("varref: {:?}",varref);
1238 println!("binding: {:?}", *bindings.get(binding_idx));
1239 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1241 assert_eq!(varref_name,binding_name);
1242 if bound_ident_check {
1243 // we're checking bound-identifier=?, and the marks
1244 // should be the same, too:
1245 assert_eq!(varref_marks,binding_marks.clone());
1248 let fail = (varref.segments.len() == 1)
1249 && (mtwt::resolve(varref.segments.get(0).identifier)
1253 println!("failure on test {}",test_idx);
1254 println!("text of test case: \"{}\"", teststr);
1256 println!("uh oh, matches but shouldn't:");
1257 println!("varref: {:?}",varref);
1258 // good lord, you can't make a path with 0 segments, can you?
1259 let string = token::get_ident(varref.segments
1262 println!("varref's first segment's uint: {}, and string: \"{}\"",
1263 varref.segments.get(0).identifier.name,
1265 println!("binding: {:?}", *bindings.get(binding_idx));
1266 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1274 #[test] fn fmt_in_macro_used_inside_module_macro() {
1275 let crate_str = ~"macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
1276 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1279 let cr = expand_crate_str(crate_str);
1280 // find the xx binding
1281 let mut name_finder = new_name_finder(Vec::new());
1282 visit::walk_crate(&mut name_finder, &cr, ());
1283 let bindings = name_finder.ident_accumulator;
1285 let cxbinds: Vec<&ast::Ident> =
1286 bindings.iter().filter(|b| {
1287 let ident = token::get_ident(**b);
1288 let string = ident.get();
1291 let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1292 let cxbind = match cxbinds {
1294 _ => fail!("expected just one binding for ext_cx")
1296 let resolved_binding = mtwt::resolve(*cxbind);
1297 // find all the xx varrefs:
1298 let mut path_finder = new_path_finder(Vec::new());
1299 visit::walk_crate(&mut path_finder, &cr, ());
1300 let varrefs = path_finder.path_accumulator;
1302 // the xx binding should bind all of the xx varrefs:
1303 for (idx,v) in varrefs.iter().filter(|p| {
1304 p.segments.len() == 1
1305 && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1307 if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1308 println!("uh oh, xx binding didn't match xx varref:");
1309 println!("this is xx varref \\# {:?}",idx);
1310 println!("binding: {:?}",cxbind);
1311 println!("resolves to: {:?}",resolved_binding);
1312 println!("varref: {:?}",v.segments.get(0).identifier);
1313 println!("resolves to: {:?}",
1314 mtwt::resolve(v.segments.get(0).identifier));
1315 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1317 assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1324 let pat = string_to_pat(~"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
1325 let mut pat_idents = new_name_finder(Vec::new());
1326 pat_idents.visit_pat(pat, ());
1327 assert_eq!(pat_idents.ident_accumulator,
1328 strs_to_idents(vec!("a","c","b","d")));