]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
Remove 'Local Variable' comments
[rust.git] / src / libsyntax / ext / expand.rs
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.
4 //
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.
10
11 use ast::{blk_, attribute_, attr_outer, meta_word};
12 use ast::{crate, expr_, expr_mac, mac_invoc_tt};
13 use ast::{item_mac, stmt_, stmt_mac, stmt_expr, stmt_semi};
14 use ast;
15 use attr;
16 use codemap;
17 use codemap::{span, CallInfo, ExpandedFrom, NameAndSpan, spanned};
18 use ext::base::*;
19 use fold::*;
20 use parse;
21 use parse::{parse_item_from_source_str};
22
23 pub fn expand_expr(extsbox: @mut SyntaxEnv,
24                    cx: @ext_ctxt,
25                    e: &expr_,
26                    s: span,
27                    fld: @ast_fold,
28                    orig: @fn(&expr_, span, @ast_fold) -> (expr_, span))
29                 -> (expr_, span) {
30     match *e {
31         // expr_mac should really be expr_ext or something; it's the
32         // entry-point for all syntax extensions.
33         expr_mac(ref mac) => {
34             match (*mac).node {
35                 // Token-tree macros:
36                 mac_invoc_tt(pth, ref tts) => {
37                     if (pth.idents.len() > 1u) {
38                         cx.span_fatal(
39                             pth.span,
40                             fmt!("expected macro name without module \
41                                   separators"));
42                     }
43                     /* using idents and token::special_idents would make the
44                     the macro names be hygienic */
45                     let extname = cx.parse_sess().interner.get(pth.idents[0]);
46                     // leaving explicit deref here to highlight unbox op:
47                     match (*extsbox).find(&extname) {
48                         None => {
49                             cx.span_fatal(
50                                 pth.span,
51                                 fmt!("macro undefined: '%s'", *extname))
52                         }
53                         Some(@SE(NormalTT(SyntaxExpanderTT{
54                             expander: exp,
55                             span: exp_sp
56                         }))) => {
57                             cx.bt_push(ExpandedFrom(CallInfo {
58                                 call_site: s,
59                                 callee: NameAndSpan {
60                                     name: copy *extname,
61                                     span: exp_sp,
62                                 },
63                             }));
64
65                             let expanded = match exp(cx, mac.span, *tts) {
66                                 MRExpr(e) => e,
67                                 MRAny(expr_maker,_,_) => expr_maker(),
68                                 _ => {
69                                     cx.span_fatal(
70                                         pth.span,
71                                         fmt!(
72                                             "non-expr macro in expr pos: %s",
73                                             *extname
74                                         )
75                                     )
76                                 }
77                             };
78
79                             //keep going, outside-in
80                             let fully_expanded =
81                                 copy fld.fold_expr(expanded).node;
82                             cx.bt_pop();
83
84                             (fully_expanded, s)
85                         }
86                         _ => {
87                             cx.span_fatal(
88                                 pth.span,
89                                 fmt!("'%s' is not a tt-style macro", *extname)
90                             )
91                         }
92                     }
93                 }
94             }
95         }
96         _ => orig(e, s, fld)
97     }
98 }
99
100 // This is a secondary mechanism for invoking syntax extensions on items:
101 // "decorator" attributes, such as #[auto_encode]. These are invoked by an
102 // attribute prefixing an item, and are interpreted by feeding the item
103 // through the named attribute _as a syntax extension_ and splicing in the
104 // resulting item vec into place in favour of the decorator. Note that
105 // these do _not_ work for macro extensions, just ItemDecorator ones.
106 //
107 // NB: there is some redundancy between this and expand_item, below, and
108 // they might benefit from some amount of semantic and language-UI merger.
109 pub fn expand_mod_items(extsbox: @mut SyntaxEnv,
110                         cx: @ext_ctxt,
111                         module_: &ast::_mod,
112                         fld: @ast_fold,
113                         orig: @fn(&ast::_mod, @ast_fold) -> ast::_mod)
114                      -> ast::_mod {
115     // Fold the contents first:
116     let module_ = orig(module_, fld);
117
118     // For each item, look through the attributes.  If any of them are
119     // decorated with "item decorators", then use that function to transform
120     // the item into a new set of items.
121     let new_items = do vec::flat_map(module_.items) |item| {
122         do vec::foldr(item.attrs, ~[*item]) |attr, items| {
123             let mname = attr::get_attr_name(attr);
124
125             match (*extsbox).find(&mname) {
126               Some(@SE(ItemDecorator(dec_fn))) => {
127                   cx.bt_push(ExpandedFrom(CallInfo {
128                       call_site: attr.span,
129                       callee: NameAndSpan {
130                           name: /*bad*/ copy *mname,
131                           span: None
132                       }
133                   }));
134                   let r = dec_fn(cx, attr.span, attr.node.value, items);
135                   cx.bt_pop();
136                   r
137               },
138               _ => items,
139             }
140         }
141     };
142
143     ast::_mod { items: new_items, ..module_ }
144 }
145
146
147 // eval $e with a new exts frame:
148 macro_rules! with_exts_frame (
149     ($extsboxexpr:expr,$e:expr) =>
150     ({let extsbox = $extsboxexpr;
151       let oldexts = *extsbox;
152       *extsbox = oldexts.push_frame();
153       let result = $e;
154       *extsbox = oldexts;
155       result
156      })
157 )
158
159 // When we enter a module, record it, for the sake of `module!`
160 pub fn expand_item(extsbox: @mut SyntaxEnv,
161                    cx: @ext_ctxt,
162                    it: @ast::item,
163                    fld: @ast_fold,
164                    orig: @fn(@ast::item, @ast_fold) -> Option<@ast::item>)
165                 -> Option<@ast::item> {
166     // need to do expansion first... it might turn out to be a module.
167     let maybe_it = match it.node {
168       ast::item_mac(*) => expand_item_mac(extsbox, cx, it, fld),
169       _ => Some(it)
170     };
171     match maybe_it {
172       Some(it) => {
173           match it.node {
174               ast::item_mod(_) | ast::item_foreign_mod(_) => {
175                   cx.mod_push(it.ident);
176                   let result =
177                       // don't push a macro scope for macro_escape:
178                       if contains_macro_escape(it.attrs) {
179                       orig(it,fld)
180                   } else {
181                       // otherwise, push a scope:
182                       with_exts_frame!(extsbox,orig(it,fld))
183                   };
184                   cx.mod_pop();
185                   result
186               }
187               _ => orig(it,fld)
188           }
189       }
190       None => None
191     }
192 }
193
194 // does this attribute list contain "macro_escape" ?
195 pub fn contains_macro_escape (attrs: &[ast::attribute]) -> bool{
196     let mut accum = false;
197     do attrs.each |attr| {
198         let mname = attr::get_attr_name(attr);
199         if (mname == @~"macro_escape") {
200             accum = true;
201             false
202         } else {
203             true
204         }
205     }
206     accum
207 }
208
209 // this macro disables (one layer of) macro
210 // scoping, to allow a block to add macro bindings
211 // to its parent env
212 macro_rules! without_macro_scoping(
213     ($extsexpr:expr,$exp:expr) =>
214     ({
215         // only evaluate this once:
216         let exts = $extsexpr;
217         // capture the existing binding:
218         let existingBlockBinding =
219             match exts.find(&@~" block"){
220                 Some(binding) => binding,
221                 None => cx.bug("expected to find \" block\" binding")
222             };
223         // this prevents the block from limiting the macros' scope:
224         exts.insert(@~" block",@ScopeMacros(false));
225         let result = $exp;
226         // reset the block binding. Note that since the original
227         // one may have been inherited, this procedure may wind
228         // up introducing a block binding where one didn't exist
229         // before.
230         exts.insert(@~" block",existingBlockBinding);
231         result
232     }))
233
234 // Support for item-position macro invocations, exactly the same
235 // logic as for expression-position macro invocations.
236 pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
237                        cx: @ext_ctxt, it: @ast::item,
238                        fld: @ast_fold)
239                     -> Option<@ast::item> {
240     let (pth, tts) = match it.node {
241         item_mac(codemap::spanned { node: mac_invoc_tt(pth, ref tts), _}) => {
242             (pth, copy *tts)
243         }
244         _ => cx.span_bug(it.span, ~"invalid item macro invocation")
245     };
246
247     let extname = cx.parse_sess().interner.get(pth.idents[0]);
248     let expanded = match (*extsbox).find(&extname) {
249         None => cx.span_fatal(pth.span,
250                               fmt!("macro undefined: '%s!'", *extname)),
251
252         Some(@SE(NormalTT(ref expand))) => {
253             if it.ident != parse::token::special_idents::invalid {
254                 cx.span_fatal(pth.span,
255                               fmt!("macro %s! expects no ident argument, \
256                                     given '%s'", *extname,
257                                    *cx.parse_sess().interner.get(it.ident)));
258             }
259             cx.bt_push(ExpandedFrom(CallInfo {
260                 call_site: it.span,
261                 callee: NameAndSpan {
262                     name: copy *extname,
263                     span: expand.span
264                 }
265             }));
266             ((*expand).expander)(cx, it.span, tts)
267         }
268         Some(@SE(IdentTT(ref expand))) => {
269             if it.ident == parse::token::special_idents::invalid {
270                 cx.span_fatal(pth.span,
271                               fmt!("macro %s! expects an ident argument",
272                                    *extname));
273             }
274             cx.bt_push(ExpandedFrom(CallInfo {
275                 call_site: it.span,
276                 callee: NameAndSpan {
277                     name: copy *extname,
278                     span: expand.span
279                 }
280             }));
281             ((*expand).expander)(cx, it.span, it.ident, tts)
282         }
283         _ => cx.span_fatal(
284             it.span, fmt!("%s! is not legal in item position", *extname))
285     };
286
287     let maybe_it = match expanded {
288         MRItem(it) => fld.fold_item(it),
289         MRExpr(_) => cx.span_fatal(pth.span,
290                                     ~"expr macro in item position: "
291                                     + *extname),
292         MRAny(_, item_maker, _) => item_maker().chain(|i| {fld.fold_item(i)}),
293         MRDef(ref mdef) => {
294             extsbox.insert(@/*bad*/ copy mdef.name, @SE((*mdef).ext));
295             None
296         }
297     };
298     cx.bt_pop();
299     return maybe_it;
300 }
301
302 // expand a stmt
303 pub fn expand_stmt(extsbox: @mut SyntaxEnv,
304                    cx: @ext_ctxt,
305                    s: &stmt_,
306                    sp: span,
307                    fld: @ast_fold,
308                    orig: @fn(&stmt_, span, @ast_fold) -> (stmt_, span))
309                 -> (stmt_, span) {
310     let (mac, pth, tts, semi) = match *s {
311         stmt_mac(ref mac, semi) => {
312             match mac.node {
313                 mac_invoc_tt(pth, ref tts) => {
314                     (copy *mac, pth, copy *tts, semi)
315                 }
316             }
317         }
318         _ => return orig(s, sp, fld)
319     };
320     if (pth.idents.len() > 1u) {
321         cx.span_fatal(
322             pth.span,
323             fmt!("expected macro name without module \
324                   separators"));
325     }
326     let extname = cx.parse_sess().interner.get(pth.idents[0]);
327     let (fully_expanded, sp) = match (*extsbox).find(&extname) {
328         None =>
329             cx.span_fatal(pth.span, fmt!("macro undefined: '%s'", *extname)),
330
331         Some(@SE(NormalTT(
332             SyntaxExpanderTT{expander: exp, span: exp_sp}))) => {
333             cx.bt_push(ExpandedFrom(CallInfo {
334                 call_site: sp,
335                 callee: NameAndSpan { name: copy *extname, span: exp_sp }
336             }));
337             let expanded = match exp(cx, mac.span, tts) {
338                 MRExpr(e) =>
339                     @codemap::spanned { node: stmt_expr(e, cx.next_id()),
340                                     span: e.span},
341                 MRAny(_,_,stmt_mkr) => stmt_mkr(),
342                 _ => cx.span_fatal(
343                     pth.span,
344                     fmt!("non-stmt macro in stmt pos: %s", *extname))
345             };
346
347             //keep going, outside-in
348             let fully_expanded = copy fld.fold_stmt(expanded).node;
349             cx.bt_pop();
350
351             (fully_expanded, sp)
352         }
353
354         _ => {
355             cx.span_fatal(pth.span,
356                           fmt!("'%s' is not a tt-style macro", *extname))
357         }
358     };
359
360     (match fully_expanded {
361         stmt_expr(e, stmt_id) if semi => stmt_semi(e, stmt_id),
362         _ => { fully_expanded } /* might already have a semi */
363     }, sp)
364
365 }
366
367
368
369 pub fn expand_block(extsbox: @mut SyntaxEnv,
370                     cx: @ext_ctxt,
371                     blk: &blk_,
372                     sp: span,
373                     fld: @ast_fold,
374                     orig: @fn(&blk_, span, @ast_fold) -> (blk_, span))
375                  -> (blk_, span) {
376     match (*extsbox).find(&@~" block") {
377         // no scope limit on macros in this block, no need
378         // to push an exts frame:
379         Some(@ScopeMacros(false)) => {
380             orig (blk,sp,fld)
381         },
382         // this block should limit the scope of its macros:
383         Some(@ScopeMacros(true)) => {
384             // see note below about treatment of exts table
385             with_exts_frame!(extsbox,orig(blk,sp,fld))
386         },
387         _ => cx.span_bug(sp,
388                          ~"expected ScopeMacros binding for \" block\"")
389     }
390 }
391
392 pub fn new_span(cx: @ext_ctxt, sp: span) -> span {
393     /* this discards information in the case of macro-defining macros */
394     return span {lo: sp.lo, hi: sp.hi, expn_info: cx.backtrace()};
395 }
396
397 // FIXME (#2247): this is a moderately bad kludge to inject some macros into
398 // the default compilation environment. It would be much nicer to use
399 // a mechanism like syntax_quote to ensure hygiene.
400
401 pub fn core_macros() -> ~str {
402     return
403 ~"pub mod macros {
404     macro_rules! ignore (($($x:tt)*) => (()))
405
406     macro_rules! error (
407         ($arg:expr) => (
408             __log(1u32, fmt!( \"%?\", $arg ))
409         );
410         ($( $arg:expr ),+) => (
411             __log(1u32, fmt!( $($arg),+ ))
412         )
413     )
414
415     macro_rules! warn (
416         ($arg:expr) => (
417             __log(2u32, fmt!( \"%?\", $arg ))
418         );
419         ($( $arg:expr ),+) => (
420             __log(2u32, fmt!( $($arg),+ ))
421         )
422     )
423
424     macro_rules! info (
425         ($arg:expr) => (
426             __log(3u32, fmt!( \"%?\", $arg ))
427         );
428         ($( $arg:expr ),+) => (
429             __log(3u32, fmt!( $($arg),+ ))
430         )
431     )
432
433     macro_rules! debug (
434         ($arg:expr) => (
435             __log(4u32, fmt!( \"%?\", $arg ))
436         );
437         ($( $arg:expr ),+) => (
438             __log(4u32, fmt!( $($arg),+ ))
439         )
440     )
441
442     macro_rules! fail(
443         () => (
444             fail!(\"explicit failure\")
445         );
446         ($msg:expr) => (
447             ::core::sys::FailWithCause::fail_with($msg, file!(), line!())
448         );
449         ($( $arg:expr ),+) => (
450             ::core::sys::FailWithCause::fail_with(fmt!( $($arg),+ ), file!(), line!())
451         )
452     )
453
454     macro_rules! assert(
455         ($cond:expr) => {
456             if !$cond {
457                 ::core::sys::FailWithCause::fail_with(
458                     ~\"assertion failed: \" + stringify!($cond), file!(), line!())
459             }
460         };
461         ($cond:expr, $msg:expr) => {
462             if !$cond {
463                 ::core::sys::FailWithCause::fail_with($msg, file!(), line!())
464             }
465         };
466         ($cond:expr, $( $arg:expr ),+) => {
467             if !$cond {
468                 ::core::sys::FailWithCause::fail_with(fmt!( $($arg),+ ), file!(), line!())
469             }
470         }
471     )
472
473     macro_rules! assert_eq (
474         ($given:expr , $expected:expr) => (
475             {
476                 let given_val = $given;
477                 let expected_val = $expected;
478                 // check both directions of equality....
479                 if !((given_val == expected_val) && (expected_val == given_val)) {
480                     fail!(fmt!(\"left: %? != right: %?\", given_val, expected_val));
481                 }
482             }
483         )
484     )
485
486     macro_rules! condition (
487
488         { $c:ident: $in:ty -> $out:ty; } => {
489
490             mod $c {
491                 fn key(_x: @::core::condition::Handler<$in,$out>) { }
492
493                 pub static cond :
494                     ::core::condition::Condition<'static,$in,$out> =
495                     ::core::condition::Condition {
496                         name: stringify!($c),
497                         key: key
498                     };
499             }
500         }
501     )
502
503
504 }";
505 }
506
507 pub fn expand_crate(parse_sess: @mut parse::ParseSess,
508                     cfg: ast::crate_cfg, c: @crate) -> @crate {
509     // adding *another* layer of indirection here so that the block
510     // visitor can swap out one exts table for another for the duration
511     // of the block.  The cleaner alternative would be to thread the
512     // exts table through the fold, but that would require updating
513     // every method/element of AstFoldFns in fold.rs.
514     let extsbox = @mut syntax_expander_table();
515     let afp = default_ast_fold();
516     let cx: @ext_ctxt = mk_ctxt(parse_sess, copy cfg);
517     let f_pre = @AstFoldFns {
518         fold_expr: |expr,span,recur|
519             expand_expr(extsbox, cx, expr, span, recur, afp.fold_expr),
520         fold_mod: |modd,recur|
521             expand_mod_items(extsbox, cx, modd, recur, afp.fold_mod),
522         fold_item: |item,recur|
523             expand_item(extsbox, cx, item, recur, afp.fold_item),
524         fold_stmt: |stmt,span,recur|
525             expand_stmt(extsbox, cx, stmt, span, recur, afp.fold_stmt),
526         fold_block: |blk,span,recur|
527             expand_block(extsbox, cx, blk, span, recur, afp.fold_block),
528         new_span: |a| new_span(cx, a),
529         .. *afp};
530     let f = make_fold(f_pre);
531     // add a bunch of macros as though they were placed at the
532     // head of the program (ick).
533     let attrs = ~[
534         spanned {
535             span: codemap::dummy_sp(),
536             node: attribute_ {
537                 style: attr_outer,
538                 value: @spanned {
539                     node: meta_word(@~"macro_escape"),
540                     span: codemap::dummy_sp(),
541                 },
542                 is_sugared_doc: false,
543             }
544         }
545     ];
546
547     let cm = match parse_item_from_source_str(~"<core-macros>",
548                                               @core_macros(),
549                                               copy cfg,
550                                               attrs,
551                                               parse_sess) {
552         Some(item) => item,
553         None => cx.bug(~"expected core macros to parse correctly")
554     };
555     // This is run for its side-effects on the expander env,
556     // as it registers all the core macros as expanders.
557     f.fold_item(cm);
558
559     @f.fold_crate(&*c)
560 }
561
562 // given a function from paths to paths, produce
563 // an ast_fold that applies that function:
564 fn fun_to_path_folder(f: @fn(&ast::Path)->ast::Path) -> @ast_fold{
565     let afp = default_ast_fold();
566     let f_pre = @AstFoldFns{
567         fold_path : |p, _| f(p),
568         .. *afp
569     };
570     make_fold(f_pre)
571 }
572 /* going to have to figure out whether the table is passed in or
573 extracted from TLS...
574 // update the ctxts in a path to get a rename node
575 fn ctxt_update_rename(from: ast::Name,
576                        fromctx: ast::SyntaxContext, to: ast::Name) ->
577     @fn(&ast::Path,@ast_fold)->ast::Path {
578     return |p:&ast::Path,_|
579     ast::Path {span: p.span,
580                global: p.global,
581                idents: p.idents.map(|id|
582                                     ast::ident{
583                                         repr: id.repr,
584                                         // this needs to be cached....
585                                         ctxt: Some(@ast::Rename(from,fromctx,
586                                                            to,id.ctxt))
587                                     }),
588                rp: p.rp,
589                types: p.types};
590 }
591
592 // update the ctxts in a path to get a mark node
593 fn ctxt_update_mark(mark: uint) ->
594     @fn(&ast::Path,@ast_fold)->ast::Path {
595     return |p:&ast::Path,_|
596     ast::Path {span: p.span,
597                global: p.global,
598                idents: p.idents.map(|id|
599                                     ast::ident{
600                                         repr: id.repr,
601                                         // this needs to be cached....
602                                         ctxt: Some(@ast::Mark(mark,id.ctxt))
603                                     }),
604                rp: p.rp,
605                types: p.types};
606 }
607 */
608
609 #[cfg(test)]
610 mod test {
611     use super::*;
612     use ast;
613     use ast::{attribute_, attr_outer, meta_word};
614     use codemap;
615     use codemap::spanned;
616     use parse;
617     use core::option::{None, Some};
618
619     // make sure that fail! is present
620     #[test] fn fail_exists_test () {
621         let src = ~"fn main() { fail!(~\"something appropriately gloomy\");}";
622         let sess = parse::new_parse_sess(None);
623         let cfg = ~[];
624         let crate_ast = parse::parse_crate_from_source_str(
625             ~"<test>",
626             @src,
627             cfg,sess);
628         expand_crate(sess,cfg,crate_ast);
629     }
630
631     // these following tests are quite fragile, in that they don't test what
632     // *kind* of failure occurs.
633
634     // make sure that macros can leave scope
635     #[should_fail]
636     #[test] fn macros_cant_escape_fns_test () {
637         let src = ~"fn bogus() {macro_rules! z (() => (3+4))}\
638                     fn inty() -> int { z!() }";
639         let sess = parse::new_parse_sess(None);
640         let cfg = ~[];
641         let crate_ast = parse::parse_crate_from_source_str(
642             ~"<test>",
643             @src,
644             cfg,sess);
645         // should fail:
646         expand_crate(sess,cfg,crate_ast);
647     }
648
649     // make sure that macros can leave scope for modules
650     #[should_fail]
651     #[test] fn macros_cant_escape_mods_test () {
652         let src = ~"mod foo {macro_rules! z (() => (3+4))}\
653                     fn inty() -> int { z!() }";
654         let sess = parse::new_parse_sess(None);
655         let cfg = ~[];
656         let crate_ast = parse::parse_crate_from_source_str(
657             ~"<test>",
658             @src,
659             cfg,sess);
660         // should fail:
661         expand_crate(sess,cfg,crate_ast);
662     }
663
664     // macro_escape modules shouldn't cause macros to leave scope
665     #[test] fn macros_can_escape_flattened_mods_test () {
666         let src = ~"#[macro_escape] mod foo {macro_rules! z (() => (3+4))}\
667                     fn inty() -> int { z!() }";
668         let sess = parse::new_parse_sess(None);
669         let cfg = ~[];
670         let crate_ast = parse::parse_crate_from_source_str(
671             ~"<test>",
672             @src,
673             cfg,sess);
674         // should fail:
675         expand_crate(sess,cfg,crate_ast);
676     }
677
678     #[test] fn core_macros_must_parse () {
679         let src = ~"
680   pub mod macros {
681     macro_rules! ignore (($($x:tt)*) => (()))
682
683     macro_rules! error ( ($( $arg:expr ),+) => (
684         log(::core::error, fmt!( $($arg),+ )) ))
685 }";
686         let sess = parse::new_parse_sess(None);
687         let cfg = ~[];
688         let item_ast = parse::parse_item_from_source_str(
689             ~"<test>",
690             @src,
691             cfg,~[make_dummy_attr (@~"macro_escape")],sess);
692         match item_ast {
693             Some(_) => (), // success
694             None => fail!(~"expected this to parse")
695         }
696     }
697
698     #[test] fn test_contains_flatten (){
699         let attr1 = make_dummy_attr (@~"foo");
700         let attr2 = make_dummy_attr (@~"bar");
701         let escape_attr = make_dummy_attr (@~"macro_escape");
702         let attrs1 = ~[attr1, escape_attr, attr2];
703         assert_eq!(contains_macro_escape (attrs1),true);
704         let attrs2 = ~[attr1,attr2];
705         assert_eq!(contains_macro_escape (attrs2),false);
706     }
707
708     // make a "meta_word" outer attribute with the given name
709     fn make_dummy_attr(s: @~str) -> ast::attribute {
710         spanned {
711             span:codemap::dummy_sp(),
712             node: attribute_ {
713                 style: attr_outer,
714                 value: @spanned {
715                     node: meta_word(s),
716                     span: codemap::dummy_sp(),
717                 },
718                 is_sugared_doc: false,
719             }
720         }
721     }
722
723 }