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 DummyResult::raw_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 DummyResult::raw_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()).make_expr() {
97 "non-expr macro in expr pos: {}",
101 return DummyResult::raw_expr(e.span);
106 mark_expr(expanded,fm)
111 format!("'{}' is not a tt-style macro",
114 return DummyResult::raw_expr(e.span);
118 // Keep going, outside-in.
120 // FIXME(pcwalton): Is it necessary to clone the
123 fld.fold_expr(marked_after).node.clone();
127 id: ast::DUMMY_NODE_ID,
128 node: fully_expanded,
135 // Desugar expr_for_loop
136 // From: `['<ident>:] for <src_pat> in <src_expr> <src_loop_block>`
137 // FIXME #6993: change type of opt_ident to Option<Name>
138 ast::ExprForLoop(src_pat, src_expr, src_loop_block, opt_ident) => {
139 // Expand any interior macros etc.
140 // NB: we don't fold pats yet. Curious.
141 let src_expr = fld.fold_expr(src_expr).clone();
142 let (src_loop_block, opt_ident) = expand_loop_block(src_loop_block, opt_ident, fld);
148 // match &mut <src_expr> {
150 // ['<ident>:] loop {
153 // Some(<src_pat>) => <src_loop_block>
159 let local_ident = token::gensym_ident("i");
160 let next_ident = fld.cx.ident_of("next");
161 let none_ident = fld.cx.ident_of("None");
163 let local_path = fld.cx.path_ident(span, local_ident);
164 let some_path = fld.cx.path_ident(span, fld.cx.ident_of("Some"));
166 // `None => break ['<ident>];`
168 let break_expr = fld.cx.expr(span, ast::ExprBreak(opt_ident));
169 let none_pat = fld.cx.pat_ident(span, none_ident);
170 fld.cx.arm(span, vec!(none_pat), break_expr)
173 // `Some(<src_pat>) => <src_loop_block>`
176 vec!(fld.cx.pat_enum(span, some_path, vec!(src_pat))),
177 fld.cx.expr_block(src_loop_block));
179 // `match i.next() { ... }`
182 fld.cx.expr_method_call(span,
183 fld.cx.expr_path(local_path),
187 fld.cx.expr_match(span, next_call_expr, vec!(none_arm, some_arm))
190 // ['ident:] loop { ... }
191 let loop_expr = fld.cx.expr(span,
192 ast::ExprLoop(fld.cx.block_expr(match_expr),
195 // `i => loop { ... }`
197 // `match &mut <src_expr> { i => loop { ... } }`
198 let discrim = fld.cx.expr_mut_addr_of(span, src_expr);
199 let i_pattern = fld.cx.pat_ident(span, local_ident);
200 let arm = fld.cx.arm(span, vec!(i_pattern), loop_expr);
201 fld.cx.expr_match(span, discrim, vec!(arm))
204 ast::ExprLoop(loop_block, opt_ident) => {
205 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
206 fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
209 _ => noop_fold_expr(e, fld)
213 // Rename loop label and expand its loop body
215 // The renaming procedure for loop is different in the sense that the loop
216 // body is in a block enclosed by loop head so the renaming of loop label
217 // must be propagated to the enclosed context.
218 fn expand_loop_block(loop_block: P<Block>,
219 opt_ident: Option<Ident>,
220 fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
223 let new_label = fresh_name(&label);
224 let rename = (label, new_label);
226 // The rename *must not* be added to the pending list of current
227 // syntax context otherwise an unrelated `break` or `continue` in
228 // the same context will pick that up in the deferred renaming pass
229 // and be renamed incorrectly.
230 let mut rename_list = vec!(rename);
231 let mut rename_fld = renames_to_fold(&mut rename_list);
232 let renamed_ident = rename_fld.fold_ident(label);
234 // The rename *must* be added to the enclosed syntax context for
235 // `break` or `continue` to pick up because by definition they are
236 // in a block enclosed by loop head.
237 fld.extsbox.push_frame();
238 fld.extsbox.info().pending_renames.push(rename);
239 let expanded_block = expand_block_elts(loop_block, fld);
240 fld.extsbox.pop_frame();
242 (expanded_block, Some(renamed_ident))
244 None => (fld.fold_block(loop_block), opt_ident)
248 // eval $e with a new exts frame:
249 macro_rules! with_exts_frame (
250 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
251 ({$extsboxexpr.push_frame();
252 $extsboxexpr.info().macros_escape = $macros_escape;
254 $extsboxexpr.pop_frame();
259 // When we enter a module, record it, for the sake of `module!`
260 pub fn expand_item(it: @ast::Item, fld: &mut MacroExpander)
261 -> SmallVector<@ast::Item> {
262 let it = expand_item_modifiers(it, fld);
264 let mut decorator_items = SmallVector::zero();
265 for attr in it.attrs.iter().rev() {
266 let mname = attr.name();
268 match fld.extsbox.find(&intern(mname.get())) {
269 Some(&ItemDecorator(dec_fn)) => {
270 fld.cx.bt_push(ExpnInfo {
271 call_site: attr.span,
272 callee: NameAndSpan {
273 name: mname.get().to_str(),
274 format: MacroAttribute,
279 // we'd ideally decorator_items.push_all(expand_item(item, fld)),
280 // but that double-mut-borrows fld
281 let mut items: SmallVector<@ast::Item> = SmallVector::zero();
282 dec_fn(fld.cx, attr.span, attr.node.value, it,
283 |item| items.push(item));
284 decorator_items.extend(items.move_iter()
285 .flat_map(|item| expand_item(item, fld).move_iter()));
293 let mut new_items = match it.node {
294 ast::ItemMac(..) => expand_item_mac(it, fld),
295 ast::ItemMod(_) | ast::ItemForeignMod(_) => {
296 fld.cx.mod_push(it.ident);
297 let macro_escape = contains_macro_escape(it.attrs.as_slice());
298 let result = with_exts_frame!(fld.extsbox,
300 noop_fold_item(it, fld));
304 _ => noop_fold_item(it, fld)
307 new_items.push_all(decorator_items);
311 fn expand_item_modifiers(mut it: @ast::Item, fld: &mut MacroExpander)
313 let (modifiers, attrs) = it.attrs.partitioned(|attr| {
314 match fld.extsbox.find(&intern(attr.name().get())) {
315 Some(&ItemModifier(_)) => true,
325 if modifiers.is_empty() {
329 for attr in modifiers.iter() {
330 let mname = attr.name();
332 match fld.extsbox.find(&intern(mname.get())) {
333 Some(&ItemModifier(dec_fn)) => {
334 fld.cx.bt_push(ExpnInfo {
335 call_site: attr.span,
336 callee: NameAndSpan {
337 name: mname.get().to_str(),
338 format: MacroAttribute,
342 it = dec_fn(fld.cx, attr.span, attr.node.value, it);
349 // expansion may have added new ItemModifiers
350 expand_item_modifiers(it, fld)
353 // does this attribute list contain "macro_escape" ?
354 pub fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
355 attr::contains_name(attrs, "macro_escape")
358 // Support for item-position macro invocations, exactly the same
359 // logic as for expression-position macro invocations.
360 pub fn expand_item_mac(it: @ast::Item, fld: &mut MacroExpander)
361 -> SmallVector<@ast::Item> {
362 let (pth, tts) = match it.node {
363 ItemMac(codemap::Spanned {
364 node: MacInvocTT(ref pth, ref tts, _),
367 (pth, (*tts).clone())
369 _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
372 let extname = pth.segments.get(0).identifier;
373 let extnamestr = token::get_ident(extname);
374 let fm = fresh_mark();
375 let expanded = match fld.extsbox.find(&extname.name) {
377 fld.cx.span_err(pth.span,
378 format!("macro undefined: '{}!'",
380 // let compilation continue
381 return SmallVector::zero();
384 Some(&NormalTT(ref expander, span)) => {
385 if it.ident.name != parse::token::special_idents::invalid.name {
386 fld.cx.span_err(pth.span,
387 format!("macro {}! expects no ident argument, \
390 token::get_ident(it.ident)));
391 return SmallVector::zero();
393 fld.cx.bt_push(ExpnInfo {
395 callee: NameAndSpan {
396 name: extnamestr.get().to_str(),
401 // mark before expansion:
402 let marked_before = mark_tts(tts.as_slice(), fm);
403 expander.expand(fld.cx, it.span, marked_before.as_slice())
405 Some(&IdentTT(ref expander, span)) => {
406 if it.ident.name == parse::token::special_idents::invalid.name {
407 fld.cx.span_err(pth.span,
408 format!("macro {}! expects an ident argument",
410 return SmallVector::zero();
412 fld.cx.bt_push(ExpnInfo {
414 callee: NameAndSpan {
415 name: extnamestr.get().to_str(),
420 // mark before expansion:
421 let marked_tts = mark_tts(tts.as_slice(), fm);
422 expander.expand(fld.cx, it.span, it.ident, marked_tts)
425 fld.cx.span_err(it.span,
426 format!("{}! is not legal in item position",
428 return SmallVector::zero();
432 let items = match expanded.make_def() {
433 Some(MacroDef { name, ext }) => {
434 // yikes... no idea how to apply the mark to this. I'm afraid
435 // we're going to have to wait-and-see on this one.
436 fld.extsbox.insert(intern(name), ext);
437 if attr::contains_name(it.attrs.as_slice(), "macro_export") {
444 match expanded.make_items() {
447 .flat_map(|i| mark_item(i, fm).move_iter())
448 .flat_map(|i| fld.fold_item(i).move_iter())
452 fld.cx.span_err(pth.span,
453 format!("expr macro in item position: {}",
455 return SmallVector::zero();
464 // load macros from syntax-phase crates
465 pub fn expand_view_item(vi: &ast::ViewItem,
466 fld: &mut MacroExpander)
469 ast::ViewItemExternCrate(..) => {
470 let should_load = vi.attrs.iter().any(|attr| {
471 attr.name().get() == "phase" &&
472 attr.meta_item_list().map_or(false, |phases| {
473 attr::contains_name(phases, "syntax")
478 load_extern_macros(vi, fld);
481 ast::ViewItemUse(_) => {}
484 noop_fold_view_item(vi, fld)
487 fn load_extern_macros(krate: &ast::ViewItem, fld: &mut MacroExpander) {
488 let MacroCrate { lib, macros, registrar_symbol } =
489 fld.cx.ecfg.loader.load_crate(krate);
491 let crate_name = match krate.node {
492 ast::ViewItemExternCrate(name, _, _) => name,
495 let name = format!("<{} macros>", token::get_ident(crate_name));
497 for source in macros.iter() {
498 let item = parse::parse_item_from_source_str(name.clone(),
502 .expect("expected a serialized item");
503 expand_item_mac(item, fld);
506 let path = match lib {
510 // Make sure the path contains a / or the linker will search for it.
511 let path = os::make_absolute(&path);
513 let registrar = match registrar_symbol {
514 Some(registrar) => registrar,
518 let lib = match DynamicLibrary::open(Some(&path)) {
520 // this is fatal: there are almost certainly macros we need
521 // inside this crate, so continue would spew "macro undefined"
523 Err(err) => fld.cx.span_fatal(krate.span, err)
527 let registrar: MacroCrateRegistrationFun = match lib.symbol(registrar) {
528 Ok(registrar) => registrar,
529 // again fatal if we can't register macros
530 Err(err) => fld.cx.span_fatal(krate.span, err)
532 registrar(|name, extension| {
533 let extension = match extension {
534 NormalTT(ext, _) => NormalTT(ext, Some(krate.span)),
535 IdentTT(ext, _) => IdentTT(ext, Some(krate.span)),
536 ItemDecorator(ext) => ItemDecorator(ext),
537 ItemModifier(ext) => ItemModifier(ext),
539 fld.extsbox.insert(name, extension);
542 // Intentionally leak the dynamic library. We can't ever unload it
543 // since the library can do things that will outlive the expansion
544 // phase (e.g. make an @-box cycle or launch a task).
550 pub fn expand_stmt(s: &Stmt, fld: &mut MacroExpander) -> SmallVector<@Stmt> {
551 // why the copying here and not in expand_expr?
552 // looks like classic changed-in-only-one-place
553 let (pth, tts, semi) = match s.node {
554 StmtMac(ref mac, semi) => {
556 MacInvocTT(ref pth, ref tts, _) => {
557 (pth, (*tts).clone(), semi)
561 _ => return expand_non_macro_stmt(s, fld)
563 if pth.segments.len() > 1u {
564 fld.cx.span_err(pth.span, "expected macro name without module separators");
565 return SmallVector::zero();
567 let extname = pth.segments.get(0).identifier;
568 let extnamestr = token::get_ident(extname);
569 let marked_after = match fld.extsbox.find(&extname.name) {
571 fld.cx.span_err(pth.span, format!("macro undefined: '{}'", extnamestr));
572 return SmallVector::zero();
575 Some(&NormalTT(ref expandfun, exp_span)) => {
576 fld.cx.bt_push(ExpnInfo {
578 callee: NameAndSpan {
579 name: extnamestr.get().to_str(),
584 let fm = fresh_mark();
585 // mark before expansion:
586 let marked_tts = mark_tts(tts.as_slice(), fm);
588 // See the comment in expand_expr for why we want the original span,
589 // not the current mac.span.
590 let mac_span = original_span(fld.cx);
592 let expanded = match expandfun.expand(fld.cx,
594 marked_tts.as_slice()).make_stmt() {
597 fld.cx.span_err(pth.span,
598 format!("non-stmt macro in stmt pos: {}",
600 return SmallVector::zero();
604 mark_stmt(expanded,fm)
608 fld.cx.span_err(pth.span, format!("'{}' is not a tt-style macro",
610 return SmallVector::zero();
614 // Keep going, outside-in.
615 let fully_expanded = fld.fold_stmt(marked_after);
616 if fully_expanded.is_empty() {
617 fld.cx.span_err(pth.span, "macro didn't expand to a statement");
618 return SmallVector::zero();
621 let fully_expanded: SmallVector<@Stmt> = fully_expanded.move_iter()
622 .map(|s| @Spanned { span: s.span, node: s.node.clone() })
625 fully_expanded.move_iter().map(|s| {
627 StmtExpr(e, stmt_id) if semi => {
630 node: StmtSemi(e, stmt_id)
633 _ => s /* might already have a semi */
638 // expand a non-macro stmt. this is essentially the fallthrough for
639 // expand_stmt, above.
640 fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
641 -> SmallVector<@Stmt> {
644 StmtDecl(decl, node_id) => {
647 node: DeclLocal(ref local),
658 // expand the pat (it might contain exprs... #:(o)>
659 let expanded_pat = fld.fold_pat(pat);
660 // find the pat_idents in the pattern:
661 // oh dear heaven... this is going to include the enum
662 // names, as well... but that should be okay, as long as
663 // the new names are gensyms for the old ones.
664 let mut name_finder = new_name_finder(Vec::new());
665 name_finder.visit_pat(expanded_pat,());
666 // generate fresh names, push them to a new pending list
667 let mut new_pending_renames = Vec::new();
668 for ident in name_finder.ident_accumulator.iter() {
669 let new_name = fresh_name(ident);
670 new_pending_renames.push((*ident,new_name));
672 let rewritten_pat = {
674 renames_to_fold(&mut new_pending_renames);
675 // rewrite the pattern using the new names (the old
676 // ones have already been applied):
677 rename_fld.fold_pat(expanded_pat)
679 // add them to the existing pending renames:
680 fld.extsbox.info().pending_renames.push_all_move(new_pending_renames);
681 // also, don't forget to expand the init:
682 let new_init_opt = init.map(|e| fld.fold_expr(e));
683 let rewritten_local =
691 SmallVector::one(@Spanned {
692 node: StmtDecl(@Spanned {
693 node: DeclLocal(rewritten_local),
700 _ => noop_fold_stmt(s, fld),
703 _ => noop_fold_stmt(s, fld),
707 // a visitor that extracts the pat_ident paths
708 // from a given thingy and puts them in a mutable
709 // array (passed in to the traversal)
711 pub struct NewNameFinderContext {
712 ident_accumulator: Vec<ast::Ident> ,
715 impl Visitor<()> for NewNameFinderContext {
716 fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
718 // we found a pat_ident!
721 node: ast::PatIdent(_, ref path, ref inner),
725 // a path of length one:
729 segments: ref segments
730 } if segments.len() == 1 => {
731 self.ident_accumulator.push(segments.get(0)
734 // I believe these must be enums...
737 // visit optional subpattern of pat_ident:
738 for subpat in inner.iter() {
739 self.visit_pat(*subpat, ())
742 // use the default traversal for non-pat_idents
743 _ => visit::walk_pat(self, pattern, ())
747 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
748 visit::walk_ty(self, typ, ())
753 // return a visitor that extracts the pat_ident paths
754 // from a given thingy and puts them in a mutable
755 // array (passed in to the traversal)
756 pub fn new_name_finder(idents: Vec<ast::Ident> ) -> NewNameFinderContext {
757 NewNameFinderContext {
758 ident_accumulator: idents,
762 // expand a block. pushes a new exts_frame, then calls expand_block_elts
763 pub fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
764 // see note below about treatment of exts table
765 with_exts_frame!(fld.extsbox,false,
766 expand_block_elts(blk, fld))
769 // expand the elements of a block.
770 pub fn expand_block_elts(b: &Block, fld: &mut MacroExpander) -> P<Block> {
771 let new_view_items = b.view_items.iter().map(|x| fld.fold_view_item(x)).collect();
773 b.stmts.iter().flat_map(|x| {
775 let pending_renames = &mut fld.extsbox.info().pending_renames;
776 let mut rename_fld = renames_to_fold(pending_renames);
777 rename_fld.fold_stmt(*x).expect_one("rename_fold didn't return one value")
779 fld.fold_stmt(renamed_stmt).move_iter()
781 let new_expr = b.expr.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_expr(x)
790 view_items: new_view_items,
793 id: fld.new_id(b.id),
799 pub struct IdentRenamer<'a> {
800 renames: &'a mut RenameList,
803 impl<'a> Folder for IdentRenamer<'a> {
804 fn fold_ident(&mut self, id: Ident) -> Ident {
805 let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
806 mtwt::new_rename(from, to, ctxt)
815 // given a mutable list of renames, return a tree-folder that applies those
817 pub fn renames_to_fold<'a>(renames: &'a mut RenameList) -> IdentRenamer<'a> {
823 pub fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
824 /* this discards information in the case of macro-defining macros */
828 expn_info: cx.backtrace(),
832 pub struct MacroExpander<'a, 'b> {
833 pub extsbox: SyntaxEnv,
834 pub cx: &'a mut ExtCtxt<'b>,
837 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
838 fn fold_expr(&mut self, expr: @ast::Expr) -> @ast::Expr {
839 expand_expr(expr, self)
842 fn fold_item(&mut self, item: @ast::Item) -> SmallVector<@ast::Item> {
843 expand_item(item, self)
846 fn fold_view_item(&mut self, vi: &ast::ViewItem) -> ast::ViewItem {
847 expand_view_item(vi, self)
850 fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<@ast::Stmt> {
851 expand_stmt(stmt, self)
854 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
855 expand_block(block, self)
858 fn new_span(&mut self, span: Span) -> Span {
859 new_span(self.cx, span)
863 pub struct ExpansionConfig<'a> {
864 pub loader: &'a mut CrateLoader,
865 pub deriving_hash_type_parameter: bool,
866 pub crate_id: CrateId,
869 pub fn expand_crate(parse_sess: &parse::ParseSess,
870 cfg: ExpansionConfig,
872 let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
873 let mut expander = MacroExpander {
874 extsbox: syntax_expander_table(),
878 let ret = expander.fold_crate(c);
879 parse_sess.span_diagnostic.handler().abort_if_errors();
883 // HYGIENIC CONTEXT EXTENSION:
884 // all of these functions are for walking over
885 // ASTs and making some change to the context of every
886 // element that has one. a CtxtFn is a trait-ified
887 // version of a closure in (SyntaxContext -> SyntaxContext).
888 // the ones defined here include:
889 // Marker - add a mark to a context
891 // A Marker adds the given mark to the syntax context
892 struct Marker { mark: Mrk }
894 impl Folder for Marker {
895 fn fold_ident(&mut self, id: Ident) -> Ident {
898 ctxt: mtwt::new_mark(self.mark, id.ctxt)
901 fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
902 let macro = match m.node {
903 MacInvocTT(ref path, ref tts, ctxt) => {
904 MacInvocTT(self.fold_path(path),
905 fold_tts(tts.as_slice(), self),
906 mtwt::new_mark(self.mark, ctxt))
916 // just a convenience:
917 fn new_mark_folder(m: Mrk) -> Marker {
921 // apply a given mark to the given token trees. Used prior to expansion of a macro.
922 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
923 fold_tts(tts, &mut new_mark_folder(m))
926 // apply a given mark to the given expr. Used following the expansion of a macro.
927 fn mark_expr(expr: @ast::Expr, m: Mrk) -> @ast::Expr {
928 new_mark_folder(m).fold_expr(expr)
931 // apply a given mark to the given stmt. Used following the expansion of a macro.
932 fn mark_stmt(expr: &ast::Stmt, m: Mrk) -> @ast::Stmt {
933 new_mark_folder(m).fold_stmt(expr)
934 .expect_one("marking a stmt didn't return a stmt")
937 // apply a given mark to the given item. Used following the expansion of a macro.
938 fn mark_item(expr: @ast::Item, m: Mrk) -> SmallVector<@ast::Item> {
939 new_mark_folder(m).fold_item(expr)
942 fn original_span(cx: &ExtCtxt) -> @codemap::ExpnInfo {
943 let mut relevant_info = cx.backtrace();
944 let mut einfo = relevant_info.unwrap();
946 match relevant_info {
950 relevant_info = einfo.call_site.expn_info;
961 use ast::{Attribute_, AttrOuter, MetaWord};
963 use codemap::Spanned;
964 use ext::base::{CrateLoader, MacroCrate};
968 use util::parser_testing::{string_to_parser};
969 use util::parser_testing::{string_to_pat, strs_to_idents};
973 // a visitor that extracts the paths
974 // from a given thingy and puts them in a mutable
975 // array (passed in to the traversal)
977 struct NewPathExprFinderContext {
978 path_accumulator: Vec<ast::Path> ,
981 impl Visitor<()> for NewPathExprFinderContext {
983 fn visit_expr(&mut self, expr: &ast::Expr, _: ()) {
985 ast::Expr{id:_,span:_,node:ast::ExprPath(ref p)} => {
986 self.path_accumulator.push(p.clone());
987 // not calling visit_path, should be fine.
989 _ => visit::walk_expr(self,expr,())
993 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
994 visit::walk_ty(self, typ, ())
999 // return a visitor that extracts the paths
1000 // from a given pattern and puts them in a mutable
1001 // array (passed in to the traversal)
1002 pub fn new_path_finder(paths: Vec<ast::Path> ) -> NewPathExprFinderContext {
1003 NewPathExprFinderContext {
1004 path_accumulator: paths
1010 impl CrateLoader for ErrLoader {
1011 fn load_crate(&mut self, _: &ast::ViewItem) -> MacroCrate {
1016 // these following tests are quite fragile, in that they don't test what
1017 // *kind* of failure occurs.
1019 // make sure that macros can leave scope
1021 #[test] fn macros_cant_escape_fns_test () {
1022 let src = "fn bogus() {macro_rules! z (() => (3+4))}\
1023 fn inty() -> int { z!() }".to_owned();
1024 let sess = parse::new_parse_sess();
1025 let crate_ast = parse::parse_crate_from_source_str(
1026 "<test>".to_owned(),
1030 let mut loader = ErrLoader;
1031 let cfg = ::syntax::ext::expand::ExpansionConfig {
1032 loader: &mut loader,
1033 deriving_hash_type_parameter: false,
1034 crate_id: from_str("test").unwrap(),
1036 expand_crate(&sess,cfg,crate_ast);
1039 // make sure that macros can leave scope for modules
1041 #[test] fn macros_cant_escape_mods_test () {
1042 let src = "mod foo {macro_rules! z (() => (3+4))}\
1043 fn inty() -> int { z!() }".to_owned();
1044 let sess = parse::new_parse_sess();
1045 let crate_ast = parse::parse_crate_from_source_str(
1046 "<test>".to_owned(),
1050 let mut loader = ErrLoader;
1051 let cfg = ::syntax::ext::expand::ExpansionConfig {
1052 loader: &mut loader,
1053 deriving_hash_type_parameter: false,
1054 crate_id: from_str("test").unwrap(),
1056 expand_crate(&sess,cfg,crate_ast);
1059 // macro_escape modules shouldn't cause macros to leave scope
1060 #[test] fn macros_can_escape_flattened_mods_test () {
1061 let src = "#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1062 fn inty() -> int { z!() }".to_owned();
1063 let sess = parse::new_parse_sess();
1064 let crate_ast = parse::parse_crate_from_source_str(
1065 "<test>".to_owned(),
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 #[test] fn test_contains_flatten (){
1079 let attr1 = make_dummy_attr ("foo");
1080 let attr2 = make_dummy_attr ("bar");
1081 let escape_attr = make_dummy_attr ("macro_escape");
1082 let attrs1 = vec!(attr1, escape_attr, attr2);
1083 assert_eq!(contains_macro_escape(attrs1.as_slice()),true);
1084 let attrs2 = vec!(attr1,attr2);
1085 assert_eq!(contains_macro_escape(attrs2.as_slice()),false);
1088 // make a MetaWord outer attribute with the given name
1089 fn make_dummy_attr(s: &str) -> ast::Attribute {
1091 span:codemap::DUMMY_SP,
1095 node: MetaWord(token::intern_and_get_ident(s)),
1096 span: codemap::DUMMY_SP,
1098 is_sugared_doc: false,
1103 //fn fake_print_crate(krate: &ast::Crate) {
1104 // let mut out = ~std::io::stderr() as ~std::io::Writer;
1105 // let mut s = pprust::rust_printer(out, get_ident_interner());
1106 // pprust::print_crate_(&mut s, krate);
1109 fn expand_crate_str(crate_str: ~str) -> ast::Crate {
1110 let ps = parse::new_parse_sess();
1111 let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
1112 // the cfg argument actually does matter, here...
1113 let mut loader = ErrLoader;
1114 let cfg = ::syntax::ext::expand::ExpansionConfig {
1115 loader: &mut loader,
1116 deriving_hash_type_parameter: false,
1117 crate_id: from_str("test").unwrap(),
1119 expand_crate(&ps,cfg,crate_ast)
1122 //fn expand_and_resolve(crate_str: @str) -> ast::crate {
1123 //let expanded_ast = expand_crate_str(crate_str);
1124 // println!("expanded: {:?}\n",expanded_ast);
1125 //mtwt_resolve_crate(expanded_ast)
1127 //fn expand_and_resolve_and_pretty_print (crate_str: @str) -> ~str {
1128 //let resolved_ast = expand_and_resolve(crate_str);
1129 //pprust::to_str(&resolved_ast,fake_print_crate,get_ident_interner())
1132 #[test] fn macro_tokens_should_match(){
1133 expand_crate_str("macro_rules! m((a)=>(13)) fn main(){m!(a);}".to_owned());
1136 // renaming tests expand a crate and then check that the bindings match
1137 // the right varrefs. The specification of the test case includes the
1138 // text of the crate, and also an array of arrays. Each element in the
1139 // outer array corresponds to a binding in the traversal of the AST
1140 // induced by visit. Each of these arrays contains a list of indexes,
1141 // interpreted as the varrefs in the varref traversal that this binding
1142 // should match. So, for instance, in a program with two bindings and
1143 // three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
1144 // binding should match the second two varrefs, and the second binding
1145 // should match the first varref.
1147 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1148 // names; differences in marks don't matter any more.
1150 // oog... I also want tests that check "binding-identifier-=?". That is,
1151 // not just "do these have the same name", but "do they have the same
1152 // name *and* the same marks"? Understanding this is really pretty painful.
1153 // in principle, you might want to control this boolean on a per-varref basis,
1154 // but that would make things even harder to understand, and might not be
1155 // necessary for thorough testing.
1156 type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
1159 fn automatic_renaming () {
1160 let tests: Vec<RenamingTest> =
1161 vec!(// b & c should get new names throughout, in the expr too:
1162 ("fn a() -> int { let b = 13; let c = b; b+c }",
1163 vec!(vec!(0,1),vec!(2)), false),
1164 // both x's should be renamed (how is this causing a bug?)
1165 ("fn main () {let x: int = 13;x;}",
1166 vec!(vec!(0)), false),
1167 // the use of b after the + should be renamed, the other one not:
1168 ("macro_rules! f (($x:ident) => (b + $x)) fn a() -> int { let b = 13; f!(b)}",
1169 vec!(vec!(1)), false),
1170 // the b before the plus should not be renamed (requires marks)
1171 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})) fn a() -> int { f!(b)}",
1172 vec!(vec!(1)), false),
1173 // the marks going in and out of letty should cancel, allowing that $x to
1174 // capture the one following the semicolon.
1175 // this was an awesome test case, and caught a *lot* of bugs.
1176 ("macro_rules! letty(($x:ident) => (let $x = 15;))
1177 macro_rules! user(($x:ident) => ({letty!($x); $x}))
1178 fn main() -> int {user!(z)}",
1179 vec!(vec!(0)), false));
1180 for (idx,s) in tests.iter().enumerate() {
1181 run_renaming_test(s,idx);
1185 // run one of the renaming tests
1186 fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
1187 let invalid_name = token::special_idents::invalid.name;
1188 let (teststr, bound_connections, bound_ident_check) = match *t {
1189 (ref str,ref conns, bic) => (str.to_owned(), conns.clone(), bic)
1191 let cr = expand_crate_str(teststr.to_owned());
1192 // find the bindings:
1193 let mut name_finder = new_name_finder(Vec::new());
1194 visit::walk_crate(&mut name_finder,&cr,());
1195 let bindings = name_finder.ident_accumulator;
1197 // find the varrefs:
1198 let mut path_finder = new_path_finder(Vec::new());
1199 visit::walk_crate(&mut path_finder,&cr,());
1200 let varrefs = path_finder.path_accumulator;
1202 // must be one check clause for each binding:
1203 assert_eq!(bindings.len(),bound_connections.len());
1204 for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1205 let binding_name = mtwt::resolve(*bindings.get(binding_idx));
1206 let binding_marks = mtwt::marksof(bindings.get(binding_idx).ctxt, invalid_name);
1207 // shouldmatch can't name varrefs that don't exist:
1208 assert!((shouldmatch.len() == 0) ||
1209 (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1210 for (idx,varref) in varrefs.iter().enumerate() {
1211 if shouldmatch.contains(&idx) {
1212 // it should be a path of length 1, and it should
1213 // be free-identifier=? or bound-identifier=? to the given binding
1214 assert_eq!(varref.segments.len(),1);
1215 let varref_name = mtwt::resolve(varref.segments
1218 let varref_marks = mtwt::marksof(varref.segments
1223 if !(varref_name==binding_name) {
1224 println!("uh oh, should match but doesn't:");
1225 println!("varref: {:?}",varref);
1226 println!("binding: {:?}", *bindings.get(binding_idx));
1227 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1229 assert_eq!(varref_name,binding_name);
1230 if bound_ident_check {
1231 // we're checking bound-identifier=?, and the marks
1232 // should be the same, too:
1233 assert_eq!(varref_marks,binding_marks.clone());
1236 let fail = (varref.segments.len() == 1)
1237 && (mtwt::resolve(varref.segments.get(0).identifier)
1241 println!("failure on test {}",test_idx);
1242 println!("text of test case: \"{}\"", teststr);
1244 println!("uh oh, matches but shouldn't:");
1245 println!("varref: {:?}",varref);
1246 // good lord, you can't make a path with 0 segments, can you?
1247 let string = token::get_ident(varref.segments
1250 println!("varref's first segment's uint: {}, and string: \"{}\"",
1251 varref.segments.get(0).identifier.name,
1253 println!("binding: {:?}", *bindings.get(binding_idx));
1254 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1262 #[test] fn fmt_in_macro_used_inside_module_macro() {
1263 let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_str()))
1264 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
1267 let cr = expand_crate_str(crate_str);
1268 // find the xx binding
1269 let mut name_finder = new_name_finder(Vec::new());
1270 visit::walk_crate(&mut name_finder, &cr, ());
1271 let bindings = name_finder.ident_accumulator;
1273 let cxbinds: Vec<&ast::Ident> =
1274 bindings.iter().filter(|b| {
1275 let ident = token::get_ident(**b);
1276 let string = ident.get();
1279 let cxbinds: &[&ast::Ident] = cxbinds.as_slice();
1280 let cxbind = match cxbinds {
1282 _ => fail!("expected just one binding for ext_cx")
1284 let resolved_binding = mtwt::resolve(*cxbind);
1285 // find all the xx varrefs:
1286 let mut path_finder = new_path_finder(Vec::new());
1287 visit::walk_crate(&mut path_finder, &cr, ());
1288 let varrefs = path_finder.path_accumulator;
1290 // the xx binding should bind all of the xx varrefs:
1291 for (idx,v) in varrefs.iter().filter(|p| {
1292 p.segments.len() == 1
1293 && "xx" == token::get_ident(p.segments.get(0).identifier).get()
1295 if mtwt::resolve(v.segments.get(0).identifier) != resolved_binding {
1296 println!("uh oh, xx binding didn't match xx varref:");
1297 println!("this is xx varref \\# {:?}",idx);
1298 println!("binding: {:?}",cxbind);
1299 println!("resolves to: {:?}",resolved_binding);
1300 println!("varref: {:?}",v.segments.get(0).identifier);
1301 println!("resolves to: {:?}",
1302 mtwt::resolve(v.segments.get(0).identifier));
1303 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1305 assert_eq!(mtwt::resolve(v.segments.get(0).identifier),
1312 let pat = string_to_pat("(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_owned());
1313 let mut pat_idents = new_name_finder(Vec::new());
1314 pat_idents.visit_pat(pat, ());
1315 assert_eq!(pat_idents.ident_accumulator,
1316 strs_to_idents(vec!("a","c","b","d")));