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;
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.iter().rev() {
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(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, macros, registrar_symbol } =
491 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 for source in 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 registrar_symbol {
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.iter().map(|x| fld.fold_view_item(x)).collect();
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, 'b> {
841 pub extsbox: SyntaxEnv,
842 pub cx: &'a mut ExtCtxt<'b>,
845 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
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 pub loader: &'a mut CrateLoader,
873 pub deriving_hash_type_parameter: bool,
874 pub crate_id: CrateId,
877 pub fn expand_crate(parse_sess: &parse::ParseSess,
878 cfg: ExpansionConfig,
880 let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
881 let mut expander = MacroExpander {
882 extsbox: syntax_expander_table(),
886 let ret = expander.fold_crate(c);
887 parse_sess.span_diagnostic.handler().abort_if_errors();
891 // HYGIENIC CONTEXT EXTENSION:
892 // all of these functions are for walking over
893 // ASTs and making some change to the context of every
894 // element that has one. a CtxtFn is a trait-ified
895 // version of a closure in (SyntaxContext -> SyntaxContext).
896 // the ones defined here include:
897 // Marker - add a mark to a context
899 // A Marker adds the given mark to the syntax context
900 struct Marker { mark: Mrk }
902 impl Folder for Marker {
903 fn fold_ident(&mut self, id: Ident) -> Ident {
906 ctxt: mtwt::new_mark(self.mark, id.ctxt)
909 fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
910 let macro = match m.node {
911 MacInvocTT(ref path, ref tts, ctxt) => {
912 MacInvocTT(self.fold_path(path),
913 fold_tts(tts.as_slice(), self),
914 mtwt::new_mark(self.mark, ctxt))
924 // just a convenience:
925 fn new_mark_folder(m: Mrk) -> Marker {
929 // apply a given mark to the given token trees. Used prior to expansion of a macro.
930 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
931 fold_tts(tts, &mut new_mark_folder(m))
934 // apply a given mark to the given expr. Used following the expansion of a macro.
935 fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
936 new_mark_folder(m).fold_expr(expr)
939 // apply a given mark to the given stmt. Used following the expansion of a macro.
940 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
941 new_mark_folder(m).fold_stmt(expr)
942 .expect_one("marking a stmt didn't return a stmt")
945 // apply a given mark to the given item. Used following the expansion of a macro.
946 fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
947 new_mark_folder(m).fold_item(expr)
950 fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
951 let mut relevant_info = cx.backtrace();
952 let mut einfo = relevant_info.unwrap();
954 match relevant_info {
958 relevant_info = einfo.call_site.expn_info;
969 use ast::{Attribute_, AttrOuter, MetaWord};
971 use codemap::Spanned;
972 use ext::base::{CrateLoader, MacroCrate};
976 use util::parser_testing::{string_to_parser};
977 use util::parser_testing::{string_to_pat, strs_to_idents};
981 // a visitor that extracts the paths
982 // from a given thingy and puts them in a mutable
983 // array (passed in to the traversal)
985 struct NewPathExprFinderContext {
986 path_accumulator: Vec<ast::Path> ,
989 impl Visitor<()> for NewPathExprFinderContext {
991 fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
993 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
994 self.path_accumulator.push(p.clone());
995 // not calling visit_path, should be fine.
997 _ => visit::walk_expr(self,expr,())
1001 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
1002 visit::walk_ty(self, typ, ())
1007 // return a visitor that extracts the paths
1008 // from a given pattern and puts them in a mutable
1009 // array (passed in to the traversal)
1010 pub fn new_path_finder(paths: Vec<ast::Path> ) -> NewPathExprFinderContext {
1011 NewPathExprFinderContext {
1012 path_accumulator: paths
1018 impl CrateLoader for ErrLoader {
1019 fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
1023 fn get_exported_macros(&mut self, _: ast::CrateNum) -> Vec<~str> {
1027 fn get_registrar_symbol(&mut self, _: ast::CrateNum) -> Option<~str> {
1032 // these following tests are quite fragile, in that they don't test what
1033 // *kind* of failure occurs.
1035 // make sure that macros can leave scope
1037 #[test] fn macros_cant_escape_fns_test () {
1038 let src = ~"fn bogus() {macro_rules! z (() => (3+4))}\
1039 fn inty() -> int { z!() }";
1040 let sess = parse::new_parse_sess();
1041 let crate_ast = parse::parse_crate_from_source_str(
1046 let mut loader = ErrLoader;
1047 let cfg = ::syntax::ext::expand::ExpansionConfig {
1048 loader: &mut loader,
1049 deriving_hash_type_parameter: false,
1050 crate_id: from_str("test").unwrap(),
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,
1070 crate_id: from_str("test").unwrap(),
1072 expand_crate(&sess,cfg,crate_ast);
1075 // macro_escape modules shouldn't cause macros to leave scope
1076 #[test] fn macros_can_escape_flattened_mods_test () {
1077 let src = ~"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1078 fn inty() -> int { z!() }";
1079 let sess = parse::new_parse_sess();
1080 let crate_ast = parse::parse_crate_from_source_str(
1085 let mut loader = ErrLoader;
1086 let cfg = ::syntax::ext::expand::ExpansionConfig {
1087 loader: &mut loader,
1088 deriving_hash_type_parameter: false,
1089 crate_id: from_str("test").unwrap(),
1091 expand_crate(&sess, cfg, crate_ast);
1094 #[test] fn test_contains_flatten (){
1095 let attr1 = make_dummy_attr ("foo");
1096 let attr2 = make_dummy_attr ("bar");
1097 let escape_attr = make_dummy_attr ("macro_escape");
1098 let attrs1 = vec!(attr1, escape_attr, attr2);
1099 assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1100 let attrs2 = vec!(attr1,attr2);
1101 assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1104 // make a MetaWord outer attribute with the given name
1105 fn make_dummy_attr(s: &str) -> ast::Attribute {
1107 span:codemap::DUMMY_SP,
1111 node: MetaWord(token::intern_and_get_ident(s)),
1112 span: codemap::DUMMY_SP,
1114 is_sugared_doc: false,
1119 //fn fake_print_crate(krate: &ast::Crate) {
1120 // let mut out = ~std::io::stderr() as ~std::io::Writer;
1121 // let mut s = pprust::rust_printer(out, get_ident_interner());
1122 // pprust::print_crate_(&mut s, krate);
1125 fn expand_crate_str(crate_str: ~str) -> ast::Crate {
1126 let ps = parse::new_parse_sess();
1127 let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
1128 // the cfg argument actually does matter, here...
1129 let mut loader = ErrLoader;
1130 let cfg = ::syntax::ext::expand::ExpansionConfig {
1131 loader: &mut loader,
1132 deriving_hash_type_parameter: false,
1133 crate_id: from_str("test").unwrap(),
1135 expand_crate(&ps,cfg,crate_ast)
1138 //fn expand_and_resolve(crate_str: @str) -> ast::crate {
1139 //let expanded_ast = expand_crate_str(crate_str);
1140 // println!("expanded: {:?}\n",expanded_ast);
1141 //mtwt_resolve_crate(expanded_ast)
1143 //fn expand_and_resolve_and_pretty_print (crate_str: @str) -> ~str {
1144 //let resolved_ast = expand_and_resolve(crate_str);
1145 //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
1148 #[test] fn macro_tokens_should_match(){
1149 expand_crate_str(~"macro_rules! m((a)=>(13)) fn main(){m!(a);}");
1152 // renaming tests expand a crate and then check that the bindings match
1153 // the right varrefs. The specification of the test case includes the
1154 // text of the crate, and also an array of arrays. Each element in the
1155 // outer array corresponds to a binding in the traversal of the AST
1156 // induced by visit. Each of these arrays contains a list of indexes,
1157 // interpreted as the varrefs in the varref traversal that this binding
1158 // should match. So, for instance, in a program with two bindings and
1159 // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1160 // binding should match the second two varrefs, and the second binding
1161 // should match the first varref.
1163 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1164 // names; differences in marks don't matter any more.
1166 // oog... I also want tests that check "binding-identifier-=?". That is,
1167 // not just "do these have the same name", but "do they have the same
1168 // name *and* the same marks"? Understanding this is really pretty painful.
1169 // in principle, you might want to control this boolean on a per-varref basis,
1170 // but that would make things even harder to understand, and might not be
1171 // necessary for thorough testing.
1172 type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1175 fn automatic_renaming () {
1176 let tests: Vec<RenamingTest> =
1177 vec!(// b & c should get new names throughout, in the expr too:
1178 ("fn a() -> int { let b = 13; let c = b; b+c }",
1179 vec!(vec!(0,1),vec!(2)), false),
1180 // both x's should be renamed (how is this causing a bug?)
1181 ("fn main () {let x: int = 13;x;}",
1182 vec!(vec!(0)), false),
1183 // the use of b after the + should be renamed, the other one not:
1184 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1185 vec!(vec!(1)), false),
1186 // the b before the plus should not be renamed (requires marks)
1187 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1188 vec!(vec!(1)), false),
1189 // the marks going in and out of letty should cancel, allowing that $x to
1190 // capture the one following the semicolon.
1191 // this was an awesome test case, and caught a *lot* of bugs.
1192 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1193 macro_rules! user(($x:ident) => ({letty!($x); $x}))
1194 fn main() -> int {user!(z)}",
1195 vec!(vec!(0)), false));
1196 for (idx,s) in tests.iter().enumerate() {
1197 run_renaming_test(s,idx);
1201 // run one of the renaming tests
1202 fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1203 let invalid_name = token::special_idents::invalid.name;
1204 let (teststr, bound_connections, bound_ident_check) = match *t {
1205 (ref str,ref conns, bic) => (str.to_owned(), conns.clone(), bic)
1207 let cr = expand_crate_str(teststr.to_owned());
1208 // find the bindings:
1209 let mut name_finder = new_name_finder(Vec::new());
1210 visit::walk_crate(&mut name_finder,&cr,());
1211 let bindings = name_finder.ident_accumulator;
1213 // find the varrefs:
1214 let mut path_finder = new_path_finder(Vec::new());
1215 visit::walk_crate(&mut path_finder,&cr,());
1216 let varrefs = path_finder.path_accumulator;
1218 // must be one check clause for each binding:
1219 assert_eq!(bindings.len(),bound_connections.len());
1220 for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1221 let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1222 let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1223 // shouldmatch can't name varrefs that don't exist:
1224 assert!((shouldmatch.len() == 0) ||
1225 (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1226 for (idx,varref) in varrefs.iter().enumerate() {
1227 if shouldmatch.contains(&idx) {
1228 // it should be a path of length 1, and it should
1229 // be free-identifier=? or bound-identifier=? to the given binding
1230 assert_eq!(varref.segments.len(),1);
1231 let varref_name = mtwt::resolve(varref.segments
1234 let varref_marks = mtwt::marksof(varref.segments
1239 if !(varref_name==binding_name) {
1240 println!("uh oh, should match but doesn't:");
1241 println!("varref: {:?}",varref);
1242 println!("binding: {:?}", *bindings.get(binding_idx));
1243 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1245 assert_eq!(varref_name,binding_name);
1246 if bound_ident_check {
1247 // we're checking bound-identifier=?, and the marks
1248 // should be the same, too:
1249 assert_eq!(varref_marks,binding_marks.clone());
1252 let fail = (varref.segments.len() == 1)
1253 && (mtwt::resolve(varref.segments.get(0).identifier)
1257 println!("failure on test {}",test_idx);
1258 println!("text of test case: \"{}\"", teststr);
1260 println!("uh oh, matches but shouldn't:");
1261 println!("varref: {:?}",varref);
1262 // good lord, you can't make a path with 0 segments, can you?
1263 let string = token::get_ident(varref.segments
1266 println!("varref's first segment's uint: {}, and string: \"{}\"",
1267 varref.segments.get(0).identifier.name,
1269 println!("binding: {:?}", *bindings.get(binding_idx));
1270 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1278 #[test] fn fmt_in_macro_used_inside_module_macro() {
1279 let crate_str = ~"macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
1280 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1283 let cr = expand_crate_str(crate_str);
1284 // find the xx binding
1285 let mut name_finder = new_name_finder(Vec::new());
1286 visit::walk_crate(&mut name_finder, &cr, ());
1287 let bindings = name_finder.ident_accumulator;
1289 let cxbinds: Vec<&ast::Ident> =
1290 bindings.iter().filter(|b| {
1291 let ident = token::get_ident(**b);
1292 let string = ident.get();
1295 let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1296 let cxbind = match cxbinds {
1298 _ => fail!("expected just one binding for ext_cx")
1300 let resolved_binding = mtwt::resolve(*cxbind);
1301 // find all the xx varrefs:
1302 let mut path_finder = new_path_finder(Vec::new());
1303 visit::walk_crate(&mut path_finder, &cr, ());
1304 let varrefs = path_finder.path_accumulator;
1306 // the xx binding should bind all of the xx varrefs:
1307 for (idx,v) in varrefs.iter().filter(|p| {
1308 p.segments.len() == 1
1309 && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1311 if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1312 println!("uh oh, xx binding didn't match xx varref:");
1313 println!("this is xx varref \\# {:?}",idx);
1314 println!("binding: {:?}",cxbind);
1315 println!("resolves to: {:?}",resolved_binding);
1316 println!("varref: {:?}",v.segments.get(0).identifier);
1317 println!("resolves to: {:?}",
1318 mtwt::resolve(v.segments.get(0).identifier));
1319 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1321 assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1328 let pat = string_to_pat(~"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
1329 let mut pat_idents = new_name_finder(Vec::new());
1330 pat_idents.visit_pat(pat, ());
1331 assert_eq!(pat_idents.ident_accumulator,
1332 strs_to_idents(vec!("a","c","b","d")));