1 // Copyright 2012 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::{Block, Crate, NodeId, expr_, expr_mac, ident, mac_invoc_tt};
12 use ast::{item_mac, stmt_, stmt_mac, stmt_expr, stmt_semi};
13 use ast::{illegal_ctxt};
15 use ast_util::{new_rename, new_mark, resolve};
17 use attr::AttrMetaMethods;
19 use codemap::{span, spanned, ExpnInfo, NameAndSpan};
23 use parse::{parse_item_from_source_str};
25 use parse::token::{ident_to_str, intern};
31 pub fn expand_expr(extsbox: @mut SyntaxEnv,
36 orig: @fn(&expr_, span, @ast_fold) -> (expr_, span))
39 // expr_mac should really be expr_ext or something; it's the
40 // entry-point for all syntax extensions.
41 expr_mac(ref mac) => {
44 mac_invoc_tt(ref pth, ref tts) => {
45 if (pth.idents.len() > 1u) {
48 fmt!("expected macro name without module \
51 let extname = &pth.idents[0];
52 let extnamestr = ident_to_str(extname);
53 // leaving explicit deref here to highlight unbox op:
54 match (*extsbox).find(&extname.name) {
58 fmt!("macro undefined: '%s'", extnamestr))
60 Some(@SE(NormalTT(SyntaxExpanderTT{
72 let expanded = match exp(cx, mac.span, *tts) {
74 MRAny(expr_maker,_,_) => expr_maker(),
79 "non-expr macro in expr pos: %s",
86 //keep going, outside-in
88 fld.fold_expr(expanded).node.clone();
96 fmt!("'%s' is not a tt-style macro", extnamestr)
104 // Desugar expr_for_loop
105 // From: `for <src_pat> in <src_expr> <src_loop_block>`
106 ast::expr_for_loop(src_pat, src_expr, ref src_loop_block) => {
107 let src_pat = src_pat.clone();
108 let src_expr = src_expr.clone();
110 // Expand any interior macros etc.
111 // NB: we don't fold pats yet. Curious.
112 let src_expr = fld.fold_expr(src_expr).clone();
113 let src_loop_block = fld.fold_block(src_loop_block).clone();
119 pub fn mk_expr(cx: @ExtCtxt, span: span,
120 node: expr_) -> @ast::expr {
128 fn mk_block(cx: @ExtCtxt,
129 stmts: &[@ast::stmt],
130 expr: Option<@ast::expr>,
131 span: span) -> ast::Block {
134 stmts: stmts.to_owned(),
137 rules: ast::DefaultBlock,
142 fn mk_simple_path(ident: ast::ident, span: span) -> ast::Path {
155 // let _i = &mut <src_expr>;
159 // Some(<src_pat>) => <src_loop_block>
164 let local_ident = token::gensym_ident("i");
165 let some_ident = token::str_to_ident("Some");
166 let none_ident = token::str_to_ident("None");
167 let next_ident = token::str_to_ident("next");
169 let local_path_1 = mk_simple_path(local_ident, span);
170 let local_path_2 = mk_simple_path(local_ident, span);
171 let some_path = mk_simple_path(some_ident, span);
172 let none_path = mk_simple_path(none_ident, span);
174 // `let i = &mut <src_expr>`
175 let iter_decl_stmt = {
181 let local = @ast::Local {
186 node: ast::pat_ident(ast::bind_infer, local_path_1, None),
189 init: Some(mk_expr(cx, src_expr.span,
190 ast::expr_addr_of(ast::m_mutbl, src_expr))),
194 let e = @spanned(src_expr.span.lo,
196 ast::decl_local(local));
197 @spanned(lo, hi, ast::stmt_decl(e, cx.next_id()))
202 let break_expr = mk_expr(cx, span, ast::expr_break(None));
203 let break_stmt = @spanned(lo, hi, ast::stmt_expr(break_expr, cx.next_id()));
204 let none_block = mk_block(cx, [break_stmt], None, span);
205 let none_pat = @ast::pat {
207 node: ast::pat_ident(ast::bind_infer, none_path, None),
217 // `Some(<src_pat>) => <src_loop_block>`
219 let pat = @ast::pat {
221 node: ast::pat_enum(some_path, Some(~[src_pat])),
231 // `match i.next() { ... }`
233 let local_expr = mk_expr(cx, span, ast::expr_path(local_path_2));
234 let next_call_expr = mk_expr(cx, span,
235 ast::expr_method_call(cx.next_id(),
236 local_expr, next_ident,
237 ~[], ~[], ast::NoSugar));
238 let match_expr = mk_expr(cx, span, ast::expr_match(next_call_expr,
239 ~[none_arm, some_arm]));
240 @spanned(lo, hi, ast::stmt_expr(match_expr, cx.next_id()))
245 let loop_body_block = mk_block(cx, [match_stmt], None, span);
246 let loop_body_expr = mk_expr(cx, span, ast::expr_loop(loop_body_block, None));
247 let loop_body_stmt = @spanned(lo, hi, ast::stmt_expr(loop_body_expr, cx.next_id()));
248 mk_block(cx, [iter_decl_stmt,
253 (ast::expr_block(loop_block), span)
260 // This is a secondary mechanism for invoking syntax extensions on items:
261 // "decorator" attributes, such as #[auto_encode]. These are invoked by an
262 // attribute prefixing an item, and are interpreted by feeding the item
263 // through the named attribute _as a syntax extension_ and splicing in the
264 // resulting item vec into place in favour of the decorator. Note that
265 // these do _not_ work for macro extensions, just ItemDecorator ones.
267 // NB: there is some redundancy between this and expand_item, below, and
268 // they might benefit from some amount of semantic and language-UI merger.
269 pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
273 orig: @fn(&ast::_mod, @ast_fold) -> ast::_mod)
276 // Fold the contents first:
277 let module_ = orig(module_, fld);
279 // For each item, look through the attributes. If any of them are
280 // decorated with "item decorators", then use that function to transform
281 // the item into a new set of items.
282 let new_items = do vec::flat_map(module_.items) |item| {
283 do item.attrs.rev_iter().fold(~[*item]) |items, attr| {
284 let mname = attr.name();
286 match (*extsbox).find(&intern(mname)) {
287 Some(@SE(ItemDecorator(dec_fn))) => {
288 cx.bt_push(ExpnInfo {
289 call_site: attr.span,
290 callee: NameAndSpan {
295 let r = dec_fn(cx, attr.span, attr.node.value, items);
304 ast::_mod { items: new_items, ..module_ }
308 // eval $e with a new exts frame:
309 macro_rules! with_exts_frame (
310 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
311 ({let extsbox = $extsboxexpr;
312 let oldexts = *extsbox;
313 *extsbox = oldexts.push_frame();
314 extsbox.insert(intern(special_block_name),
315 @BlockInfo(BlockInfo{macros_escape:$macros_escape,pending_renames:@mut ~[]}));
322 static special_block_name : &'static str = " block";
324 // When we enter a module, record it, for the sake of `module!`
325 pub fn expand_item(extsbox: @mut SyntaxEnv,
329 orig: @fn(@ast::item, @ast_fold) -> Option<@ast::item>)
330 -> Option<@ast::item> {
331 // need to do expansion first... it might turn out to be a module.
332 let maybe_it = match it.node {
333 ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
339 ast::item_mod(_) | ast::item_foreign_mod(_) => {
340 cx.mod_push(it.ident);
341 let macro_escape = contains_macro_escape(it.attrs);
342 let result = with_exts_frame!(extsbox,macro_escape,orig(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(extsbox: @mut SyntaxEnv,
361 cx: @ExtCtxt, it: @ast::item,
363 -> Option<@ast::item> {
364 let (pth, tts) = match it.node {
365 item_mac(codemap::spanned { node: mac_invoc_tt(ref pth, ref tts), _}) => {
366 (pth, (*tts).clone())
368 _ => cx.span_bug(it.span, "invalid item macro invocation")
371 let extname = &pth.idents[0];
372 let extnamestr = ident_to_str(extname);
373 let expanded = match (*extsbox).find(&extname.name) {
374 None => cx.span_fatal(pth.span,
375 fmt!("macro undefined: '%s!'", extnamestr)),
377 Some(@SE(NormalTT(ref expand))) => {
378 if it.ident != parse::token::special_idents::invalid {
379 cx.span_fatal(pth.span,
380 fmt!("macro %s! expects no ident argument, \
381 given '%s'", extnamestr,
382 ident_to_str(&it.ident)));
384 cx.bt_push(ExpnInfo {
386 callee: NameAndSpan {
391 ((*expand).expander)(cx, it.span, tts)
393 Some(@SE(IdentTT(ref expand))) => {
394 if it.ident == parse::token::special_idents::invalid {
395 cx.span_fatal(pth.span,
396 fmt!("macro %s! expects an ident argument",
399 cx.bt_push(ExpnInfo {
401 callee: NameAndSpan {
406 ((*expand).expander)(cx, it.span, it.ident, tts)
409 it.span, fmt!("%s! is not legal in item position", extnamestr))
412 let maybe_it = match expanded {
413 MRItem(it) => fld.fold_item(it),
414 MRExpr(_) => cx.span_fatal(pth.span,
415 fmt!("expr macro in item position: %s", extnamestr)),
416 MRAny(_, item_maker, _) => item_maker().chain(|i| {fld.fold_item(i)}),
418 insert_macro(*extsbox,intern(mdef.name), @SE((*mdef).ext));
427 // insert a macro into the innermost frame that doesn't have the
429 fn insert_macro(exts: SyntaxEnv, name: ast::Name, transformer: @Transformer) {
430 let is_non_escaping_block =
431 |t : &@Transformer| -> bool{
433 &@BlockInfo(BlockInfo {macros_escape:false,_}) => true,
434 &@BlockInfo(BlockInfo {_}) => false,
435 _ => fail!(fmt!("special identifier %? was bound to a non-BlockInfo",
439 exts.insert_into_frame(name,transformer,intern(special_block_name),
440 is_non_escaping_block)
444 pub fn expand_stmt(extsbox: @mut SyntaxEnv,
449 orig: @fn(&stmt_, span, @ast_fold)
450 -> (Option<stmt_>, span))
451 -> (Option<stmt_>, span) {
452 let (mac, pth, tts, semi) = match *s {
453 stmt_mac(ref mac, semi) => {
455 mac_invoc_tt(ref pth, ref tts) => {
456 ((*mac).clone(), pth, (*tts).clone(), semi)
460 _ => return orig(s, sp, fld)
462 if (pth.idents.len() > 1u) {
465 fmt!("expected macro name without module \
468 let extname = &pth.idents[0];
469 let extnamestr = ident_to_str(extname);
470 let (fully_expanded, sp) = match (*extsbox).find(&extname.name) {
472 cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", extnamestr)),
475 SyntaxExpanderTT{expander: exp, span: exp_sp}))) => {
476 cx.bt_push(ExpnInfo {
478 callee: NameAndSpan { name: extnamestr, span: exp_sp }
480 let expanded = match exp(cx, mac.span, tts) {
482 @codemap::spanned { node: stmt_expr(e, cx.next_id()),
484 MRAny(_,_,stmt_mkr) => stmt_mkr(),
487 fmt!("non-stmt macro in stmt pos: %s", extnamestr))
490 //keep going, outside-in
491 let fully_expanded = match fld.fold_stmt(expanded) {
493 let fully_expanded = &stmt.node;
495 (*fully_expanded).clone()
498 cx.span_fatal(pth.span,
499 "macro didn't expand to a statement")
507 cx.span_fatal(pth.span,
508 fmt!("'%s' is not a tt-style macro", extnamestr))
512 (match fully_expanded {
513 stmt_expr(e, stmt_id) if semi => Some(stmt_semi(e, stmt_id)),
514 _ => { Some(fully_expanded) } /* might already have a semi */
520 struct NewNameFinderContext {
521 ident_accumulator: @mut ~[ast::ident],
524 impl Visitor<()> for NewNameFinderContext {
525 fn visit_pat(&mut self, pattern: @ast::pat, _: ()) {
527 // we found a pat_ident!
530 node: ast::pat_ident(_, ref path, ref inner),
534 // a path of length one:
541 } => self.ident_accumulator.push(id),
542 // I believe these must be enums...
545 // visit optional subpattern of pat_ident:
546 for subpat in inner.iter() {
547 self.visit_pat(*subpat, ())
550 // use the default traversal for non-pat_idents
551 _ => visit::walk_pat(self, pattern, ())
555 // XXX: Methods below can become default methods.
557 fn visit_mod(&mut self, module: &ast::_mod, _: span, _: NodeId, _: ()) {
558 visit::walk_mod(self, module, ())
561 fn visit_view_item(&mut self, view_item: &ast::view_item, _: ()) {
562 visit::walk_view_item(self, view_item, ())
565 fn visit_item(&mut self, item: @ast::item, _: ()) {
566 visit::walk_item(self, item, ())
569 fn visit_foreign_item(&mut self,
570 foreign_item: @ast::foreign_item,
572 visit::walk_foreign_item(self, foreign_item, ())
575 fn visit_local(&mut self, local: @ast::Local, _: ()) {
576 visit::walk_local(self, local, ())
579 fn visit_block(&mut self, block: &ast::Block, _: ()) {
580 visit::walk_block(self, block, ())
583 fn visit_stmt(&mut self, stmt: @ast::stmt, _: ()) {
584 visit::walk_stmt(self, stmt, ())
587 fn visit_arm(&mut self, arm: &ast::arm, _: ()) {
588 visit::walk_arm(self, arm, ())
591 fn visit_decl(&mut self, decl: @ast::decl, _: ()) {
592 visit::walk_decl(self, decl, ())
595 fn visit_expr(&mut self, expr: @ast::expr, _: ()) {
596 visit::walk_expr(self, expr, ())
599 fn visit_expr_post(&mut self, _: @ast::expr, _: ()) {
603 fn visit_ty(&mut self, typ: &ast::Ty, _: ()) {
604 visit::walk_ty(self, typ, ())
607 fn visit_generics(&mut self, generics: &ast::Generics, _: ()) {
608 visit::walk_generics(self, generics, ())
611 fn visit_fn(&mut self,
612 function_kind: &visit::fn_kind,
613 function_declaration: &ast::fn_decl,
620 function_declaration,
627 fn visit_ty_method(&mut self, ty_method: &ast::TypeMethod, _: ()) {
628 visit::walk_ty_method(self, ty_method, ())
631 fn visit_trait_method(&mut self,
632 trait_method: &ast::trait_method,
634 visit::walk_trait_method(self, trait_method, ())
637 fn visit_struct_def(&mut self,
638 struct_def: @ast::struct_def,
640 generics: &ast::Generics,
643 visit::walk_struct_def(self,
651 fn visit_struct_field(&mut self,
652 struct_field: @ast::struct_field,
654 visit::walk_struct_field(self, struct_field, ())
658 // return a visitor that extracts the pat_ident paths
659 // from a given pattern and puts them in a mutable
660 // array (passed in to the traversal)
661 pub fn new_name_finder(idents: @mut ~[ast::ident]) -> @mut Visitor<()> {
662 let context = @mut NewNameFinderContext {
663 ident_accumulator: idents,
665 context as @mut Visitor<()>
668 pub fn expand_block(extsbox: @mut SyntaxEnv,
672 orig: @fn(&Block, @ast_fold) -> Block)
674 // see note below about treatment of exts table
675 with_exts_frame!(extsbox,false,orig(blk,fld))
679 // get the (innermost) BlockInfo from an exts stack
680 fn get_block_info(exts : SyntaxEnv) -> BlockInfo {
681 match exts.find_in_topmost_frame(&intern(special_block_name)) {
682 Some(@BlockInfo(bi)) => bi,
683 _ => fail!(fmt!("special identifier %? was bound to a non-BlockInfo",
689 // given a mutable list of renames, return a tree-folder that applies those
691 fn renames_to_fold(renames : @mut ~[(ast::ident,ast::Name)]) -> @ast_fold {
692 let afp = default_ast_fold();
693 let f_pre = @AstFoldFns {
695 // the individual elements are memoized... it would
696 // also be possible to memoize on the whole list at once.
697 let new_ctxt = renames.iter().fold(id.ctxt,|ctxt,&(from,to)| {
698 new_rename(from,to,ctxt)
700 ast::ident{name:id.name,ctxt:new_ctxt}
707 // perform a bunch of renames
708 fn apply_pending_renames(folder : @ast_fold, stmt : ast::stmt) -> @ast::stmt {
709 match folder.fold_stmt(&stmt) {
711 None => fail!(fmt!("renaming of stmt produced None"))
717 pub fn new_span(cx: @ExtCtxt, sp: span) -> span {
718 /* this discards information in the case of macro-defining macros */
719 return span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
722 // FIXME (#2247): this is a moderately bad kludge to inject some macros into
723 // the default compilation environment. It would be much nicer to use
724 // a mechanism like syntax_quote to ensure hygiene.
726 pub fn std_macros() -> @str {
732 macro_rules! ignore (($($x:tt)*) => (()))
736 __log(1u32, fmt!( \"%?\", $arg ))
738 ($( $arg:expr ),+) => (
739 __log(1u32, fmt!( $($arg),+ ))
745 __log(2u32, fmt!( \"%?\", $arg ))
747 ($( $arg:expr ),+) => (
748 __log(2u32, fmt!( $($arg),+ ))
754 __log(3u32, fmt!( \"%?\", $arg ))
756 ($( $arg:expr ),+) => (
757 __log(3u32, fmt!( $($arg),+ ))
761 // conditionally define debug!, but keep it type checking even
762 // in non-debug builds.
763 macro_rules! __debug (
765 __log(4u32, fmt!( \"%?\", $arg ))
767 ($( $arg:expr ),+) => (
768 __log(4u32, fmt!( $($arg),+ ))
775 macro_rules! debug (($($arg:expr),*) => {
783 macro_rules! debug (($($arg:expr),*) => {
784 if false { __debug!($($arg),*) }
790 fail!(\"explicit failure\")
793 ::std::sys::FailWithCause::fail_with($msg, file!(), line!())
795 ($( $arg:expr ),+) => (
796 ::std::sys::FailWithCause::fail_with(fmt!( $($arg),+ ), file!(), line!())
803 ::std::sys::FailWithCause::fail_with(
804 \"assertion failed: \" + stringify!($cond), file!(), line!())
807 ($cond:expr, $msg:expr) => {
809 ::std::sys::FailWithCause::fail_with($msg, file!(), line!())
812 ($cond:expr, $( $arg:expr ),+) => {
814 ::std::sys::FailWithCause::fail_with(fmt!( $($arg),+ ), file!(), line!())
819 macro_rules! assert_eq (
820 ($given:expr , $expected:expr) => (
822 let given_val = $given;
823 let expected_val = $expected;
824 // check both directions of equality....
825 if !((given_val == expected_val) && (expected_val == given_val)) {
826 fail!(\"assertion failed: `(left == right) && (right == \
827 left)` (left: `%?`, right: `%?`)\", given_val, expected_val);
833 macro_rules! assert_approx_eq (
834 ($given:expr , $expected:expr) => (
836 use std::cmp::ApproxEq;
838 let given_val = $given;
839 let expected_val = $expected;
840 // check both directions of equality....
842 given_val.approx_eq(&expected_val) &&
843 expected_val.approx_eq(&given_val)
845 fail!(\"left: %? does not approximately equal right: %?\",
846 given_val, expected_val);
850 ($given:expr , $expected:expr , $epsilon:expr) => (
852 use std::cmp::ApproxEq;
854 let given_val = $given;
855 let expected_val = $expected;
856 let epsilon_val = $epsilon;
857 // check both directions of equality....
859 given_val.approx_eq_eps(&expected_val, &epsilon_val) &&
860 expected_val.approx_eq_eps(&given_val, &epsilon_val)
862 fail!(\"left: %? does not approximately equal right: %? with epsilon: %?\",
863 given_val, expected_val, epsilon_val);
869 macro_rules! condition (
871 { pub $c:ident: $input:ty -> $out:ty; } => {
874 #[allow(non_uppercase_statics)];
875 static key: ::std::local_data::Key<
876 @::std::condition::Handler<$input, $out>> =
877 &::std::local_data::Key;
880 ::std::condition::Condition<$input,$out> =
881 ::std::condition::Condition {
882 name: stringify!($c),
888 { $c:ident: $input:ty -> $out:ty; } => {
890 // FIXME (#6009): remove mod's `pub` below once variant above lands.
892 #[allow(non_uppercase_statics)];
893 static key: ::std::local_data::Key<
894 @::std::condition::Handler<$input, $out>> =
895 &::std::local_data::Key;
898 ::std::condition::Condition<$input,$out> =
899 ::std::condition::Condition {
900 name: stringify!($c),
908 // A scheme-style conditional that helps to improve code clarity in some instances when
909 // the `if`, `else if`, and `else` keywords obscure predicates undesirably.
916 // else if x < mn { mn }
920 // Using `cond!`, the above could be written as:
923 // let clamped = cond!(
930 // The optional default case is denoted by `_`.
933 ( $(($pred:expr) $body:block)+ _ $default:block ) => (
934 $(if $pred $body else)+
937 // for if the default case was ommitted
938 ( $(($pred:expr) $body:block)+ ) => (
939 $(if $pred $body)else+
943 macro_rules! printf (
945 print(fmt!(\"%?\", $arg))
947 ($( $arg:expr ),+) => (
948 print(fmt!($($arg),+))
952 macro_rules! printfln (
954 println(fmt!(\"%?\", $arg))
956 ($( $arg:expr ),+) => (
957 println(fmt!($($arg),+))
961 // NOTE: use this after a snapshot lands to abstract the details
962 // of the TLS interface.
963 macro_rules! local_data_key (
964 ($name:ident: $ty:ty) => (
965 static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key;
967 (pub $name:ident: $ty:ty) => (
968 pub static $name: ::std::local_data::Key<$ty> = &::std::local_data::Key;
972 // externfn! declares a wrapper for an external function.
973 // It is intended to be used like:
975 // externfn!(#[nolink]
976 // #[abi = \"cdecl\"]
977 // fn memcmp(cx: *u8, ct: *u8, n: u32) -> u32)
979 // Due to limitations in the macro parser, this pattern must be
980 // implemented with 4 distinct patterns (with attrs / without
981 // attrs CROSS with args / without ARGS).
983 // Also, this macro grammar allows for any number of return types
984 // because I couldn't figure out the syntax to specify at most one.
985 macro_rules! externfn(
986 (fn $name:ident () $(-> $ret_ty:ty),*) => (
987 pub unsafe fn $name() $(-> $ret_ty),* {
988 // Note: to avoid obscure bug in macros, keep these
989 // attributes *internal* to the fn
990 #[fixed_stack_segment];
992 #[allow(missing_doc)];
997 fn $name() $(-> $ret_ty),*;
1001 (fn $name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret_ty:ty),*) => (
1002 pub unsafe fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),* {
1003 // Note: to avoid obscure bug in macros, keep these
1004 // attributes *internal* to the fn
1005 #[fixed_stack_segment];
1007 #[allow(missing_doc)];
1009 return $name($($arg_name),*);
1012 fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),*;
1016 ($($attrs:attr)* fn $name:ident () $(-> $ret_ty:ty),*) => (
1017 pub unsafe fn $name() $(-> $ret_ty),* {
1018 // Note: to avoid obscure bug in macros, keep these
1019 // attributes *internal* to the fn
1020 #[fixed_stack_segment];
1022 #[allow(missing_doc)];
1028 fn $name() $(-> $ret_ty),*;
1032 ($($attrs:attr)* fn $name:ident ($($arg_name:ident : $arg_ty:ty),*) $(-> $ret_ty:ty),*) => (
1033 pub unsafe fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),* {
1034 // Note: to avoid obscure bug in macros, keep these
1035 // attributes *internal* to the fn
1036 #[fixed_stack_segment];
1038 #[allow(missing_doc)];
1040 return $name($($arg_name),*);
1044 fn $name($($arg_name : $arg_ty),*) $(-> $ret_ty),*;
1053 // add a bunch of macros as though they were placed at the head of the
1054 // program (ick). This should run before cfg stripping.
1055 pub fn inject_std_macros(parse_sess: @mut parse::ParseSess,
1056 cfg: ast::CrateConfig, c: &Crate) -> @Crate {
1057 let sm = match parse_item_from_source_str(@"<std-macros>",
1063 None => fail!("expected core macros to parse correctly")
1066 let injecter = @AstFoldFns {
1067 fold_mod: |modd, _| {
1068 // just inject the std macros at the start of the first
1069 // module in the crate (i.e the crate file itself.)
1070 let items = vec::append(~[sm], modd.items);
1073 // FIXME #2543: Bad copy.
1077 .. *default_ast_fold()
1079 @make_fold(injecter).fold_crate(c)
1082 pub fn expand_crate(parse_sess: @mut parse::ParseSess,
1083 cfg: ast::CrateConfig, c: &Crate) -> @Crate {
1084 // adding *another* layer of indirection here so that the block
1085 // visitor can swap out one exts table for another for the duration
1086 // of the block. The cleaner alternative would be to thread the
1087 // exts table through the fold, but that would require updating
1088 // every method/element of AstFoldFns in fold.rs.
1089 let extsbox = @mut syntax_expander_table();
1090 let afp = default_ast_fold();
1091 let cx = ExtCtxt::new(parse_sess, cfg.clone());
1092 let f_pre = @AstFoldFns {
1093 fold_expr: |expr,span,recur|
1094 expand_expr(extsbox, cx, expr, span, recur, afp.fold_expr),
1095 fold_mod: |modd,recur|
1096 expand_mod_items(extsbox, cx, modd, recur, afp.fold_mod),
1097 fold_item: |item,recur|
1098 expand_item(extsbox, cx, item, recur, afp.fold_item),
1099 fold_stmt: |stmt,span,recur|
1100 expand_stmt(extsbox, cx, stmt, span, recur, afp.fold_stmt),
1101 fold_block: |blk,recur|
1102 expand_block(extsbox, cx, blk, recur, afp.fold_block),
1103 new_span: |a| new_span(cx, a),
1105 let f = make_fold(f_pre);
1107 let ret = @f.fold_crate(c);
1108 parse_sess.span_diagnostic.handler().abort_if_errors();
1112 // given a function from idents to idents, produce
1113 // an ast_fold that applies that function:
1114 pub fn fun_to_ident_folder(f: @fn(ast::ident)->ast::ident) -> @ast_fold{
1115 let afp = default_ast_fold();
1116 let f_pre = @AstFoldFns{
1117 fold_ident : |id, _| f(id),
1123 // update the ctxts in a path to get a rename node
1124 pub fn new_ident_renamer(from: ast::ident,
1126 @fn(ast::ident)->ast::ident {
1130 ctxt: new_rename(from,to,id.ctxt)
1135 // update the ctxts in a path to get a mark node
1136 pub fn new_ident_marker(mark: uint) ->
1137 @fn(ast::ident)->ast::ident {
1141 ctxt: new_mark(mark,id.ctxt)
1145 // perform resolution (in the MTWT sense) on all of the
1146 // idents in the tree. This is the final step in expansion.
1147 pub fn new_ident_resolver() ->
1148 @fn(ast::ident)->ast::ident {
1161 use ast::{Attribute_, AttrOuter, MetaWord, empty_ctxt};
1163 use codemap::spanned;
1165 use parse::token::{intern, get_ident_interner};
1167 use util::parser_testing::{string_to_item, string_to_pat, strs_to_idents};
1169 // make sure that fail! is present
1170 #[test] fn fail_exists_test () {
1171 let src = @"fn main() { fail!(\"something appropriately gloomy\");}";
1172 let sess = parse::new_parse_sess(None);
1173 let crate_ast = parse::parse_crate_from_source_str(
1177 let crate_ast = inject_std_macros(sess, ~[], crate_ast);
1178 // don't bother with striping, doesn't affect fail!.
1179 expand_crate(sess,~[],crate_ast);
1182 // these following tests are quite fragile, in that they don't test what
1183 // *kind* of failure occurs.
1185 // make sure that macros can leave scope
1187 #[test] fn macros_cant_escape_fns_test () {
1188 let src = @"fn bogus() {macro_rules! z (() => (3+4))}\
1189 fn inty() -> int { z!() }";
1190 let sess = parse::new_parse_sess(None);
1191 let crate_ast = parse::parse_crate_from_source_str(
1196 expand_crate(sess,~[],crate_ast);
1199 // make sure that macros can leave scope for modules
1201 #[test] fn macros_cant_escape_mods_test () {
1202 let src = @"mod foo {macro_rules! z (() => (3+4))}\
1203 fn inty() -> int { z!() }";
1204 let sess = parse::new_parse_sess(None);
1205 let crate_ast = parse::parse_crate_from_source_str(
1210 expand_crate(sess,~[],crate_ast);
1213 // macro_escape modules shouldn't cause macros to leave scope
1214 #[test] fn macros_can_escape_flattened_mods_test () {
1215 let src = @"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
1216 fn inty() -> int { z!() }";
1217 let sess = parse::new_parse_sess(None);
1218 let crate_ast = parse::parse_crate_from_source_str(
1223 expand_crate(sess,~[],crate_ast);
1226 #[test] fn std_macros_must_parse () {
1227 let src = super::std_macros();
1228 let sess = parse::new_parse_sess(None);
1230 let item_ast = parse::parse_item_from_source_str(
1235 Some(_) => (), // success
1236 None => fail!("expected this to parse")
1240 #[test] fn test_contains_flatten (){
1241 let attr1 = make_dummy_attr (@"foo");
1242 let attr2 = make_dummy_attr (@"bar");
1243 let escape_attr = make_dummy_attr (@"macro_escape");
1244 let attrs1 = ~[attr1, escape_attr, attr2];
1245 assert_eq!(contains_macro_escape (attrs1),true);
1246 let attrs2 = ~[attr1,attr2];
1247 assert_eq!(contains_macro_escape (attrs2),false);
1250 // make a MetaWord outer attribute with the given name
1251 fn make_dummy_attr(s: @str) -> ast::Attribute {
1253 span:codemap::dummy_sp(),
1258 span: codemap::dummy_sp(),
1260 is_sugared_doc: false,
1267 let maybe_item_ast = string_to_item(@"fn a() -> int { let b = 13; b }");
1268 let item_ast = match maybe_item_ast {
1270 None => fail!("test case fail")
1272 let a_name = intern("a");
1273 let a2_name = intern("a2");
1274 let renamer = new_ident_renamer(ast::ident{name:a_name,ctxt:empty_ctxt},
1276 let renamed_ast = fun_to_ident_folder(renamer).fold_item(item_ast).unwrap();
1277 let resolver = new_ident_resolver();
1278 let resolved_ast = fun_to_ident_folder(resolver).fold_item(renamed_ast).unwrap();
1279 let resolved_as_str = pprust::item_to_str(resolved_ast,
1280 get_ident_interner());
1281 assert_eq!(resolved_as_str,~"fn a2() -> int { let b = 13; b }");
1286 // sigh... it looks like I have two different renaming mechanisms, now...
1290 let pat = string_to_pat(@"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");
1291 let idents = @mut ~[];
1292 let pat_idents = new_name_finder(idents);
1293 pat_idents.visit_pat(pat, ());
1294 assert_eq!(idents, @mut strs_to_idents(~["a","c","b","d"]));