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.
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
11 use ast::{Block, Crate, Ident, Mac_, Name, PatKind};
12 use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
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;
22 use feature_gate::{self, Features};
25 use parse::token::{fresh_mark, intern, keywords};
27 use tokenstream::TokenTree;
28 use util::small_vector::SmallVector;
33 use std::collections::HashSet;
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>;
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);
44 // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics.
45 fn kind_name() -> &'static str;
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()
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() )*
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) } )*
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;
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)
89 fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
90 self.and_then(|expr| folder.fold_opt_expr(expr))
92 fn visit_with<V: Visitor>(&self, visitor: &mut V) {
93 self.as_ref().map(|expr| visitor.visit_expr(expr));
97 pub fn expand_expr(expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
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);
104 _ => P(noop_fold_expr(expr, fld)),
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(),
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() },
119 vis: ast::Visibility::Inherited,
120 span: syntax_pos::DUMMY_SP,
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,
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();
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());
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");
151 let extname = path.segments[0].identifier.name;
152 let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) {
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);
162 let ident = ident.unwrap_or(keywords::Invalid.ident());
163 let marked_tts = mark_tts(&tts, mark);
165 NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
166 if ident.name != keywords::Invalid.name() {
168 format!("macro {}! expects no ident argument, given '{}'", extname, ident);
169 fld.cx.span_err(path.span, &msg);
173 fld.cx.bt_push(ExpnInfo {
174 call_site: call_site,
175 callee: NameAndSpan {
176 format: MacroBang(extname),
178 allow_internal_unstable: allow_internal_unstable,
182 Some(expandfun.expand(fld.cx, call_site, &marked_tts))
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));
192 fld.cx.bt_push(ExpnInfo {
193 call_site: call_site,
194 callee: NameAndSpan {
195 format: MacroBang(extname),
197 allow_internal_unstable: allow_internal_unstable,
201 Some(expander.expand(fld.cx, call_site, ident, marked_tts))
205 if ident.name == keywords::Invalid.name() {
206 fld.cx.span_err(path.span,
207 &format!("macro {}! expects an ident argument", extname));
211 fld.cx.bt_push(ExpnInfo {
212 call_site: call_site,
213 callee: NameAndSpan {
214 format: MacroBang(extname),
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,
222 // DON'T mark before expansion.
223 fld.cx.insert_macro(ast::MacroDef {
225 id: ast::DUMMY_NODE_ID,
230 export: attr::contains_name(&attrs, "macro_export"),
231 allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
235 // macro_rules! has a side effect but expands to nothing.
236 Some(Box::new(MacroScopePlaceholder))
239 MultiDecorator(..) | MultiModifier(..) => {
240 fld.cx.span_err(path.span,
241 &format!("`{}` can only be used in attributes", extname));
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),
252 let expanded = if let Some(expanded) = opt_expanded {
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);
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);
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;
276 $extsboxexpr.pop_frame();
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()
288 // does this attribute list contain "macro_use" ?
289 fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
291 let mut is_use = attr.check_name("macro_use");
292 if attr.check_name("macro_escape") {
294 fld.cx.struct_span_warn(attr.span,
295 "macro_escape is a deprecated synonym for macro_use");
297 if let ast::AttrStyle::Inner = attr.node.style {
298 err.help("consider an outer attribute, \
299 #[macro_use] mod ...").emit();
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"),
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)
323 let mut fully_expanded: SmallVector<ast::Stmt> =
324 expand_mac_invoc(mac, None, attrs.into(), stmt.span, fld);
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());
337 fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
339 PatKind::Mac(_) => {}
340 _ => return noop_fold_pat(p, fld)
342 p.and_then(|ast::Pat {node, span, ..}| {
344 PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld),
350 fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
352 Annotatable::Item(it) => match it.node {
353 ast::ItemKind::Mac(..) => {
355 ItemKind::Mac(ref mac) => mac.node.path.segments.is_empty(),
358 return SmallVector::one(Annotatable::Item(it));
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),
366 ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => {
368 it.ident.name != keywords::Invalid.name();
371 fld.cx.mod_push(it.ident);
373 let macro_use = contains_macro_use(fld, &it.attrs);
374 let result = with_exts_frame!(fld.cx.syntax_env,
376 noop_fold_item(it, fld));
382 _ => noop_fold_item(it, fld),
383 }.into_iter().map(|i| Annotatable::Item(i)).collect(),
385 Annotatable::TraitItem(it) => {
386 expand_trait_item(it.unwrap(), fld).into_iter().
387 map(|it| Annotatable::TraitItem(P(it))).collect()
390 Annotatable::ImplItem(ii) => {
391 expand_impl_item(ii.unwrap(), fld).into_iter().
392 map(|ii| Annotatable::ImplItem(P(ii))).collect()
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())) {
403 MultiModifier(..) | MultiDecorator(..) => {
404 multi_modifier = Some((attrs.remove(i), extension));
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,
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));
441 let configured = modified.into_iter().flat_map(|it| {
442 it.fold_with(&mut fld.strip_unconfigured())
443 }).collect::<SmallVector<_>>();
445 configured.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
450 fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
451 -> SmallVector<ast::ImplItem> {
453 ast::ImplItemKind::Macro(mac) => {
454 expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
456 _ => fold::noop_fold_impl_item(ii, fld)
460 fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
461 -> SmallVector<ast::TraitItem> {
463 ast::TraitItemKind::Macro(mac) => {
464 expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
466 _ => fold::noop_fold_trait_item(ti, fld)
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)
476 feature_gate::emit_feature_err(
477 &fld.cx.parse_sess.span_diagnostic,
480 feature_gate::GateIssue::Language,
481 "type macros are experimental");
483 DummyResult::raw_ty(t.span)
489 fold::noop_fold_ty(t, fld)
492 /// A tree-folder that performs macro expansion
493 pub struct MacroExpander<'a, 'b:'a> {
494 pub cx: &'a mut ExtCtxt<'b>,
497 impl<'a, 'b> MacroExpander<'a, 'b> {
498 pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
499 MacroExpander { cx: cx }
502 fn strip_unconfigured(&mut self) -> 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,
511 fn load_macros<T: MacroGenerable>(&mut self, node: &T) {
512 struct MacroLoadingVisitor<'a, 'b: 'a>{
513 cx: &'a mut ExtCtxt<'b>,
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);
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;
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;
539 node.visit_with(&mut MacroLoadingVisitor {
540 at_crate_root: self.cx.syntax_env.is_crate_root(),
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)
552 fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
553 expr.and_then(|expr| expand_expr(expr, self))
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)),
564 fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
565 expand_pat(pat, self)
568 fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
569 use std::mem::replace;
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);
577 let filename = if inner != syntax_pos::DUMMY_SP {
578 Some(self.cx.parse_sess.codemap().span_to_filename(inner))
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;
587 result = expand_item(item, self);
592 fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
593 expand_stmt(stmt, self)
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;
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()
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()
613 fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
614 expand_type(ty, self)
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") {
623 None => default_path,
625 self.cx.mod_path_stack.push(file_path)
628 fn pop_mod_path(&mut self) {
629 self.cx.mod_path_stack.pop().unwrap();
633 pub struct ExpansionConfig<'feat> {
634 pub crate_name: String,
635 pub features: Option<&'feat Features>,
636 pub recursion_limit: usize,
638 pub should_test: bool, // If false, strip `#[test]` nodes
641 macro_rules! feature_tests {
642 ($( fn $getter:ident = $field:ident, )*) => {
644 pub fn $getter(&self) -> bool {
645 match self.features {
646 Some(&Features { $field: true, .. }) => true,
654 impl<'feat> ExpansionConfig<'feat> {
655 pub fn default(crate_name: String) -> ExpansionConfig<'static> {
657 crate_name: crate_name,
666 fn enable_quotes = quote,
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,
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");
685 cx.crate_root = Some("std");
688 let mut expander = MacroExpander::new(&mut cx);
690 for (name, extension) in user_exts {
691 expander.cx.syntax_env.insert(name, extension);
694 let items = SmallVector::many(c.module.items);
695 expander.load_macros(&items);
696 c.module.items = items.into();
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();
702 if cx.parse_sess.span_diagnostic.err_count() > err_count {
703 cx.parse_sess.span_diagnostic.abort_if_errors();
708 return (ret, cx.syntax_env.names);
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
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> }
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))
727 fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
730 path: self.fold_path(node.path),
731 tts: self.fold_tts(&node.tts),
733 span: self.new_span(span),
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;
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})
753 use super::{expand_crate, ExpansionConfig};
755 use ext::base::{ExtCtxt, DummyMacroLoader};
757 use util::parser_testing::{string_to_parser};
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)
765 struct PathExprFinderContext {
766 path_accumulator: Vec<ast::Path> ,
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());
774 visit::walk_expr(self, expr);
778 // these following tests are quite fragile, in that they don't test what
779 // *kind* of failure occurs.
781 fn test_ecfg() -> ExpansionConfig<'static> {
782 ExpansionConfig::default("test".to_string())
785 // make sure that macros can't escape fns
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(),
794 Vec::new(), &sess).unwrap();
796 let mut loader = DummyMacroLoader;
797 let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
798 expand_crate(ecx, vec![], crate_ast);
801 // make sure that macros can't escape modules
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(),
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);
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(),
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);
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
839 #[test] fn macro_tokens_should_match(){
841 "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
844 // should be able to use a bound identifier as a literal in a macro definition:
845 #[test] fn self_macro_parsing(){
847 "macro_rules! foo ((zz) => (287;));
848 fn f(zz: i32) {foo!(zz);}".to_string()
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.