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, PatKind};
12 use ast::{Local, Ident, Mac_, Name, SpannedIdent};
13 use ast::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind};
18 use attr::AttrMetaMethods;
19 use codemap::{Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
20 use syntax_pos::{self, Span, ExpnId};
21 use config::StripUnconfigured;
23 use feature_gate::{self, Features};
26 use util::move_map::MoveMap;
27 use parse::token::{fresh_mark, fresh_name, intern, keywords};
29 use tokenstream::TokenTree;
30 use util::small_vector::SmallVector;
35 use std::collections::HashSet;
37 // A trait for AST nodes and AST node lists into which macro invocations may expand.
38 trait MacroGenerable: Sized {
39 // Expand the given MacResult using its appropriate `make_*` method.
40 fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self>;
42 // Fold this node or list of nodes using the given folder.
43 fn fold_with<F: Folder>(self, folder: &mut F) -> Self;
44 fn visit_with<V: Visitor>(&self, visitor: &mut V);
46 // The user-friendly name of the node type (e.g. "expression", "item", etc.) for diagnostics.
47 fn kind_name() -> &'static str;
49 // Return a placeholder expansion to allow compilation to continue after an erroring expansion.
50 fn dummy(span: Span) -> Self {
51 Self::make_with(DummyResult::any(span)).unwrap()
55 macro_rules! impl_macro_generable {
56 ($($ty:ty: $kind_name:expr, .$make:ident,
57 $(.$fold:ident)* $(lift .$fold_elt:ident)*,
58 $(.$visit:ident)* $(lift .$visit_elt:ident)*;)*) => { $(
59 impl MacroGenerable for $ty {
60 fn kind_name() -> &'static str { $kind_name }
61 fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> { result.$make() }
62 fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
63 $( folder.$fold(self) )*
64 $( self.into_iter().flat_map(|item| folder. $fold_elt (item)).collect() )*
66 fn visit_with<V: Visitor>(&self, visitor: &mut V) {
67 $( visitor.$visit(self) )*
68 $( for item in self.as_slice() { visitor. $visit_elt (item) } )*
74 impl_macro_generable! {
75 P<ast::Expr>: "expression", .make_expr, .fold_expr, .visit_expr;
76 P<ast::Pat>: "pattern", .make_pat, .fold_pat, .visit_pat;
77 P<ast::Ty>: "type", .make_ty, .fold_ty, .visit_ty;
78 SmallVector<ast::Stmt>: "statement", .make_stmts, lift .fold_stmt, lift .visit_stmt;
79 SmallVector<P<ast::Item>>: "item", .make_items, lift .fold_item, lift .visit_item;
80 SmallVector<ast::TraitItem>:
81 "trait item", .make_trait_items, lift .fold_trait_item, lift .visit_trait_item;
82 SmallVector<ast::ImplItem>:
83 "impl item", .make_impl_items, lift .fold_impl_item, lift .visit_impl_item;
86 impl MacroGenerable for Option<P<ast::Expr>> {
87 fn kind_name() -> &'static str { "expression" }
88 fn make_with<'a>(result: Box<MacResult + 'a>) -> Option<Self> {
89 result.make_expr().map(Some)
91 fn fold_with<F: Folder>(self, folder: &mut F) -> Self {
92 self.and_then(|expr| folder.fold_opt_expr(expr))
94 fn visit_with<V: Visitor>(&self, visitor: &mut V) {
95 self.as_ref().map(|expr| visitor.visit_expr(expr));
99 pub fn expand_expr(mut expr: ast::Expr, fld: &mut MacroExpander) -> P<ast::Expr> {
101 // expr_mac should really be expr_ext or something; it's the
102 // entry-point for all syntax extensions.
103 ast::ExprKind::Mac(mac) => {
104 return expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, fld);
107 ast::ExprKind::While(cond, body, opt_ident) => {
108 let cond = fld.fold_expr(cond);
109 let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
110 expr.node = ast::ExprKind::While(cond, body, opt_ident);
113 ast::ExprKind::WhileLet(pat, cond, body, opt_ident) => {
114 let pat = fld.fold_pat(pat);
115 let cond = fld.fold_expr(cond);
117 // Hygienic renaming of the body.
118 let ((body, opt_ident), mut rewritten_pats) =
119 rename_in_scope(vec![pat],
122 |rename_fld, fld, (body, opt_ident)| {
123 expand_loop_block(rename_fld.fold_block(body), opt_ident, fld)
125 assert!(rewritten_pats.len() == 1);
127 expr.node = ast::ExprKind::WhileLet(rewritten_pats.remove(0), cond, body, opt_ident);
130 ast::ExprKind::Loop(loop_block, opt_ident) => {
131 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
132 expr.node = ast::ExprKind::Loop(loop_block, opt_ident);
135 ast::ExprKind::ForLoop(pat, head, body, opt_ident) => {
136 let pat = fld.fold_pat(pat);
138 // Hygienic renaming of the for loop body (for loop binds its pattern).
139 let ((body, opt_ident), mut rewritten_pats) =
140 rename_in_scope(vec![pat],
143 |rename_fld, fld, (body, opt_ident)| {
144 expand_loop_block(rename_fld.fold_block(body), opt_ident, fld)
146 assert!(rewritten_pats.len() == 1);
148 let head = fld.fold_expr(head);
149 expr.node = ast::ExprKind::ForLoop(rewritten_pats.remove(0), head, body, opt_ident);
152 ast::ExprKind::IfLet(pat, sub_expr, body, else_opt) => {
153 let pat = fld.fold_pat(pat);
155 // Hygienic renaming of the body.
156 let (body, mut rewritten_pats) =
157 rename_in_scope(vec![pat],
160 |rename_fld, fld, body| {
161 fld.fold_block(rename_fld.fold_block(body))
163 assert!(rewritten_pats.len() == 1);
165 let else_opt = else_opt.map(|else_opt| fld.fold_expr(else_opt));
166 let sub_expr = fld.fold_expr(sub_expr);
167 expr.node = ast::ExprKind::IfLet(rewritten_pats.remove(0), sub_expr, body, else_opt);
170 ast::ExprKind::Closure(capture_clause, fn_decl, block, fn_decl_span) => {
171 let (rewritten_fn_decl, rewritten_block)
172 = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
173 expr.node = ast::ExprKind::Closure(capture_clause,
179 _ => expr = noop_fold_expr(expr, fld),
184 /// Expand a macro invocation. Returns the result of expansion.
185 fn expand_mac_invoc<T>(mac: ast::Mac, ident: Option<Ident>, attrs: Vec<ast::Attribute>, span: Span,
186 fld: &mut MacroExpander) -> T
187 where T: MacroGenerable,
189 // It would almost certainly be cleaner to pass the whole macro invocation in,
190 // rather than pulling it apart and marking the tts and the ctxt separately.
191 let Mac_ { path, tts, .. } = mac.node;
192 let mark = fresh_mark();
194 fn mac_result<'a>(path: &ast::Path, ident: Option<Ident>, tts: Vec<TokenTree>, mark: Mrk,
195 attrs: Vec<ast::Attribute>, call_site: Span, fld: &'a mut MacroExpander)
196 -> Option<Box<MacResult + 'a>> {
197 // Detect use of feature-gated or invalid attributes on macro invoations
198 // since they will not be detected after macro expansion.
199 for attr in attrs.iter() {
200 feature_gate::check_attribute(&attr, &fld.cx.parse_sess.span_diagnostic,
201 &fld.cx.parse_sess.codemap(),
202 &fld.cx.ecfg.features.unwrap());
205 if path.segments.len() > 1 || path.global || !path.segments[0].parameters.is_empty() {
206 fld.cx.span_err(path.span, "expected macro name without module separators");
210 let extname = path.segments[0].identifier.name;
211 let extension = if let Some(extension) = fld.cx.syntax_env.find(extname) {
214 let mut err = fld.cx.struct_span_err(path.span,
215 &format!("macro undefined: '{}!'", &extname));
216 fld.cx.suggest_macro_name(&extname.as_str(), &mut err);
221 let ident = ident.unwrap_or(keywords::Invalid.ident());
223 NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
224 if ident.name != keywords::Invalid.name() {
226 format!("macro {}! expects no ident argument, given '{}'", extname, ident);
227 fld.cx.span_err(path.span, &msg);
231 fld.cx.bt_push(ExpnInfo {
232 call_site: call_site,
233 callee: NameAndSpan {
234 format: MacroBang(extname),
236 allow_internal_unstable: allow_internal_unstable,
240 let marked_tts = mark_tts(&tts, mark);
241 Some(expandfun.expand(fld.cx, call_site, &marked_tts))
244 IdentTT(ref expander, tt_span, allow_internal_unstable) => {
245 if ident.name == keywords::Invalid.name() {
246 fld.cx.span_err(path.span,
247 &format!("macro {}! expects an ident argument", extname));
251 fld.cx.bt_push(ExpnInfo {
252 call_site: call_site,
253 callee: NameAndSpan {
254 format: MacroBang(extname),
256 allow_internal_unstable: allow_internal_unstable,
260 let marked_tts = mark_tts(&tts, mark);
261 Some(expander.expand(fld.cx, call_site, ident, marked_tts))
265 if ident.name == keywords::Invalid.name() {
266 fld.cx.span_err(path.span,
267 &format!("macro {}! expects an ident argument", extname));
271 fld.cx.bt_push(ExpnInfo {
272 call_site: call_site,
273 callee: NameAndSpan {
274 format: MacroBang(extname),
276 // `macro_rules!` doesn't directly allow unstable
277 // (this is orthogonal to whether the macro it creates allows it)
278 allow_internal_unstable: false,
282 // DON'T mark before expansion.
283 fld.cx.insert_macro(ast::MacroDef {
285 id: ast::DUMMY_NODE_ID,
290 export: attr::contains_name(&attrs, "macro_export"),
291 allow_internal_unstable: attr::contains_name(&attrs, "allow_internal_unstable"),
295 // macro_rules! has a side effect but expands to nothing.
300 MultiDecorator(..) | MultiModifier(..) => {
301 fld.cx.span_err(path.span,
302 &format!("`{}` can only be used in attributes", extname));
308 let opt_expanded = T::make_with(match mac_result(&path, ident, tts, mark, attrs, span, fld) {
309 Some(result) => result,
310 None => return T::dummy(span),
313 let expanded = if let Some(expanded) = opt_expanded {
316 let msg = format!("non-{kind} macro in {kind} position: {name}",
317 name = path.segments[0].identifier.name, kind = T::kind_name());
318 fld.cx.span_err(path.span, &msg);
319 return T::dummy(span);
322 let marked = expanded.fold_with(&mut Marker { mark: mark, expn_id: Some(fld.cx.backtrace()) });
323 let configured = marked.fold_with(&mut fld.strip_unconfigured());
324 fld.load_macros(&configured);
325 let fully_expanded = configured.fold_with(fld);
330 /// Rename loop label and expand its loop body
332 /// The renaming procedure for loop is different in the sense that the loop
333 /// body is in a block enclosed by loop head so the renaming of loop label
334 /// must be propagated to the enclosed context.
335 fn expand_loop_block(loop_block: P<Block>,
336 opt_ident: Option<SpannedIdent>,
337 fld: &mut MacroExpander) -> (P<Block>, Option<SpannedIdent>) {
340 let new_label = fresh_name(label.node);
341 let rename = (label.node, new_label);
343 // The rename *must not* be added to the pending list of current
344 // syntax context otherwise an unrelated `break` or `continue` in
345 // the same context will pick that up in the deferred renaming pass
346 // and be renamed incorrectly.
347 let mut rename_list = vec!(rename);
348 let mut rename_fld = IdentRenamer{renames: &mut rename_list};
349 let renamed_ident = rename_fld.fold_ident(label.node);
351 // The rename *must* be added to the enclosed syntax context for
352 // `break` or `continue` to pick up because by definition they are
353 // in a block enclosed by loop head.
354 fld.cx.syntax_env.push_frame();
355 fld.cx.syntax_env.info().pending_renames.push(rename);
356 let expanded_block = expand_block_elts(loop_block, fld);
357 fld.cx.syntax_env.pop_frame();
359 (expanded_block, Some(Spanned { node: renamed_ident, span: label.span }))
361 None => (fld.fold_block(loop_block), opt_ident)
365 // eval $e with a new exts frame.
366 // must be a macro so that $e isn't evaluated too early.
367 macro_rules! with_exts_frame {
368 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
369 ({$extsboxexpr.push_frame();
370 $extsboxexpr.info().macros_escape = $macros_escape;
372 $extsboxexpr.pop_frame();
377 // When we enter a module, record it, for the sake of `module!`
378 pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
379 -> SmallVector<P<ast::Item>> {
380 expand_annotatable(Annotatable::Item(it), fld)
381 .into_iter().map(|i| i.expect_item()).collect()
385 fn expand_item_kind(item: ast::ItemKind, fld: &mut MacroExpander) -> ast::ItemKind {
387 ast::ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
388 let (rewritten_fn_decl, rewritten_body)
389 = expand_and_rename_fn_decl_and_block(decl, body, fld);
390 let expanded_generics = fold::noop_fold_generics(generics,fld);
391 ast::ItemKind::Fn(rewritten_fn_decl, unsafety, constness, abi,
392 expanded_generics, rewritten_body)
394 _ => noop_fold_item_kind(item, fld)
398 // does this attribute list contain "macro_use" ?
399 fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
401 let mut is_use = attr.check_name("macro_use");
402 if attr.check_name("macro_escape") {
404 fld.cx.struct_span_warn(attr.span,
405 "macro_escape is a deprecated synonym for macro_use");
407 if let ast::AttrStyle::Inner = attr.node.style {
408 err.help("consider an outer attribute, \
409 #[macro_use] mod ...").emit();
416 match attr.node.value.node {
417 ast::MetaItemKind::Word(..) => (),
418 _ => fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"),
427 fn expand_stmt(stmt: Stmt, fld: &mut MacroExpander) -> SmallVector<Stmt> {
428 // perform all pending renames
430 let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
431 let mut rename_fld = IdentRenamer{renames:pending_renames};
432 rename_fld.fold_stmt(stmt).expect_one("rename_fold didn't return one value")
435 let (mac, style, attrs) = match stmt.node {
436 StmtKind::Mac(mac) => mac.unwrap(),
437 _ => return expand_non_macro_stmt(stmt, fld)
440 let mut fully_expanded: SmallVector<ast::Stmt> =
441 expand_mac_invoc(mac, None, attrs.into(), stmt.span, fld);
443 // If this is a macro invocation with a semicolon, then apply that
444 // semicolon to the final statement produced by expansion.
445 if style == MacStmtStyle::Semicolon {
446 if let Some(stmt) = fully_expanded.pop() {
447 fully_expanded.push(Stmt {
449 node: match stmt.node {
450 StmtKind::Expr(expr) => StmtKind::Semi(expr),
451 _ => stmt.node /* might already have a semi */
461 // expand a non-macro stmt. this is essentially the fallthrough for
462 // expand_stmt, above.
463 fn expand_non_macro_stmt(stmt: Stmt, fld: &mut MacroExpander)
464 -> SmallVector<Stmt> {
467 StmtKind::Local(local) => {
469 let rewritten_local = local.map(|Local {id, pat, ty, init, span, attrs}| {
470 // expand the ty since TyKind::FixedLengthVec contains an Expr
471 // and thus may have a macro use
472 let expanded_ty = ty.map(|t| fld.fold_ty(t));
473 // expand the pat (it might contain macro uses):
474 let expanded_pat = fld.fold_pat(pat);
475 // find the PatIdents in the pattern:
476 // oh dear heaven... this is going to include the enum
477 // names, as well... but that should be okay, as long as
478 // the new names are gensyms for the old ones.
479 // generate fresh names, push them to a new pending list
480 let idents = pattern_bindings(&expanded_pat);
481 let mut new_pending_renames =
482 idents.iter().map(|ident| (*ident, fresh_name(*ident))).collect();
483 // rewrite the pattern using the new names (the old
484 // ones have already been applied):
485 let rewritten_pat = {
486 // nested binding to allow borrow to expire:
487 let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
488 rename_fld.fold_pat(expanded_pat)
490 // add them to the existing pending renames:
491 fld.cx.syntax_env.info().pending_renames
492 .extend(new_pending_renames);
497 // also, don't forget to expand the init:
498 init: init.map(|e| fld.fold_expr(e)),
500 attrs: fold::fold_thin_attrs(attrs, fld),
503 SmallVector::one(Stmt {
505 node: StmtKind::Local(rewritten_local),
509 _ => noop_fold_stmt(stmt, fld),
513 // expand the arm of a 'match', renaming for macro hygiene
514 fn expand_arm(arm: ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
515 // expand pats... they might contain macro uses:
516 let expanded_pats = arm.pats.move_map(|pat| fld.fold_pat(pat));
517 if expanded_pats.is_empty() {
518 panic!("encountered match arm with 0 patterns");
521 // apply renaming and then expansion to the guard and the body:
522 let ((rewritten_guard, rewritten_body), rewritten_pats) =
523 rename_in_scope(expanded_pats,
525 (arm.guard, arm.body),
526 |rename_fld, fld, (ag, ab)|{
527 let rewritten_guard = ag.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
528 let rewritten_body = fld.fold_expr(rename_fld.fold_expr(ab));
529 (rewritten_guard, rewritten_body)
533 attrs: fold::fold_attrs(arm.attrs, fld),
534 pats: rewritten_pats,
535 guard: rewritten_guard,
536 body: rewritten_body,
540 fn rename_in_scope<X, F>(pats: Vec<P<ast::Pat>>,
541 fld: &mut MacroExpander,
544 -> (X, Vec<P<ast::Pat>>)
545 where F: Fn(&mut IdentRenamer, &mut MacroExpander, X) -> X
547 // all of the pats must have the same set of bindings, so use the
548 // first one to extract them and generate new names:
549 let idents = pattern_bindings(&pats[0]);
550 let new_renames = idents.into_iter().map(|id| (id, fresh_name(id))).collect();
551 // apply the renaming, but only to the PatIdents:
552 let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
553 let rewritten_pats = pats.move_map(|pat| rename_pats_fld.fold_pat(pat));
555 let mut rename_fld = IdentRenamer{ renames:&new_renames };
556 (f(&mut rename_fld, fld, x), rewritten_pats)
559 /// A visitor that extracts the PatKind::Ident (binding) paths
560 /// from a given thingy and puts them in a mutable
563 struct PatIdentFinder {
564 ident_accumulator: Vec<ast::Ident>
567 impl Visitor for PatIdentFinder {
568 fn visit_pat(&mut self, pattern: &ast::Pat) {
570 ast::Pat { id: _, node: PatKind::Ident(_, ref path1, ref inner), span: _ } => {
571 self.ident_accumulator.push(path1.node);
572 // visit optional subpattern of PatKind::Ident:
573 if let Some(ref subpat) = *inner {
574 self.visit_pat(subpat)
577 // use the default traversal for non-PatIdents
578 _ => visit::walk_pat(self, pattern)
583 /// find the PatKind::Ident paths in a pattern
584 fn pattern_bindings(pat: &ast::Pat) -> Vec<ast::Ident> {
585 let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
586 name_finder.visit_pat(pat);
587 name_finder.ident_accumulator
590 /// find the PatKind::Ident paths in a
591 fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
592 let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
593 for arg in &fn_decl.inputs {
594 pat_idents.visit_pat(&arg.pat);
596 pat_idents.ident_accumulator
599 // expand a block. pushes a new exts_frame, then calls expand_block_elts
600 pub fn expand_block(blk: P<Block>, fld: &mut MacroExpander) -> P<Block> {
601 // see note below about treatment of exts table
602 with_exts_frame!(fld.cx.syntax_env,false,
603 expand_block_elts(blk, fld))
606 // expand the elements of a block.
607 pub fn expand_block_elts(b: P<Block>, fld: &mut MacroExpander) -> P<Block> {
608 b.map(|Block {id, stmts, rules, span}| {
609 let new_stmts = stmts.into_iter().flat_map(|x| {
610 // perform pending renames and expand macros in the statement
611 fld.fold_stmt(x).into_iter()
622 fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
624 PatKind::Mac(_) => {}
625 _ => return noop_fold_pat(p, fld)
627 p.and_then(|ast::Pat {node, span, ..}| {
629 PatKind::Mac(mac) => expand_mac_invoc(mac, None, Vec::new(), span, fld),
635 /// A tree-folder that applies every rename in its (mutable) list
636 /// to every identifier, including both bindings and varrefs
637 /// (and lots of things that will turn out to be neither)
638 pub struct IdentRenamer<'a> {
639 renames: &'a mtwt::RenameList,
642 impl<'a> Folder for IdentRenamer<'a> {
643 fn fold_ident(&mut self, id: Ident) -> Ident {
644 mtwt::apply_renames(self.renames, id)
646 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
647 fold::noop_fold_mac(mac, self)
651 /// A tree-folder that applies every rename in its list to
652 /// the idents that are in PatKind::Ident patterns. This is more narrowly
653 /// focused than IdentRenamer, and is needed for FnDecl,
654 /// where we want to rename the args but not the fn name or the generics etc.
655 pub struct PatIdentRenamer<'a> {
656 renames: &'a mtwt::RenameList,
659 impl<'a> Folder for PatIdentRenamer<'a> {
660 fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
662 PatKind::Ident(..) => {},
663 _ => return noop_fold_pat(pat, self)
666 pat.map(|ast::Pat {id, node, span}| match node {
667 PatKind::Ident(binding_mode, Spanned{span: sp, node: ident}, sub) => {
668 let new_ident = mtwt::apply_renames(self.renames, ident);
670 PatKind::Ident(binding_mode,
671 Spanned{span: sp, node: new_ident},
672 sub.map(|p| self.fold_pat(p)));
682 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
683 fold::noop_fold_mac(mac, self)
687 fn expand_multi_modified(a: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
689 Annotatable::Item(it) => match it.node {
690 ast::ItemKind::Mac(..) => {
691 it.and_then(|it| match it.node {
692 ItemKind::Mac(mac) =>
693 expand_mac_invoc(mac, Some(it.ident), it.attrs, it.span, fld),
697 ast::ItemKind::Mod(_) | ast::ItemKind::ForeignMod(_) => {
699 it.ident.name != keywords::Invalid.name();
702 fld.cx.mod_push(it.ident);
704 let macro_use = contains_macro_use(fld, &it.attrs);
705 let result = with_exts_frame!(fld.cx.syntax_env,
707 noop_fold_item(it, fld));
713 _ => noop_fold_item(it, fld),
714 }.into_iter().map(|i| Annotatable::Item(i)).collect(),
716 Annotatable::TraitItem(it) => {
717 expand_trait_item(it.unwrap(), fld).into_iter().
718 map(|it| Annotatable::TraitItem(P(it))).collect()
721 Annotatable::ImplItem(ii) => {
722 expand_impl_item(ii.unwrap(), fld).into_iter().
723 map(|ii| Annotatable::ImplItem(P(ii))).collect()
728 fn expand_annotatable(mut item: Annotatable, fld: &mut MacroExpander) -> SmallVector<Annotatable> {
729 let mut multi_modifier = None;
730 item = item.map_attrs(|mut attrs| {
731 for i in 0..attrs.len() {
732 if let Some(extension) = fld.cx.syntax_env.find(intern(&attrs[i].name())) {
734 MultiModifier(..) | MultiDecorator(..) => {
735 multi_modifier = Some((attrs.remove(i), extension));
745 match multi_modifier {
746 None => expand_multi_modified(item, fld),
747 Some((attr, extension)) => {
748 attr::mark_used(&attr);
749 fld.cx.bt_push(ExpnInfo {
750 call_site: attr.span,
751 callee: NameAndSpan {
752 format: MacroAttribute(intern(&attr.name())),
753 span: Some(attr.span),
754 // attributes can do whatever they like, for now
755 allow_internal_unstable: true,
759 let modified = match *extension {
760 MultiModifier(ref mac) => mac.expand(fld.cx, attr.span, &attr.node.value, item),
761 MultiDecorator(ref mac) => {
762 let mut items = Vec::new();
763 mac.expand(fld.cx, attr.span, &attr.node.value, &item,
764 &mut |item| items.push(item));
772 let configured = modified.into_iter().flat_map(|it| {
773 it.fold_with(&mut fld.strip_unconfigured())
774 }).collect::<SmallVector<_>>();
776 configured.into_iter().flat_map(|it| expand_annotatable(it, fld)).collect()
781 fn expand_impl_item(ii: ast::ImplItem, fld: &mut MacroExpander)
782 -> SmallVector<ast::ImplItem> {
784 ast::ImplItemKind::Method(..) => SmallVector::one(ast::ImplItem {
789 defaultness: ii.defaultness,
790 node: match ii.node {
791 ast::ImplItemKind::Method(sig, body) => {
792 let (sig, body) = expand_and_rename_method(sig, body, fld);
793 ast::ImplItemKind::Method(sig, body)
799 ast::ImplItemKind::Macro(mac) => {
800 expand_mac_invoc(mac, None, ii.attrs, ii.span, fld)
802 _ => fold::noop_fold_impl_item(ii, fld)
806 fn expand_trait_item(ti: ast::TraitItem, fld: &mut MacroExpander)
807 -> SmallVector<ast::TraitItem> {
809 ast::TraitItemKind::Method(_, Some(_)) => {
810 SmallVector::one(ast::TraitItem {
814 node: match ti.node {
815 ast::TraitItemKind::Method(sig, Some(body)) => {
816 let (sig, body) = expand_and_rename_method(sig, body, fld);
817 ast::TraitItemKind::Method(sig, Some(body))
824 ast::TraitItemKind::Macro(mac) => {
825 expand_mac_invoc(mac, None, ti.attrs, ti.span, fld)
827 _ => fold::noop_fold_trait_item(ti, fld)
831 /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
832 /// PatIdents in its arguments to perform renaming in the FnDecl and
833 /// the block, returning both the new FnDecl and the new Block.
834 fn expand_and_rename_fn_decl_and_block(fn_decl: P<ast::FnDecl>, block: P<ast::Block>,
835 fld: &mut MacroExpander)
836 -> (P<ast::FnDecl>, P<ast::Block>) {
837 let expanded_decl = fld.fold_fn_decl(fn_decl);
838 let idents = fn_decl_arg_bindings(&expanded_decl);
840 idents.iter().map(|id| (*id,fresh_name(*id))).collect();
841 // first, a renamer for the PatIdents, for the fn_decl:
842 let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
843 let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
844 // now, a renamer for *all* idents, for the body:
845 let mut rename_fld = IdentRenamer{renames: &renames};
846 let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
847 (rewritten_fn_decl,rewritten_body)
850 fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
851 fld: &mut MacroExpander)
852 -> (ast::MethodSig, P<ast::Block>) {
853 let (rewritten_fn_decl, rewritten_body)
854 = expand_and_rename_fn_decl_and_block(sig.decl, body, fld);
856 generics: fld.fold_generics(sig.generics),
858 unsafety: sig.unsafety,
859 constness: sig.constness,
860 decl: rewritten_fn_decl
864 pub fn expand_type(t: P<ast::Ty>, fld: &mut MacroExpander) -> P<ast::Ty> {
865 let t = match t.node.clone() {
866 ast::TyKind::Mac(mac) => {
867 if fld.cx.ecfg.features.unwrap().type_macros {
868 expand_mac_invoc(mac, None, Vec::new(), t.span, fld)
870 feature_gate::emit_feature_err(
871 &fld.cx.parse_sess.span_diagnostic,
874 feature_gate::GateIssue::Language,
875 "type macros are experimental");
877 DummyResult::raw_ty(t.span)
883 fold::noop_fold_ty(t, fld)
886 /// A tree-folder that performs macro expansion
887 pub struct MacroExpander<'a, 'b:'a> {
888 pub cx: &'a mut ExtCtxt<'b>,
891 impl<'a, 'b> MacroExpander<'a, 'b> {
892 pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
893 MacroExpander { cx: cx }
896 fn strip_unconfigured(&mut self) -> StripUnconfigured {
898 config: &self.cx.cfg,
899 should_test: self.cx.ecfg.should_test,
900 sess: self.cx.parse_sess,
901 features: self.cx.ecfg.features,
905 fn load_macros<T: MacroGenerable>(&mut self, node: &T) {
906 struct MacroLoadingVisitor<'a, 'b: 'a>{
907 cx: &'a mut ExtCtxt<'b>,
911 impl<'a, 'b> Visitor for MacroLoadingVisitor<'a, 'b> {
912 fn visit_mac(&mut self, _: &ast::Mac) {}
913 fn visit_item(&mut self, item: &ast::Item) {
914 if let ast::ItemKind::ExternCrate(..) = item.node {
915 // We need to error on `#[macro_use] extern crate` when it isn't at the
916 // crate root, because `$crate` won't work properly.
917 for def in self.cx.loader.load_crate(item, self.at_crate_root) {
918 self.cx.insert_macro(def);
921 let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
922 visit::walk_item(self, item);
923 self.at_crate_root = at_crate_root;
926 fn visit_block(&mut self, block: &ast::Block) {
927 let at_crate_root = ::std::mem::replace(&mut self.at_crate_root, false);
928 visit::walk_block(self, block);
929 self.at_crate_root = at_crate_root;
933 node.visit_with(&mut MacroLoadingVisitor {
934 at_crate_root: self.cx.syntax_env.is_crate_root(),
940 impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
941 fn fold_crate(&mut self, c: Crate) -> Crate {
942 self.cx.filename = Some(self.cx.parse_sess.codemap().span_to_filename(c.span));
943 noop_fold_crate(c, self)
946 fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
947 expr.and_then(|expr| expand_expr(expr, self))
950 fn fold_opt_expr(&mut self, expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
951 expr.and_then(|expr| match expr.node {
952 ast::ExprKind::Mac(mac) =>
953 expand_mac_invoc(mac, None, expr.attrs.into(), expr.span, self),
954 _ => Some(expand_expr(expr, self)),
958 fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
959 expand_pat(pat, self)
962 fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
963 use std::mem::replace;
965 if let ast::ItemKind::Mod(ast::Mod { inner, .. }) = item.node {
966 if item.span.contains(inner) {
967 self.push_mod_path(item.ident, &item.attrs);
968 result = expand_item(item, self);
971 let filename = if inner != syntax_pos::DUMMY_SP {
972 Some(self.cx.parse_sess.codemap().span_to_filename(inner))
974 let orig_filename = replace(&mut self.cx.filename, filename);
975 let orig_mod_path_stack = replace(&mut self.cx.mod_path_stack, Vec::new());
976 result = expand_item(item, self);
977 self.cx.filename = orig_filename;
978 self.cx.mod_path_stack = orig_mod_path_stack;
981 result = expand_item(item, self);
986 fn fold_item_kind(&mut self, item: ast::ItemKind) -> ast::ItemKind {
987 expand_item_kind(item, self)
990 fn fold_stmt(&mut self, stmt: ast::Stmt) -> SmallVector<ast::Stmt> {
991 expand_stmt(stmt, self)
994 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
995 let was_in_block = ::std::mem::replace(&mut self.cx.in_block, true);
996 let result = expand_block(block, self);
997 self.cx.in_block = was_in_block;
1001 fn fold_arm(&mut self, arm: ast::Arm) -> ast::Arm {
1002 expand_arm(arm, self)
1005 fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
1006 expand_annotatable(Annotatable::TraitItem(P(i)), self)
1007 .into_iter().map(|i| i.expect_trait_item()).collect()
1010 fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
1011 expand_annotatable(Annotatable::ImplItem(P(i)), self)
1012 .into_iter().map(|i| i.expect_impl_item()).collect()
1015 fn fold_ty(&mut self, ty: P<ast::Ty>) -> P<ast::Ty> {
1016 expand_type(ty, self)
1020 impl<'a, 'b> MacroExpander<'a, 'b> {
1021 fn push_mod_path(&mut self, id: Ident, attrs: &[ast::Attribute]) {
1022 let default_path = id.name.as_str();
1023 let file_path = match ::attr::first_attr_value_str_by_name(attrs, "path") {
1025 None => default_path,
1027 self.cx.mod_path_stack.push(file_path)
1030 fn pop_mod_path(&mut self) {
1031 self.cx.mod_path_stack.pop().unwrap();
1035 pub struct ExpansionConfig<'feat> {
1036 pub crate_name: String,
1037 pub features: Option<&'feat Features>,
1038 pub recursion_limit: usize,
1039 pub trace_mac: bool,
1040 pub should_test: bool, // If false, strip `#[test]` nodes
1043 macro_rules! feature_tests {
1044 ($( fn $getter:ident = $field:ident, )*) => {
1046 pub fn $getter(&self) -> bool {
1047 match self.features {
1048 Some(&Features { $field: true, .. }) => true,
1056 impl<'feat> ExpansionConfig<'feat> {
1057 pub fn default(crate_name: String) -> ExpansionConfig<'static> {
1059 crate_name: crate_name,
1061 recursion_limit: 64,
1068 fn enable_quotes = quote,
1069 fn enable_asm = asm,
1070 fn enable_log_syntax = log_syntax,
1071 fn enable_concat_idents = concat_idents,
1072 fn enable_trace_macros = trace_macros,
1073 fn enable_allow_internal_unstable = allow_internal_unstable,
1074 fn enable_custom_derive = custom_derive,
1075 fn enable_pushpop_unsafe = pushpop_unsafe,
1079 pub fn expand_crate(mut cx: ExtCtxt,
1080 user_exts: Vec<NamedSyntaxExtension>,
1081 mut c: Crate) -> (Crate, HashSet<Name>) {
1082 if std_inject::no_core(&c) {
1083 cx.crate_root = None;
1084 } else if std_inject::no_std(&c) {
1085 cx.crate_root = Some("core");
1087 cx.crate_root = Some("std");
1090 let mut expander = MacroExpander::new(&mut cx);
1092 for (name, extension) in user_exts {
1093 expander.cx.syntax_env.insert(name, extension);
1096 let items = SmallVector::many(c.module.items);
1097 expander.load_macros(&items);
1098 c.module.items = items.into();
1100 let err_count = cx.parse_sess.span_diagnostic.err_count();
1101 let mut ret = expander.fold_crate(c);
1102 ret.exported_macros = expander.cx.exported_macros.clone();
1104 if cx.parse_sess.span_diagnostic.err_count() > err_count {
1105 cx.parse_sess.span_diagnostic.abort_if_errors();
1110 return (ret, cx.syntax_env.names);
1113 // HYGIENIC CONTEXT EXTENSION:
1114 // all of these functions are for walking over
1115 // ASTs and making some change to the context of every
1116 // element that has one. a CtxtFn is a trait-ified
1117 // version of a closure in (SyntaxContext -> SyntaxContext).
1118 // the ones defined here include:
1119 // Marker - add a mark to a context
1121 // A Marker adds the given mark to the syntax context and
1122 // sets spans' `expn_id` to the given expn_id (unless it is `None`).
1123 struct Marker { mark: Mrk, expn_id: Option<ExpnId> }
1125 impl Folder for Marker {
1126 fn fold_ident(&mut self, id: Ident) -> Ident {
1127 ast::Ident::new(id.name, mtwt::apply_mark(self.mark, id.ctxt))
1129 fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
1132 path: self.fold_path(node.path),
1133 tts: self.fold_tts(&node.tts),
1135 span: self.new_span(span),
1139 fn new_span(&mut self, mut span: Span) -> Span {
1140 if let Some(expn_id) = self.expn_id {
1141 span.expn_id = expn_id;
1147 // apply a given mark to the given token trees. Used prior to expansion of a macro.
1148 fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
1149 noop_fold_tts(tts, &mut Marker{mark:m, expn_id: None})
1155 use super::{pattern_bindings, expand_crate};
1156 use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
1160 use ext::base::{ExtCtxt, DummyMacroLoader};
1165 use util::parser_testing::{string_to_parser};
1166 use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
1170 // a visitor that extracts the paths
1171 // from a given thingy and puts them in a mutable
1172 // array (passed in to the traversal)
1174 struct PathExprFinderContext {
1175 path_accumulator: Vec<ast::Path> ,
1178 impl Visitor for PathExprFinderContext {
1179 fn visit_expr(&mut self, expr: &ast::Expr) {
1180 if let ast::ExprKind::Path(None, ref p) = expr.node {
1181 self.path_accumulator.push(p.clone());
1183 visit::walk_expr(self, expr);
1187 // find the variable references in a crate
1188 fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
1189 let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
1190 visit::walk_crate(&mut path_finder, the_crate);
1191 path_finder.path_accumulator
1194 /// A Visitor that extracts the identifiers from a thingy.
1195 // as a side note, I'm starting to want to abstract over these....
1196 struct IdentFinder {
1197 ident_accumulator: Vec<ast::Ident>
1200 impl Visitor for IdentFinder {
1201 fn visit_ident(&mut self, _: syntax_pos::Span, id: ast::Ident){
1202 self.ident_accumulator.push(id);
1206 /// Find the idents in a crate
1207 fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
1208 let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
1209 visit::walk_crate(&mut ident_finder, the_crate);
1210 ident_finder.ident_accumulator
1213 // these following tests are quite fragile, in that they don't test what
1214 // *kind* of failure occurs.
1216 fn test_ecfg() -> ExpansionConfig<'static> {
1217 ExpansionConfig::default("test".to_string())
1220 // make sure that macros can't escape fns
1222 #[test] fn macros_cant_escape_fns_test () {
1223 let src = "fn bogus() {macro_rules! z (() => (3+4));}\
1224 fn inty() -> i32 { z!() }".to_string();
1225 let sess = parse::ParseSess::new();
1226 let crate_ast = parse::parse_crate_from_source_str(
1227 "<test>".to_string(),
1229 Vec::new(), &sess).unwrap();
1231 let mut loader = DummyMacroLoader;
1232 let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1233 expand_crate(ecx, vec![], crate_ast);
1236 // make sure that macros can't escape modules
1238 #[test] fn macros_cant_escape_mods_test () {
1239 let src = "mod foo {macro_rules! z (() => (3+4));}\
1240 fn inty() -> i32 { z!() }".to_string();
1241 let sess = parse::ParseSess::new();
1242 let crate_ast = parse::parse_crate_from_source_str(
1243 "<test>".to_string(),
1245 Vec::new(), &sess).unwrap();
1246 let mut loader = DummyMacroLoader;
1247 let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1248 expand_crate(ecx, vec![], crate_ast);
1251 // macro_use modules should allow macros to escape
1252 #[test] fn macros_can_escape_flattened_mods_test () {
1253 let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
1254 fn inty() -> i32 { z!() }".to_string();
1255 let sess = parse::ParseSess::new();
1256 let crate_ast = parse::parse_crate_from_source_str(
1257 "<test>".to_string(),
1259 Vec::new(), &sess).unwrap();
1260 let mut loader = DummyMacroLoader;
1261 let ecx = ExtCtxt::new(&sess, vec![], test_ecfg(), &mut loader);
1262 expand_crate(ecx, vec![], crate_ast);
1265 fn expand_crate_str(crate_str: String) -> ast::Crate {
1266 let ps = parse::ParseSess::new();
1267 let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
1268 // the cfg argument actually does matter, here...
1269 let mut loader = DummyMacroLoader;
1270 let ecx = ExtCtxt::new(&ps, vec![], test_ecfg(), &mut loader);
1271 expand_crate(ecx, vec![], crate_ast).0
1274 // find the pat_ident paths in a crate
1275 fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
1276 let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
1277 visit::walk_crate(&mut name_finder, the_crate);
1278 name_finder.ident_accumulator
1281 #[test] fn macro_tokens_should_match(){
1283 "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
1286 // should be able to use a bound identifier as a literal in a macro definition:
1287 #[test] fn self_macro_parsing(){
1289 "macro_rules! foo ((zz) => (287;));
1290 fn f(zz: i32) {foo!(zz);}".to_string()
1294 // create a really evil test case where a $x appears inside a binding of $x
1295 // but *shouldn't* bind because it was inserted by a different macro....
1296 // can't write this test case until we have macro-generating macros.
1299 fn fmt_in_macro_used_inside_module_macro() {
1300 let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
1301 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
1304 let cr = expand_crate_str(crate_str);
1305 // find the xx binding
1306 let bindings = crate_bindings(&cr);
1307 let cxbinds: Vec<&ast::Ident> =
1308 bindings.iter().filter(|b| b.name.as_str() == "xx").collect();
1309 let cxbinds: &[&ast::Ident] = &cxbinds[..];
1310 let cxbind = match (cxbinds.len(), cxbinds.get(0)) {
1312 _ => panic!("expected just one binding for ext_cx")
1314 let resolved_binding = mtwt::resolve(*cxbind);
1315 let varrefs = crate_varrefs(&cr);
1317 // the xx binding should bind all of the xx varrefs:
1318 for (idx,v) in varrefs.iter().filter(|p| {
1319 p.segments.len() == 1
1320 && p.segments[0].identifier.name.as_str() == "xx"
1322 if mtwt::resolve(v.segments[0].identifier) != resolved_binding {
1323 println!("uh oh, xx binding didn't match xx varref:");
1324 println!("this is xx varref \\# {}", idx);
1325 println!("binding: {}", cxbind);
1326 println!("resolves to: {}", resolved_binding);
1327 println!("varref: {}", v.segments[0].identifier);
1328 println!("resolves to: {}",
1329 mtwt::resolve(v.segments[0].identifier));
1330 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1332 assert_eq!(mtwt::resolve(v.segments[0].identifier),
1339 let pat = string_to_pat(
1340 "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
1341 let idents = pattern_bindings(&pat);
1342 assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
1345 // test the list of identifier patterns gathered by the visitor. Note that
1346 // 'None' is listed as an identifier pattern because we don't yet know that
1347 // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
1349 fn crate_bindings_test(){
1350 let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| {
1351 match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
1352 let idents = crate_bindings(&the_crate);
1353 assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
1356 // test the IdentRenamer directly
1358 fn ident_renamer_test () {
1359 let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1360 let f_ident = token::str_to_ident("f");
1361 let x_ident = token::str_to_ident("x");
1362 let int_ident = token::str_to_ident("i32");
1363 let renames = vec!((x_ident,Name(16)));
1364 let mut renamer = IdentRenamer{renames: &renames};
1365 let renamed_crate = renamer.fold_crate(the_crate);
1366 let idents = crate_idents(&renamed_crate);
1367 let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1368 assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)]);
1371 // test the PatIdentRenamer; only PatIdents get renamed
1373 fn pat_ident_renamer_test () {
1374 let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1375 let f_ident = token::str_to_ident("f");
1376 let x_ident = token::str_to_ident("x");
1377 let int_ident = token::str_to_ident("i32");
1378 let renames = vec!((x_ident,Name(16)));
1379 let mut renamer = PatIdentRenamer{renames: &renames};
1380 let renamed_crate = renamer.fold_crate(the_crate);
1381 let idents = crate_idents(&renamed_crate);
1382 let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1383 let x_name = x_ident.name;
1384 assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name]);