]> git.lizzy.rs Git - rust.git/blob - src/libsyntax/ext/expand.rs
Partial rewrite/expansion of `Vec::truncate` documentation.
[rust.git] / src / libsyntax / ext / expand.rs
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.
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::{Block, Crate, Ident, Mac_, Name, PatKind};
12 use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
13 use ast;
14 use attr::HasAttrs;
15 use ext::mtwt;
16 use attr;
17 use attr::AttrMetaMethods;
18 use codemap::{dummy_spanned, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
19 use syntax_pos::{self, Span, ExpnId};
20 use config::StripUnconfigured;
21 use ext::base::*;
22 use feature_gate::{self, Features};
23 use fold;
24 use fold::*;
25 use parse::token::{fresh_mark, intern, keywords};
26 use ptr::P;
27 use tokenstream::TokenTree;
28 use util::small_vector::SmallVector;
29 use visit;
30 use visit::Visitor;
31 use std_inject;
32
33 use std::collections::HashSet;
34
35 // A trait for AST nodes and AST node lists into which macro invocations may expand.
36 trait MacroGenerable: Sized {
37     // Expand the given MacResult using its appropriate `make_*` method.
38     fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self>;
39
40     // Fold this node or list of nodes using the given folder.
41     fn fold_with<F: Folder>(self, folder: &mut F) -> Self;
42     fn visit_with<V: Visitor>(&self, visitor: &mut V);
43
44     // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics.
45     fn kind_name() -> &'static str;
46
47     // Return a placeholder expansion to allow compilation to continue after an erroring expansion.
48     fn dummy(span: Span) -> Self {
49         Self::make_with(DummyResult::any(span)).unwrap()
50     }
51 }
52
53 macro_rules! impl_macro_generable {
54     ($($ty:ty: $kind_name:expr, .$make:ident,
55                $(.$fold:ident)*  $(lift .$fold_elt:ident)*,
56                $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { $(
57         impl MacroGenerable for $ty {
58             fn kind_name() -> &'static str { $kind_name }
59             fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> { result.$make() }
60             fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
61                 $( folder.$fold(self) )*
62                 $( self.into_iter().flat_map(|item| folder. $fold_elt (item)).collect() )*
63             }
64             fn visit_with<V: Visitor>(&self, visitor: &mut V) {
65                 $( visitor.$visit(self) )*
66                 $( for item in self.as_slice() { visitor. $visit_elt (item) } )*
67             }
68         }
69     )* }
70 }
71
72 impl_macro_generable! {
73     P<ast::Expr>: "expression", .make_expr, .fold_expr, .visit_expr;
74     P<ast::Pat>:  "pattern",    .make_pat,  .fold_pat,  .visit_pat;
75     P<ast::Ty>:   "type",       .make_ty,   .fold_ty,   .visit_ty;
76     SmallVector<ast::Stmt>: "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt;
77     SmallVector<P<ast::Item>>: "item",   .make_items, lift .fold_item, lift .visit_item;
78     SmallVector<ast::TraitItem>:
79         "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item;
80     SmallVector<ast::ImplItem>:
81         "impl item",  .make_impl_items,  lift .fold_impl_item,  lift .visit_impl_item;
82 }
83
84 impl MacroGenerable for Option<P<ast::Expr>> {
85     fn kind_name() -> &'static str { "expression" }
86     fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> {
87         result.make_expr().map(Some)
88     }
89     fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
90         self.and_then(|expr| folder.fold_opt_expr(expr))
91     }
92     fn visit_with<V: Visitor>(&self, visitor: &mut V) {
93         self.as_ref().map(|expr| visitor.visit_expr(expr));
94     }
95 }
96
97 pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
98     match expr.node {
99         // expr_mac should really be expr_ext or something; it's the
100         // entry-point for all syntax extensions.
101         ast::ExprKind::Mac(mac) => {
102             return expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, fld);
103         }
104         _ => P(noop_fold_expr(expr, fld)),
105     }
106 }
107
108 struct MacroScopePlaceholder;
109 impl MacResult for MacroScopePlaceholder {
110     fn make_items(self: Box<Self>) -> Option<SmallVector<P<ast::Item>>> {
111         Some(SmallVector::one(P(ast::Item {
112             ident: keywords::Invalid.ident(),
113             attrs: Vec::new(),
114             id: ast::DUMMY_NODE_ID,
115             node: ast::ItemKind::Mac(dummy_spanned(ast::Mac_ {
116                 path: ast::Path { span: syntax_pos::DUMMY_SP, global: false, segments: Vec::new() },
117                 tts: Vec::new(),
118             })),
119             vis: ast::Visibility::Inherited,
120             span: syntax_pos::DUMMY_SP,
121         })))
122     }
123 }
124
125 /// Expand a macro invocation. Returns the result of expansion.
126 fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attribute>, span: Span,
127                        fld: &mut MacroExpander) -> T
128     where T: MacroGenerable,
129 {
130     // It would almost certainly be cleaner to pass the whole macro invocation in,
131     // rather than pulling it apart and marking the tts and the ctxt separately.
132     let Mac_ { path, tts, .. } = mac.node;
133     let mark = fresh_mark();
134
135     fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
136                       attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
137                       -> Option<Box<MacResult + 'a>> {
138         // Detect use of feature-gated or invalid attributes on macro invoations
139         // since they will not be detected after macro expansion.
140         for attr in attrs.iter() {
141             feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic,
142                                           &fld.cx.parse_sess.codemap(),
143                                           &fld.cx.ecfg.features.unwrap());
144         }
145
146         if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
147             fld.cx.span_err(path.span, "expected macro name without module separators");
148             return None;
149         }
150
151         let extname = path.segments[0].identifier.name;
152         let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) {
153             extension
154         } else {
155             let mut err = fld.cx.struct_span_err(path.span,
156                                                  &format!("macro undefined: '{}!'", &extname));
157             fld.cx.suggest_macro_name(&extname.as_str(), &mut err);
158             err.emit();
159             return None;
160         };
161
162         let ident = ident.unwrap_or(keywords::Invalid.ident());
163         let marked_tts = mark_tts(&tts, mark);
164         match *extension {
165             NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
166                 if ident.name != keywords::Invalid.name() {
167                     let msg =
168                         format!("macro {}! expects no ident argument, given '{}'", extname, ident);
169                     fld.cx.span_err(path.span, &msg);
170                     return None;
171                 }
172
173                 fld.cx.bt_push(ExpnInfo {
174                     call_site: call_site,
175                     callee: NameAndSpan {
176                         format: MacroBang(extname),
177                         span: exp_span,
178                         allow_internal_unstable: allow_internal_unstable,
179                     },
180                 });
181
182                 Some(expandfun.expand(fld.cx, call_site, &marked_tts))
183             }
184
185             IdentTT(ref expander, tt_span, allow_internal_unstable) => {
186                 if ident.name == keywords::Invalid.name() {
187                     fld.cx.span_err(path.span,
188                                     &format!("macro {}! expects an ident argument", extname));
189                     return None;
190                 };
191
192                 fld.cx.bt_push(ExpnInfo {
193                     call_site: call_site,
194                     callee: NameAndSpan {
195                         format: MacroBang(extname),
196                         span: tt_span,
197                         allow_internal_unstable: allow_internal_unstable,
198                     }
199                 });
200
201                 Some(expander.expand(fld.cx, call_site, ident, marked_tts))
202             }
203
204             MacroRulesTT => {
205                 if ident.name == keywords::Invalid.name() {
206                     fld.cx.span_err(path.span,
207                                     &format!("macro {}! expects an ident argument", extname));
208                     return None;
209                 };
210
211                 fld.cx.bt_push(ExpnInfo {
212                     call_site: call_site,
213                     callee: NameAndSpan {
214                         format: MacroBang(extname),
215                         span: None,
216                         // `macro_rules!` doesn't directly allow unstable
217                         // (this is orthogonal to whether the macro it creates allows it)
218                         allow_internal_unstable: false,
219                     }
220                 });
221
222                 // DON'T mark before expansion.
223                 fld.cx.insert_macro(ast::MacroDef {
224                     ident: ident,
225                     id: ast::DUMMY_NODE_ID,
226                     span: call_site,
227                     imported_from: None,
228                     use_locally: true,
229                     body: marked_tts,
230                     export: attr::contains_name(&attrs, "macro_export"),
231                     allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
232                     attrs: attrs,
233                 });
234
235                 // macro_rules! has a side effect but expands to nothing.
236                 Some(Box::new(MacroScopePlaceholder))
237             }
238
239             MultiDecorator(..) | MultiModifier(..) => {
240                 fld.cx.span_err(path.span,
241                                 &format!("`{}` can only be used in attributes", extname));
242                 None
243             }
244         }
245     }
246
247     let opt_expanded = T::make_with(match mac_result(&path, ident, tts, mark, attrs, span, fld) {
248         Some(result) => result,
249         None => return T::dummy(span),
250     });
251
252     let expanded = if let Some(expanded) = opt_expanded {
253         expanded
254     } else {
255         let msg = format!("non-{kind} macro in {kind} position: {name}",
256                           name = path.segments[0].identifier.name, kind = T::kind_name());
257         fld.cx.span_err(path.span, &msg);
258         return T::dummy(span);
259     };
260
261     let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) });
262     let configured = marked.fold_with(&mut fld.strip_unconfigured());
263     fld.load_macros(&configured);
264     let fully_expanded = configured.fold_with(fld);
265     fld.cx.bt_pop();
266     fully_expanded
267 }
268
269 // eval $e with a new exts frame.
270 // must be a macro so that $e isn't evaluated too early.
271 macro_rules! with_exts_frame {
272     ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
273     ({$extsboxexpr.push_frame();
274       $extsboxexpr.info().macros_escape = $macros_escape;
275       let result = $e;
276       $extsboxexpr.pop_frame();
277       result
278      })
279 }
280
281 // When we enter a module, record it, for the sake of `module!`
282 pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
283                    -> SmallVector<P<ast::Item>> {
284     expand_annotatable(Annotatable::Item(it), fld)
285         .into_iter().map(|i| i.expect_item()).collect()
286 }
287
288 // does this attribute list contain "macro_use" ?
289 fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
290     for attr in attrs {
291         let mut is_use = attr.check_name("macro_use");
292         if attr.check_name("macro_escape") {
293             let mut err =
294                 fld.cx.struct_span_warn(attr.span,
295                                         "macro_escape is a deprecated synonym for macro_use");
296             is_use = true;
297             if let ast::AttrStyle::Inner = attr.node.style {
298                 err.help("consider an outer attribute, \
299                           #[macro_use] mod ...").emit();
300             } else {
301                 err.emit();
302             }
303         };
304
305         if is_use {
306             match attr.node.value.node {
307                 ast::MetaItemKind::Word(..) => (),
308                 _ => fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"),
309             }
310             return true;
311         }
312     }
313     false
314 }
315
316 /// Expand a stmt
317 fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
318     let (mac, style, attrs) = match stmt.node {
319         StmtKind::Mac(mac) => mac.unwrap(),
320         _ => return noop_fold_stmt(stmt, fld)
321     };
322
323     let mut fully_expanded: SmallVector<ast::Stmt> =
324         expand_mac_invoc(mac, None, attrs.into(), stmt.span, fld);
325
326     // If this is a macro invocation with a semicolon, then apply that
327     // semicolon to the final statement produced by expansion.
328     if style == MacStmtStyle::Semicolon {
329         if let Some(stmt) = fully_expanded.pop() {
330             fully_expanded.push(stmt.add_trailing_semicolon());
331         }
332     }
333
334     fully_expanded
335 }
336
337 fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
338     match p.node {
339         PatKind::Mac(_) => {}
340         _ => return noop_fold_pat(p, fld)
341     }
342     p.and_then(|ast::Pat {node, span, ..}| {
343         match node {
344             PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld),
345             _ => unreachable!()
346         }
347     })
348 }
349
350 fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
351     match a {
352         Annotatable::Item(it) => match it.node {
353             ast::ItemKind::Mac(..) => {
354                 if match it.node {
355                     ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(),
356                     _ => unreachable!(),
357                 } {
358                     return SmallVector::one(Annotatable::Item(it));
359                 }
360                 it.and_then(|it| match it.node {
361                     ItemKind::Mac(mac) =>
362                         expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld),
363                     _ => unreachable!(),
364                 })
365             }
366             ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => {
367                 let valid_ident =
368                     it.ident.name != keywords::Invalid.name();
369
370                 if valid_ident {
371                     fld.cx.mod_push(it.ident);
372                 }
373                 let macro_use = contains_macro_use(fld, &it.attrs);
374                 let result = with_exts_frame!(fld.cx.syntax_env,
375                                               macro_use,
376                                               noop_fold_item(it, fld));
377                 if valid_ident {
378                     fld.cx.mod_pop();
379                 }
380                 result
381             },
382             _ => noop_fold_item(it, fld),
383         }.into_iter().map(|i| Annotatable::Item(i)).collect(),
384
385         Annotatable::TraitItem(it) => {
386             expand_trait_item(it.unwrap(), fld).into_iter().
387                 map(|it| Annotatable::TraitItem(P(it))).collect()
388         }
389
390         Annotatable::ImplItem(ii) => {
391             expand_impl_item(ii.unwrap(), fld).into_iter().
392                 map(|ii| Annotatable::ImplItem(P(ii))).collect()
393         }
394     }
395 }
396
397 fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
398     let mut multi_modifier = None;
399     item = item.map_attrs(|mut attrs| {
400         for i in 0..attrs.len() {
401             if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
402                 match *extension {
403                     MultiModifier(..) | MultiDecorator(..) => {
404                         multi_modifier = Some((attrs.remove(i), extension));
405                         break;
406                     }
407                     _ => {}
408                 }
409             }
410         }
411         attrs
412     });
413
414     match multi_modifier {
415         None => expand_multi_modified(item, fld),
416         Some((attr, extension)) => {
417             attr::mark_used(&attr);
418             fld.cx.bt_push(ExpnInfo {
419                 call_site: attr.span,
420                 callee: NameAndSpan {
421                     format: MacroAttribute(intern(&attr.name())),
422                     span: Some(attr.span),
423                     // attributes can do whatever they like, for now
424                     allow_internal_unstable: true,
425                 }
426             });
427
428             let modified = match *extension {
429                 MultiModifier(ref mac) => mac.expand(fld.cx, attr.span, &attr.node.value, item),
430                 MultiDecorator(ref mac) => {
431                     let mut items = Vec::new();
432                     mac.expand(fld.cx, attr.span, &attr.node.value, &item,
433                                &mut |item| items.push(item));
434                     items.push(item);
435                     items
436                 }
437                 _ => unreachable!(),
438             };
439
440             fld.cx.bt_pop();
441             let configured = modified.into_iter().flat_map(|it| {
442                 it.fold_with(&mut fld.strip_unconfigured())
443             }).collect::<SmallVector<_>>();
444
445             configured.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
446         }
447     }
448 }
449
450 fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
451                  -> SmallVector<ast::ImplItem> {
452     match ii.node {
453         ast::ImplItemKind::Macro(mac) => {
454             expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
455         }
456         _ => fold::noop_fold_impl_item(ii, fld)
457     }
458 }
459
460 fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
461                      -> SmallVector<ast::TraitItem> {
462     match ti.node {
463         ast::TraitItemKind::Macro(mac) => {
464             expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
465         }
466         _ => fold::noop_fold_trait_item(ti, fld)
467     }
468 }
469
470 pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
471     let t = match t.node.clone() {
472         ast::TyKind::Mac(mac) => {
473             if fld.cx.ecfg.features.unwrap().type_macros {
474                 expand_mac_invoc(mac, None, Vec::new(), t.span, fld)
475             } else {
476                 feature_gate::emit_feature_err(
477                     &fld.cx.parse_sess.span_diagnostic,
478                     "type_macros",
479                     t.span,
480                     feature_gate::GateIssue::Language,
481                     "type macros are experimental");
482
483                 DummyResult::raw_ty(t.span)
484             }
485         }
486         _ => t
487     };
488
489     fold::noop_fold_ty(t, fld)
490 }
491
492 /// A tree-folder that performs macro expansion
493 pub struct MacroExpander<'a, 'b:'a> {
494     pub cx: &'a mut ExtCtxt<'b>,
495 }
496
497 impl<'a, 'b> MacroExpander<'a, 'b> {
498     pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
499         MacroExpander { cx: cx }
500     }
501
502     fn strip_unconfigured(&mut self) -> StripUnconfigured {
503         StripUnconfigured {
504             config: &self.cx.cfg,
505             should_test: self.cx.ecfg.should_test,
506             sess: self.cx.parse_sess,
507             features: self.cx.ecfg.features,
508         }
509     }
510
511     fn load_macros<T: MacroGenerable>(&mut self, node: &T) {
512         struct MacroLoadingVisitor<'a, 'b: 'a>{
513             cx: &'a mut ExtCtxt<'b>,
514             at_crate_root: bool,
515         }
516
517         impl<'a, 'b> Visitor for MacroLoadingVisitor<'a, 'b> {
518             fn visit_mac(&mut self, _: &ast::Mac) {}
519             fn visit_item(&mut self, item: &ast::Item) {
520                 if let ast::ItemKind::ExternCrate(..) = item.node {
521                     // We need to error on `#[macro_use] extern crate` when it isn't at the
522                     // crate root, because `$crate` won't work properly.
523                     for def in self.cx.loader.load_crate(item, self.at_crate_root) {
524                         self.cx.insert_macro(def);
525                     }
526                 } else {
527                     let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
528                     visit::walk_item(self, item);
529                     self.at_crate_root = at_crate_root;
530                 }
531             }
532             fn visit_block(&mut self, block: &ast::Block) {
533                 let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
534                 visit::walk_block(self, block);
535                 self.at_crate_root = at_crate_root;
536             }
537         }
538
539         node.visit_with(&mut MacroLoadingVisitor {
540             at_crate_root: self.cx.syntax_env.is_crate_root(),
541             cx: self.cx,
542         });
543     }
544 }
545
546 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
547     fn fold_crate(&mut self, c: Crate) -> Crate {
548         self.cx.filename = Some(self.cx.parse_sess.codemap().span_to_filename(c.span));
549         noop_fold_crate(c, self)
550     }
551
552     fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
553         expr.and_then(|expr| expand_expr(expr, self))
554     }
555
556     fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
557         expr.and_then(|expr| match expr.node {
558             ast::ExprKind::Mac(mac) =>
559                 expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, self),
560             _ => Some(expand_expr(expr, self)),
561         })
562     }
563
564     fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
565         expand_pat(pat, self)
566     }
567
568     fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
569         use std::mem::replace;
570         let result;
571         if let ast::ItemKind::Mod(ast::Mod { inner, .. }) = item.node {
572             if item.span.contains(inner) {
573                 self.push_mod_path(item.ident, &item.attrs);
574                 result = expand_item(item, self);
575                 self.pop_mod_path();
576             } else {
577                 let filename = if inner != syntax_pos::DUMMY_SP {
578                     Some(self.cx.parse_sess.codemap().span_to_filename(inner))
579                 } else { None };
580                 let orig_filename = replace(&mut self.cx.filename, filename);
581                 let orig_mod_path_stack = replace(&mut self.cx.mod_path_stack, Vec::new());
582                 result = expand_item(item, self);
583                 self.cx.filename = orig_filename;
584                 self.cx.mod_path_stack = orig_mod_path_stack;
585             }
586         } else {
587             result = expand_item(item, self);
588         }
589         result
590     }
591
592     fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
593         expand_stmt(stmt, self)
594     }
595
596     fn fold_block(&mut self, block: P<Block>) -> P<Block> {
597         let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true);
598         let result = with_exts_frame!(self.cx.syntax_env, false, noop_fold_block(block, self));
599         self.cx.in_block = was_in_block;
600         result
601     }
602
603     fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
604         expand_annotatable(Annotatable::TraitItem(P(i)), self)
605             .into_iter().map(|i| i.expect_trait_item()).collect()
606     }
607
608     fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
609         expand_annotatable(Annotatable::ImplItem(P(i)), self)
610             .into_iter().map(|i| i.expect_impl_item()).collect()
611     }
612
613     fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
614         expand_type(ty, self)
615     }
616 }
617
618 impl<'a, 'b> MacroExpander<'a, 'b> {
619     fn push_mod_path(&mut self, id: Ident, attrs: &[ast::Attribute]) {
620         let default_path = id.name.as_str();
621         let file_path = match ::attr::first_attr_value_str_by_name(attrs, "path") {
622             Some(d) => d,
623             None => default_path,
624         };
625         self.cx.mod_path_stack.push(file_path)
626     }
627
628     fn pop_mod_path(&mut self) {
629         self.cx.mod_path_stack.pop().unwrap();
630     }
631 }
632
633 pub struct ExpansionConfig<'feat> {
634     pub crate_name: String,
635     pub features: Option<&'feat Features>,
636     pub recursion_limit: usize,
637     pub trace_mac: bool,
638     pub should_test: bool, // If false, strip `#[test]` nodes
639 }
640
641 macro_rules! feature_tests {
642     ($( fn $getter:ident = $field:ident, )*) => {
643         $(
644             pub fn $getter(&self) -> bool {
645                 match self.features {
646                     Some(&Features { $field: true, .. }) => true,
647                     _ => false,
648                 }
649             }
650         )*
651     }
652 }
653
654 impl<'feat> ExpansionConfig<'feat> {
655     pub fn default(crate_name: String) -> ExpansionConfig<'static> {
656         ExpansionConfig {
657             crate_name: crate_name,
658             features: None,
659             recursion_limit: 64,
660             trace_mac: false,
661             should_test: false,
662         }
663     }
664
665     feature_tests! {
666         fn enable_quotes = quote,
667         fn enable_asm = asm,
668         fn enable_log_syntax = log_syntax,
669         fn enable_concat_idents = concat_idents,
670         fn enable_trace_macros = trace_macros,
671         fn enable_allow_internal_unstable = allow_internal_unstable,
672         fn enable_custom_derive = custom_derive,
673         fn enable_pushpop_unsafe = pushpop_unsafe,
674     }
675 }
676
677 pub fn expand_crate(mut cx: ExtCtxt,
678                     user_exts: Vec<NamedSyntaxExtension>,
679                     mut c: Crate) -> (Crate, HashSet<Name>) {
680     if std_inject::no_core(&c) {
681         cx.crate_root = None;
682     } else if std_inject::no_std(&c) {
683         cx.crate_root = Some("core");
684     } else {
685         cx.crate_root = Some("std");
686     }
687     let ret = {
688         let mut expander = MacroExpander::new(&mut cx);
689
690         for (name, extension) in user_exts {
691             expander.cx.syntax_env.insert(name, extension);
692         }
693
694         let items = SmallVector::many(c.module.items);
695         expander.load_macros(&items);
696         c.module.items = items.into();
697
698         let err_count = cx.parse_sess.span_diagnostic.err_count();
699         let mut ret = expander.fold_crate(c);
700         ret.exported_macros = expander.cx.exported_macros.clone();
701
702         if cx.parse_sess.span_diagnostic.err_count() > err_count {
703             cx.parse_sess.span_diagnostic.abort_if_errors();
704         }
705
706         ret
707     };
708     return (ret, cx.syntax_env.names);
709 }
710
711 // HYGIENIC CONTEXT EXTENSION:
712 // all of these functions are for walking over
713 // ASTs and making some change to the context of every
714 // element that has one. a CtxtFn is a trait-ified
715 // version of a closure in (SyntaxContext -> SyntaxContext).
716 // the ones defined here include:
717 // Marker - add a mark to a context
718
719 // A Marker adds the given mark to the syntax context and
720 // sets spans' `expn_id` to the given expn_id (unless it is `None`).
721 struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
722
723 impl Folder for Marker {
724     fn fold_ident(&mut self, id: Ident) -> Ident {
725         ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt))
726     }
727     fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
728         Spanned {
729             node: Mac_ {
730                 path: self.fold_path(node.path),
731                 tts: self.fold_tts(&node.tts),
732             },
733             span: self.new_span(span),
734         }
735     }
736
737     fn new_span(&mut self, mut span: Span) -> Span {
738         if let Some(expn_id) = self.expn_id {
739             span.expn_id = expn_id;
740         }
741         span
742     }
743 }
744
745 // apply a given mark to the given token trees. Used prior to expansion of a macro.
746 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
747     noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
748 }
749
750
751 #[cfg(test)]
752 mod tests {
753     use super::{expand_crate, ExpansionConfig};
754     use ast;
755     use ext::base::{ExtCtxt, DummyMacroLoader};
756     use parse;
757     use util::parser_testing::{string_to_parser};
758     use visit;
759     use visit::Visitor;
760
761     // a visitor that extracts the paths
762     // from a given thingy and puts them in a mutable
763     // array (passed in to the traversal)
764     #[derive(Clone)]
765     struct PathExprFinderContext {
766         path_accumulator: Vec<ast::Path> ,
767     }
768
769     impl Visitor for PathExprFinderContext {
770         fn visit_expr(&mut self, expr: &ast::Expr) {
771             if let ast::ExprKind::Path(None, ref p) = expr.node {
772                 self.path_accumulator.push(p.clone());
773             }
774             visit::walk_expr(self, expr);
775         }
776     }
777
778     // these following tests are quite fragile, in that they don't test what
779     // *kind* of failure occurs.
780
781     fn test_ecfg() -> ExpansionConfig<'static> {
782         ExpansionConfig::default("test".to_string())
783     }
784
785     // make sure that macros can't escape fns
786     #[should_panic]
787     #[test] fn macros_cant_escape_fns_test () {
788         let src = "fn bogus() {macro_rules! z (() => (3+4));}\
789                    fn inty() -> i32 { z!() }".to_string();
790         let sess = parse::ParseSess::new();
791         let crate_ast = parse::parse_crate_from_source_str(
792             "<test>".to_string(),
793             src,
794             Vec::new(), &sess).unwrap();
795         // should fail:
796         let mut loader = DummyMacroLoader;
797         let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
798         expand_crate(ecx, vec![], crate_ast);
799     }
800
801     // make sure that macros can't escape modules
802     #[should_panic]
803     #[test] fn macros_cant_escape_mods_test () {
804         let src = "mod foo {macro_rules! z (() => (3+4));}\
805                    fn inty() -> i32 { z!() }".to_string();
806         let sess = parse::ParseSess::new();
807         let crate_ast = parse::parse_crate_from_source_str(
808             "<test>".to_string(),
809             src,
810             Vec::new(), &sess).unwrap();
811         let mut loader = DummyMacroLoader;
812         let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
813         expand_crate(ecx, vec![], crate_ast);
814     }
815
816     // macro_use modules should allow macros to escape
817     #[test] fn macros_can_escape_flattened_mods_test () {
818         let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
819                    fn inty() -> i32 { z!() }".to_string();
820         let sess = parse::ParseSess::new();
821         let crate_ast = parse::parse_crate_from_source_str(
822             "<test>".to_string(),
823             src,
824             Vec::new(), &sess).unwrap();
825         let mut loader = DummyMacroLoader;
826         let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
827         expand_crate(ecx, vec![], crate_ast);
828     }
829
830     fn expand_crate_str(crate_str: String) -> ast::Crate {
831         let ps = parse::ParseSess::new();
832         let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
833         // the cfg argument actually does matter, here...
834         let mut loader = DummyMacroLoader;
835         let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
836         expand_crate(ecx, vec![], crate_ast).0
837     }
838
839     #[test] fn macro_tokens_should_match(){
840         expand_crate_str(
841             "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
842     }
843
844     // should be able to use a bound identifier as a literal in a macro definition:
845     #[test] fn self_macro_parsing(){
846         expand_crate_str(
847             "macro_rules! foo ((zz) => (287;));
848             fn f(zz: i32) {foo!(zz);}".to_string()
849             );
850     }
851
852     // create a really evil test case where a $x appears inside a binding of $x
853     // but *shouldn't* bind because it was inserted by a different macro....
854     // can't write this test case until we have macro-generating macros.
855 }