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