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