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, DeclKind, PatKind}
;
12 use ast
::{Local, Ident, Mac_, Name}
;
13 use ast
::{MacStmtStyle, Mrk, Stmt, StmtKind, ItemKind}
;
17 use ext
::build
::AstBuilder
;
19 use attr
::{AttrMetaMethods, WithAttrs}
;
21 use codemap
::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute}
;
23 use feature_gate
::{self, Features}
;
26 use util
::move_map
::MoveMap
;
28 use parse
::token
::{fresh_mark, fresh_name, intern}
;
30 use util
::small_vector
::SmallVector
;
35 use std
::collections
::HashSet
;
38 pub fn expand_expr(e
: P
<ast
::Expr
>, fld
: &mut MacroExpander
) -> P
<ast
::Expr
> {
39 let expr_span
= e
.span
;
40 return e
.and_then(|ast
::Expr {id, node, span, attrs}
| match node
{
42 // expr_mac should really be expr_ext or something; it's the
43 // entry-point for all syntax extensions.
44 ast
::ExprKind
::Mac(mac
) => {
46 // Assert that we drop any macro attributes on the floor here
49 let expanded_expr
= match expand_mac_invoc(mac
, span
,
54 return DummyResult
::raw_expr(span
);
58 // Keep going, outside-in.
59 let fully_expanded
= fld
.fold_expr(expanded_expr
);
60 let span
= fld
.new_span(span
);
63 fully_expanded
.map(|e
| ast
::Expr
{
64 id
: ast
::DUMMY_NODE_ID
,
71 ast
::ExprKind
::InPlace(placer
, value_expr
) => {
72 // Ensure feature-gate is enabled
73 feature_gate
::check_for_placement_in(
75 &fld
.cx
.parse_sess
.span_diagnostic
,
78 let placer
= fld
.fold_expr(placer
);
79 let value_expr
= fld
.fold_expr(value_expr
);
80 fld
.cx
.expr(span
, ast
::ExprKind
::InPlace(placer
, value_expr
))
81 .with_attrs(fold_thin_attrs(attrs
, fld
))
84 ast
::ExprKind
::While(cond
, body
, opt_ident
) => {
85 let cond
= fld
.fold_expr(cond
);
86 let (body
, opt_ident
) = expand_loop_block(body
, opt_ident
, fld
);
87 fld
.cx
.expr(span
, ast
::ExprKind
::While(cond
, body
, opt_ident
))
88 .with_attrs(fold_thin_attrs(attrs
, fld
))
91 ast
::ExprKind
::WhileLet(pat
, expr
, body
, opt_ident
) => {
92 let pat
= fld
.fold_pat(pat
);
93 let expr
= fld
.fold_expr(expr
);
95 // Hygienic renaming of the body.
96 let ((body
, opt_ident
), mut rewritten_pats
) =
97 rename_in_scope(vec
![pat
],
100 |rename_fld
, fld
, (body
, opt_ident
)| {
101 expand_loop_block(rename_fld
.fold_block(body
), opt_ident
, fld
)
103 assert
!(rewritten_pats
.len() == 1);
105 let wl
= ast
::ExprKind
::WhileLet(rewritten_pats
.remove(0), expr
, body
, opt_ident
);
106 fld
.cx
.expr(span
, wl
).with_attrs(fold_thin_attrs(attrs
, fld
))
109 ast
::ExprKind
::Loop(loop_block
, opt_ident
) => {
110 let (loop_block
, opt_ident
) = expand_loop_block(loop_block
, opt_ident
, fld
);
111 fld
.cx
.expr(span
, ast
::ExprKind
::Loop(loop_block
, opt_ident
))
112 .with_attrs(fold_thin_attrs(attrs
, fld
))
115 ast
::ExprKind
::ForLoop(pat
, head
, body
, opt_ident
) => {
116 let pat
= fld
.fold_pat(pat
);
118 // Hygienic renaming of the for loop body (for loop binds its pattern).
119 let ((body
, opt_ident
), mut rewritten_pats
) =
120 rename_in_scope(vec
![pat
],
123 |rename_fld
, fld
, (body
, opt_ident
)| {
124 expand_loop_block(rename_fld
.fold_block(body
), opt_ident
, fld
)
126 assert
!(rewritten_pats
.len() == 1);
128 let head
= fld
.fold_expr(head
);
129 let fl
= ast
::ExprKind
::ForLoop(rewritten_pats
.remove(0), head
, body
, opt_ident
);
130 fld
.cx
.expr(span
, fl
).with_attrs(fold_thin_attrs(attrs
, fld
))
133 ast
::ExprKind
::IfLet(pat
, sub_expr
, body
, else_opt
) => {
134 let pat
= fld
.fold_pat(pat
);
136 // Hygienic renaming of the body.
137 let (body
, mut rewritten_pats
) =
138 rename_in_scope(vec
![pat
],
141 |rename_fld
, fld
, body
| {
142 fld
.fold_block(rename_fld
.fold_block(body
))
144 assert
!(rewritten_pats
.len() == 1);
146 let else_opt
= else_opt
.map(|else_opt
| fld
.fold_expr(else_opt
));
147 let sub_expr
= fld
.fold_expr(sub_expr
);
148 let il
= ast
::ExprKind
::IfLet(rewritten_pats
.remove(0), sub_expr
, body
, else_opt
);
149 fld
.cx
.expr(span
, il
).with_attrs(fold_thin_attrs(attrs
, fld
))
152 ast
::ExprKind
::Closure(capture_clause
, fn_decl
, block
) => {
153 let (rewritten_fn_decl
, rewritten_block
)
154 = expand_and_rename_fn_decl_and_block(fn_decl
, block
, fld
);
155 let new_node
= ast
::ExprKind
::Closure(capture_clause
,
158 P(ast
::Expr
{id
:id
, node
: new_node
, span
: fld
.new_span(span
),
159 attrs
: fold_thin_attrs(attrs
, fld
)})
163 P(noop_fold_expr(ast
::Expr
{
173 /// Expand a (not-ident-style) macro invocation. Returns the result
174 /// of expansion and the mark which must be applied to the result.
175 /// Our current interface doesn't allow us to apply the mark to the
176 /// result until after calling make_expr, make_items, etc.
177 fn expand_mac_invoc
<T
, F
, G
>(mac
: ast
::Mac
,
181 fld
: &mut MacroExpander
)
183 F
: for<'a
> FnOnce(Box
<MacResult
+'a
>) -> Option
<T
>,
184 G
: FnOnce(T
, Mrk
) -> T
,
186 // it would almost certainly be cleaner to pass the whole
187 // macro invocation in, rather than pulling it apart and
188 // marking the tts and the ctxt separately. This also goes
189 // for the other three macro invocation chunks of code
192 let Mac_ { path: pth, tts, .. }
= mac
.node
;
193 if pth
.segments
.len() > 1 {
194 fld
.cx
.span_err(pth
.span
,
195 "expected macro name without module \
197 // let compilation continue
200 let extname
= pth
.segments
[0].identifier
.name
;
201 match fld
.cx
.syntax_env
.find(extname
) {
203 let mut err
= fld
.cx
.struct_span_err(
205 &format
!("macro undefined: '{}!'",
207 fld
.cx
.suggest_macro_name(&extname
.as_str(), pth
.span
, &mut err
);
210 // let compilation continue
213 Some(rc
) => match *rc
{
214 NormalTT(ref expandfun
, exp_span
, allow_internal_unstable
) => {
215 fld
.cx
.bt_push(ExpnInfo
{
217 callee
: NameAndSpan
{
218 format
: MacroBang(extname
),
220 allow_internal_unstable
: allow_internal_unstable
,
223 let fm
= fresh_mark();
224 let marked_before
= mark_tts(&tts
[..], fm
);
226 // The span that we pass to the expanders we want to
227 // be the root of the call stack. That's the most
228 // relevant span and it's the actual invocation of
230 let mac_span
= fld
.cx
.original_span();
233 let expanded
= expandfun
.expand(fld
.cx
,
236 parse_thunk(expanded
)
238 let parsed
= match opt_parsed
{
243 &format
!("non-expression macro in expression position: {}",
249 Some(mark_thunk(parsed
,fm
))
254 &format
!("'{}' is not a tt-style macro",
262 /// Rename loop label and expand its loop body
264 /// The renaming procedure for loop is different in the sense that the loop
265 /// body is in a block enclosed by loop head so the renaming of loop label
266 /// must be propagated to the enclosed context.
267 fn expand_loop_block(loop_block
: P
<Block
>,
268 opt_ident
: Option
<Ident
>,
269 fld
: &mut MacroExpander
) -> (P
<Block
>, Option
<Ident
>) {
272 let new_label
= fresh_name(label
);
273 let rename
= (label
, new_label
);
275 // The rename *must not* be added to the pending list of current
276 // syntax context otherwise an unrelated `break` or `continue` in
277 // the same context will pick that up in the deferred renaming pass
278 // and be renamed incorrectly.
279 let mut rename_list
= vec
!(rename
);
280 let mut rename_fld
= IdentRenamer{renames: &mut rename_list}
;
281 let renamed_ident
= rename_fld
.fold_ident(label
);
283 // The rename *must* be added to the enclosed syntax context for
284 // `break` or `continue` to pick up because by definition they are
285 // in a block enclosed by loop head.
286 fld
.cx
.syntax_env
.push_frame();
287 fld
.cx
.syntax_env
.info().pending_renames
.push(rename
);
288 let expanded_block
= expand_block_elts(loop_block
, fld
);
289 fld
.cx
.syntax_env
.pop_frame();
291 (expanded_block
, Some(renamed_ident
))
293 None
=> (fld
.fold_block(loop_block
), opt_ident
)
297 // eval $e with a new exts frame.
298 // must be a macro so that $e isn't evaluated too early.
299 macro_rules
! with_exts_frame
{
300 ($extsboxexpr
:expr
,$macros_escape
:expr
,$e
:expr
) =>
301 ({$extsboxexpr
.push_frame();
302 $extsboxexpr
.info().macros_escape
= $macros_escape
;
304 $extsboxexpr
.pop_frame();
309 // When we enter a module, record it, for the sake of `module!`
310 pub fn expand_item(it
: P
<ast
::Item
>, fld
: &mut MacroExpander
)
311 -> SmallVector
<P
<ast
::Item
>> {
312 let it
= expand_item_multi_modifier(Annotatable
::Item(it
), fld
);
314 expand_annotatable(it
, fld
)
315 .into_iter().map(|i
| i
.expect_item()).collect()
319 fn expand_item_kind(item
: ast
::ItemKind
, fld
: &mut MacroExpander
) -> ast
::ItemKind
{
321 ast
::ItemKind
::Fn(decl
, unsafety
, constness
, abi
, generics
, body
) => {
322 let (rewritten_fn_decl
, rewritten_body
)
323 = expand_and_rename_fn_decl_and_block(decl
, body
, fld
);
324 let expanded_generics
= fold
::noop_fold_generics(generics
,fld
);
325 ast
::ItemKind
::Fn(rewritten_fn_decl
, unsafety
, constness
, abi
,
326 expanded_generics
, rewritten_body
)
328 _
=> noop_fold_item_kind(item
, fld
)
332 // does this attribute list contain "macro_use" ?
333 fn contains_macro_use(fld
: &mut MacroExpander
, attrs
: &[ast
::Attribute
]) -> bool
{
335 let mut is_use
= attr
.check_name("macro_use");
336 if attr
.check_name("macro_escape") {
338 fld
.cx
.struct_span_warn(attr
.span
,
339 "macro_escape is a deprecated synonym for macro_use");
341 if let ast
::AttrStyle
::Inner
= attr
.node
.style
{
342 err
.fileline_help(attr
.span
, "consider an outer attribute, \
343 #[macro_use] mod ...").emit();
350 match attr
.node
.value
.node
{
351 ast
::MetaItemKind
::Word(..) => (),
352 _
=> fld
.cx
.span_err(attr
.span
, "arguments to macro_use are not allowed here"),
360 // Support for item-position macro invocations, exactly the same
361 // logic as for expression-position macro invocations.
362 pub fn expand_item_mac(it
: P
<ast
::Item
>,
363 fld
: &mut MacroExpander
) -> SmallVector
<P
<ast
::Item
>> {
364 let (extname
, path_span
, tts
, span
, attrs
, ident
) = it
.and_then(|it
| match it
.node
{
365 ItemKind
::Mac(codemap
::Spanned { node: Mac_ { path, tts, .. }
, .. }) =>
366 (path
.segments
[0].identifier
.name
, path
.span
, tts
, it
.span
, it
.attrs
, it
.ident
),
367 _
=> fld
.cx
.span_bug(it
.span
, "invalid item macro invocation")
370 let fm
= fresh_mark();
372 let expanded
= match fld
.cx
.syntax_env
.find(extname
) {
374 fld
.cx
.span_err(path_span
,
375 &format
!("macro undefined: '{}!'",
377 // let compilation continue
378 return SmallVector
::zero();
381 Some(rc
) => match *rc
{
382 NormalTT(ref expander
, tt_span
, allow_internal_unstable
) => {
383 if ident
.name
!= parse
::token
::special_idents
::invalid
.name
{
386 &format
!("macro {}! expects no ident argument, given '{}'",
389 return SmallVector
::zero();
391 fld
.cx
.bt_push(ExpnInfo
{
393 callee
: NameAndSpan
{
394 format
: MacroBang(extname
),
396 allow_internal_unstable
: allow_internal_unstable
,
399 // mark before expansion:
400 let marked_before
= mark_tts(&tts
[..], fm
);
401 expander
.expand(fld
.cx
, span
, &marked_before
[..])
403 IdentTT(ref expander
, tt_span
, allow_internal_unstable
) => {
404 if ident
.name
== parse
::token
::special_idents
::invalid
.name
{
405 fld
.cx
.span_err(path_span
,
406 &format
!("macro {}! expects an ident argument",
408 return SmallVector
::zero();
410 fld
.cx
.bt_push(ExpnInfo
{
412 callee
: NameAndSpan
{
413 format
: MacroBang(extname
),
415 allow_internal_unstable
: allow_internal_unstable
,
418 // mark before expansion:
419 let marked_tts
= mark_tts(&tts
[..], fm
);
420 expander
.expand(fld
.cx
, span
, ident
, marked_tts
)
423 if ident
.name
== parse
::token
::special_idents
::invalid
.name
{
424 fld
.cx
.span_err(path_span
, "macro_rules! expects an ident argument");
425 return SmallVector
::zero();
428 fld
.cx
.bt_push(ExpnInfo
{
430 callee
: NameAndSpan
{
431 format
: MacroBang(extname
),
433 // `macro_rules!` doesn't directly allow
434 // unstable (this is orthogonal to whether
435 // the macro it creates allows it)
436 allow_internal_unstable
: false,
439 // DON'T mark before expansion.
441 let allow_internal_unstable
= attr
::contains_name(&attrs
,
442 "allow_internal_unstable");
444 // ensure any #[allow_internal_unstable]s are
445 // detected (including nested macro definitions
447 if allow_internal_unstable
&& !fld
.cx
.ecfg
.enable_allow_internal_unstable() {
448 feature_gate
::emit_feature_err(
449 &fld
.cx
.parse_sess
.span_diagnostic
,
450 "allow_internal_unstable",
452 feature_gate
::GateIssue
::Language
,
453 feature_gate
::EXPLAIN_ALLOW_INTERNAL_UNSTABLE
)
456 let export
= attr
::contains_name(&attrs
, "macro_export");
457 let def
= ast
::MacroDef
{
460 id
: ast
::DUMMY_NODE_ID
,
465 allow_internal_unstable
: allow_internal_unstable
,
468 fld
.cx
.insert_macro(def
);
470 // macro_rules! has a side effect but expands to nothing.
472 return SmallVector
::zero();
475 fld
.cx
.span_err(span
,
476 &format
!("{}! is not legal in item position",
478 return SmallVector
::zero();
483 expanded
.make_items()
486 let items
= match items
{
489 .map(|i
| mark_item(i
, fm
))
490 .flat_map(|i
| fld
.fold_item(i
).into_iter())
494 fld
.cx
.span_err(path_span
,
495 &format
!("non-item macro in item position: {}",
497 return SmallVector
::zero();
506 fn expand_stmt(stmt
: Stmt
, fld
: &mut MacroExpander
) -> SmallVector
<Stmt
> {
507 let (mac
, style
, attrs
) = match stmt
.node
{
508 StmtKind
::Mac(mac
, style
, attrs
) => (mac
, style
, attrs
),
509 _
=> return expand_non_macro_stmt(stmt
, fld
)
512 // Assert that we drop any macro attributes on the floor here
515 let maybe_new_items
=
516 expand_mac_invoc(mac
.unwrap(), stmt
.span
,
518 |stmts
, mark
| stmts
.move_map(|m
| mark_stmt(m
, mark
)),
521 let mut fully_expanded
= match maybe_new_items
{
523 // Keep going, outside-in.
524 let new_items
= stmts
.into_iter().flat_map(|s
| {
525 fld
.fold_stmt(s
).into_iter()
530 None
=> SmallVector
::zero()
533 // If this is a macro invocation with a semicolon, then apply that
534 // semicolon to the final statement produced by expansion.
535 if style
== MacStmtStyle
::Semicolon
{
536 if let Some(stmt
) = fully_expanded
.pop() {
537 let new_stmt
= Spanned
{
538 node
: match stmt
.node
{
539 StmtKind
::Expr(e
, stmt_id
) => StmtKind
::Semi(e
, stmt_id
),
540 _
=> stmt
.node
/* might already have a semi */
544 fully_expanded
.push(new_stmt
);
551 // expand a non-macro stmt. this is essentially the fallthrough for
552 // expand_stmt, above.
553 fn expand_non_macro_stmt(Spanned {node, span: stmt_span}
: Stmt
, fld
: &mut MacroExpander
)
554 -> SmallVector
<Stmt
> {
557 StmtKind
::Decl(decl
, node_id
) => decl
.and_then(|Spanned {node: decl, span}
| match decl
{
558 DeclKind
::Local(local
) => {
560 let rewritten_local
= local
.map(|Local {id, pat, ty, init, span, attrs}
| {
561 // expand the ty since TyKind::FixedLengthVec contains an Expr
562 // and thus may have a macro use
563 let expanded_ty
= ty
.map(|t
| fld
.fold_ty(t
));
564 // expand the pat (it might contain macro uses):
565 let expanded_pat
= fld
.fold_pat(pat
);
566 // find the PatIdents in the pattern:
567 // oh dear heaven... this is going to include the enum
568 // names, as well... but that should be okay, as long as
569 // the new names are gensyms for the old ones.
570 // generate fresh names, push them to a new pending list
571 let idents
= pattern_bindings(&expanded_pat
);
572 let mut new_pending_renames
=
573 idents
.iter().map(|ident
| (*ident
, fresh_name(*ident
))).collect();
574 // rewrite the pattern using the new names (the old
575 // ones have already been applied):
576 let rewritten_pat
= {
577 // nested binding to allow borrow to expire:
578 let mut rename_fld
= IdentRenamer{renames: &mut new_pending_renames}
;
579 rename_fld
.fold_pat(expanded_pat
)
581 // add them to the existing pending renames:
582 fld
.cx
.syntax_env
.info().pending_renames
583 .extend(new_pending_renames
);
588 // also, don't forget to expand the init:
589 init
: init
.map(|e
| fld
.fold_expr(e
)),
591 attrs
: fold
::fold_thin_attrs(attrs
, fld
),
594 SmallVector
::one(Spanned
{
595 node
: StmtKind
::Decl(P(Spanned
{
596 node
: DeclKind
::Local(rewritten_local
),
604 noop_fold_stmt(Spanned
{
605 node
: StmtKind
::Decl(P(Spanned
{
615 noop_fold_stmt(Spanned
{
623 // expand the arm of a 'match', renaming for macro hygiene
624 fn expand_arm(arm
: ast
::Arm
, fld
: &mut MacroExpander
) -> ast
::Arm
{
625 // expand pats... they might contain macro uses:
626 let expanded_pats
= arm
.pats
.move_map(|pat
| fld
.fold_pat(pat
));
627 if expanded_pats
.is_empty() {
628 panic
!("encountered match arm with 0 patterns");
631 // apply renaming and then expansion to the guard and the body:
632 let ((rewritten_guard
, rewritten_body
), rewritten_pats
) =
633 rename_in_scope(expanded_pats
,
635 (arm
.guard
, arm
.body
),
636 |rename_fld
, fld
, (ag
, ab
)|{
637 let rewritten_guard
= ag
.map(|g
| fld
.fold_expr(rename_fld
.fold_expr(g
)));
638 let rewritten_body
= fld
.fold_expr(rename_fld
.fold_expr(ab
));
639 (rewritten_guard
, rewritten_body
)
643 attrs
: fold
::fold_attrs(arm
.attrs
, fld
),
644 pats
: rewritten_pats
,
645 guard
: rewritten_guard
,
646 body
: rewritten_body
,
650 fn rename_in_scope
<X
, F
>(pats
: Vec
<P
<ast
::Pat
>>,
651 fld
: &mut MacroExpander
,
654 -> (X
, Vec
<P
<ast
::Pat
>>)
655 where F
: Fn(&mut IdentRenamer
, &mut MacroExpander
, X
) -> X
657 // all of the pats must have the same set of bindings, so use the
658 // first one to extract them and generate new names:
659 let idents
= pattern_bindings(&pats
[0]);
660 let new_renames
= idents
.into_iter().map(|id
| (id
, fresh_name(id
))).collect();
661 // apply the renaming, but only to the PatIdents:
662 let mut rename_pats_fld
= PatIdentRenamer{renames:&new_renames}
;
663 let rewritten_pats
= pats
.move_map(|pat
| rename_pats_fld
.fold_pat(pat
));
665 let mut rename_fld
= IdentRenamer{ renames:&new_renames }
;
666 (f(&mut rename_fld
, fld
, x
), rewritten_pats
)
669 /// A visitor that extracts the PatKind::Ident (binding) paths
670 /// from a given thingy and puts them in a mutable
673 struct PatIdentFinder
{
674 ident_accumulator
: Vec
<ast
::Ident
>
677 impl<'v
> Visitor
<'v
> for PatIdentFinder
{
678 fn visit_pat(&mut self, pattern
: &ast
::Pat
) {
680 ast
::Pat { id: _, node: PatKind::Ident(_, ref path1, ref inner), span: _ }
=> {
681 self.ident_accumulator
.push(path1
.node
);
682 // visit optional subpattern of PatKind::Ident:
683 if let Some(ref subpat
) = *inner
{
684 self.visit_pat(subpat
)
687 // use the default traversal for non-PatIdents
688 _
=> visit
::walk_pat(self, pattern
)
693 /// find the PatKind::Ident paths in a pattern
694 fn pattern_bindings(pat
: &ast
::Pat
) -> Vec
<ast
::Ident
> {
695 let mut name_finder
= PatIdentFinder{ident_accumulator:Vec::new()}
;
696 name_finder
.visit_pat(pat
);
697 name_finder
.ident_accumulator
700 /// find the PatKind::Ident paths in a
701 fn fn_decl_arg_bindings(fn_decl
: &ast
::FnDecl
) -> Vec
<ast
::Ident
> {
702 let mut pat_idents
= PatIdentFinder{ident_accumulator:Vec::new()}
;
703 for arg
in &fn_decl
.inputs
{
704 pat_idents
.visit_pat(&arg
.pat
);
706 pat_idents
.ident_accumulator
709 // expand a block. pushes a new exts_frame, then calls expand_block_elts
710 pub fn expand_block(blk
: P
<Block
>, fld
: &mut MacroExpander
) -> P
<Block
> {
711 // see note below about treatment of exts table
712 with_exts_frame
!(fld
.cx
.syntax_env
,false,
713 expand_block_elts(blk
, fld
))
716 // expand the elements of a block.
717 pub fn expand_block_elts(b
: P
<Block
>, fld
: &mut MacroExpander
) -> P
<Block
> {
718 b
.map(|Block {id, stmts, expr, rules, span}
| {
719 let new_stmts
= stmts
.into_iter().flat_map(|x
| {
720 // perform all pending renames
722 let pending_renames
= &mut fld
.cx
.syntax_env
.info().pending_renames
;
723 let mut rename_fld
= IdentRenamer{renames:pending_renames}
;
724 rename_fld
.fold_stmt(x
).expect_one("rename_fold didn't return one value")
726 // expand macros in the statement
727 fld
.fold_stmt(renamed_stmt
).into_iter()
729 let new_expr
= expr
.map(|x
| {
731 let pending_renames
= &mut fld
.cx
.syntax_env
.info().pending_renames
;
732 let mut rename_fld
= IdentRenamer{renames:pending_renames}
;
733 rename_fld
.fold_expr(x
)
747 fn expand_pat(p
: P
<ast
::Pat
>, fld
: &mut MacroExpander
) -> P
<ast
::Pat
> {
749 PatKind
::Mac(_
) => {}
750 _
=> return noop_fold_pat(p
, fld
)
752 p
.map(|ast
::Pat {node, span, ..}
| {
753 let (pth
, tts
) = match node
{
754 PatKind
::Mac(mac
) => (mac
.node
.path
, mac
.node
.tts
),
757 if pth
.segments
.len() > 1 {
758 fld
.cx
.span_err(pth
.span
, "expected macro name without module separators");
759 return DummyResult
::raw_pat(span
);
761 let extname
= pth
.segments
[0].identifier
.name
;
762 let marked_after
= match fld
.cx
.syntax_env
.find(extname
) {
764 fld
.cx
.span_err(pth
.span
,
765 &format
!("macro undefined: '{}!'",
767 // let compilation continue
768 return DummyResult
::raw_pat(span
);
771 Some(rc
) => match *rc
{
772 NormalTT(ref expander
, tt_span
, allow_internal_unstable
) => {
773 fld
.cx
.bt_push(ExpnInfo
{
775 callee
: NameAndSpan
{
776 format
: MacroBang(extname
),
778 allow_internal_unstable
: allow_internal_unstable
,
782 let fm
= fresh_mark();
783 let marked_before
= mark_tts(&tts
[..], fm
);
784 let mac_span
= fld
.cx
.original_span();
785 let pat
= expander
.expand(fld
.cx
,
787 &marked_before
[..]).make_pat();
788 let expanded
= match pat
{
794 "non-pattern macro in pattern position: {}",
798 return DummyResult
::raw_pat(span
);
803 mark_pat(expanded
,fm
)
806 fld
.cx
.span_err(span
,
807 &format
!("{}! is not legal in pattern position",
809 return DummyResult
::raw_pat(span
);
815 fld
.fold_pat(marked_after
).node
.clone();
819 id
: ast
::DUMMY_NODE_ID
,
820 node
: fully_expanded
,
826 /// A tree-folder that applies every rename in its (mutable) list
827 /// to every identifier, including both bindings and varrefs
828 /// (and lots of things that will turn out to be neither)
829 pub struct IdentRenamer
<'a
> {
830 renames
: &'a mtwt
::RenameList
,
833 impl<'a
> Folder
for IdentRenamer
<'a
> {
834 fn fold_ident(&mut self, id
: Ident
) -> Ident
{
835 Ident
::new(id
.name
, mtwt
::apply_renames(self.renames
, id
.ctxt
))
837 fn fold_mac(&mut self, mac
: ast
::Mac
) -> ast
::Mac
{
838 fold
::noop_fold_mac(mac
, self)
842 /// A tree-folder that applies every rename in its list to
843 /// the idents that are in PatKind::Ident patterns. This is more narrowly
844 /// focused than IdentRenamer, and is needed for FnDecl,
845 /// where we want to rename the args but not the fn name or the generics etc.
846 pub struct PatIdentRenamer
<'a
> {
847 renames
: &'a mtwt
::RenameList
,
850 impl<'a
> Folder
for PatIdentRenamer
<'a
> {
851 fn fold_pat(&mut self, pat
: P
<ast
::Pat
>) -> P
<ast
::Pat
> {
853 PatKind
::Ident(..) => {}
,
854 _
=> return noop_fold_pat(pat
, self)
857 pat
.map(|ast
::Pat {id, node, span}
| match node
{
858 PatKind
::Ident(binding_mode
, Spanned{span: sp, node: ident}
, sub
) => {
859 let new_ident
= Ident
::new(ident
.name
,
860 mtwt
::apply_renames(self.renames
, ident
.ctxt
));
862 PatKind
::Ident(binding_mode
,
863 Spanned{span: self.new_span(sp), node: new_ident}
,
864 sub
.map(|p
| self.fold_pat(p
)));
868 span
: self.new_span(span
)
874 fn fold_mac(&mut self, mac
: ast
::Mac
) -> ast
::Mac
{
875 fold
::noop_fold_mac(mac
, self)
879 fn expand_annotatable(a
: Annotatable
,
880 fld
: &mut MacroExpander
)
881 -> SmallVector
<Annotatable
> {
882 let a
= expand_item_multi_modifier(a
, fld
);
884 let mut decorator_items
= SmallVector
::zero();
885 let mut new_attrs
= Vec
::new();
886 expand_decorators(a
.clone(), fld
, &mut decorator_items
, &mut new_attrs
);
888 let mut new_items
: SmallVector
<Annotatable
> = match a
{
889 Annotatable
::Item(it
) => match it
.node
{
890 ast
::ItemKind
::Mac(..) => {
891 expand_item_mac(it
, fld
).into_iter().map(|i
| Annotatable
::Item(i
)).collect()
893 ast
::ItemKind
::Mod(_
) | ast
::ItemKind
::ForeignMod(_
) => {
895 it
.ident
.name
!= parse
::token
::special_idents
::invalid
.name
;
898 fld
.cx
.mod_push(it
.ident
);
900 let macro_use
= contains_macro_use(fld
, &new_attrs
[..]);
901 let result
= with_exts_frame
!(fld
.cx
.syntax_env
,
903 noop_fold_item(it
, fld
));
907 result
.into_iter().map(|i
| Annotatable
::Item(i
)).collect()
910 let it
= P(ast
::Item
{
914 noop_fold_item(it
, fld
).into_iter().map(|i
| Annotatable
::Item(i
)).collect()
918 Annotatable
::TraitItem(it
) => match it
.node
{
919 ast
::TraitItemKind
::Method(_
, Some(_
)) => {
920 let ti
= it
.unwrap();
921 SmallVector
::one(ast
::TraitItem
{
925 node
: match ti
.node
{
926 ast
::TraitItemKind
::Method(sig
, Some(body
)) => {
927 let (sig
, body
) = expand_and_rename_method(sig
, body
, fld
);
928 ast
::TraitItemKind
::Method(sig
, Some(body
))
932 span
: fld
.new_span(ti
.span
)
935 _
=> fold
::noop_fold_trait_item(it
.unwrap(), fld
)
936 }.into_iter().map(|ti
| Annotatable
::TraitItem(P(ti
))).collect(),
938 Annotatable
::ImplItem(ii
) => {
939 expand_impl_item(ii
.unwrap(), fld
).into_iter().
940 map(|ii
| Annotatable
::ImplItem(P(ii
))).collect()
944 new_items
.push_all(decorator_items
);
948 // Partition a set of attributes into one kind of attribute, and other kinds.
949 macro_rules
! partition
{
950 ($fn_name
: ident
, $variant
: ident
) => {
951 #[allow(deprecated)] // The `allow` is needed because the `Modifier` variant might be used.
952 fn $
fn_name(attrs
: &[ast
::Attribute
],
954 -> (Vec
<ast
::Attribute
>, Vec
<ast
::Attribute
>) {
955 attrs
.iter().cloned().partition(|attr
| {
956 match fld
.cx
.syntax_env
.find(intern(&attr
.name())) {
957 Some(rc
) => match *rc
{
958 $
variant(..) => true,
968 partition
!(multi_modifiers
, MultiModifier
);
971 fn expand_decorators(a
: Annotatable
,
972 fld
: &mut MacroExpander
,
973 decorator_items
: &mut SmallVector
<Annotatable
>,
974 new_attrs
: &mut Vec
<ast
::Attribute
>)
976 for attr
in a
.attrs() {
977 let mname
= intern(&attr
.name());
978 match fld
.cx
.syntax_env
.find(mname
) {
979 Some(rc
) => match *rc
{
980 MultiDecorator(ref dec
) => {
981 attr
::mark_used(&attr
);
983 fld
.cx
.bt_push(ExpnInfo
{
984 call_site
: attr
.span
,
985 callee
: NameAndSpan
{
986 format
: MacroAttribute(mname
),
987 span
: Some(attr
.span
),
988 // attributes can do whatever they like,
990 allow_internal_unstable
: true,
994 // we'd ideally decorator_items.push_all(expand_annotatable(ann, fld)),
995 // but that double-mut-borrows fld
996 let mut items
: SmallVector
<Annotatable
> = SmallVector
::zero();
1001 &mut |ann
| items
.push(ann
));
1002 decorator_items
.extend(items
.into_iter()
1003 .flat_map(|ann
| expand_annotatable(ann
, fld
).into_iter()));
1007 _
=> new_attrs
.push((*attr
).clone()),
1009 _
=> new_attrs
.push((*attr
).clone()),
1014 fn expand_item_multi_modifier(mut it
: Annotatable
,
1015 fld
: &mut MacroExpander
)
1017 let (modifiers
, other_attrs
) = multi_modifiers(it
.attrs(), fld
);
1019 // Update the attrs, leave everything else alone. Is this mutation really a good idea?
1020 it
= it
.fold_attrs(other_attrs
);
1022 if modifiers
.is_empty() {
1026 for attr
in &modifiers
{
1027 let mname
= intern(&attr
.name());
1029 match fld
.cx
.syntax_env
.find(mname
) {
1030 Some(rc
) => match *rc
{
1031 MultiModifier(ref mac
) => {
1032 attr
::mark_used(attr
);
1033 fld
.cx
.bt_push(ExpnInfo
{
1034 call_site
: attr
.span
,
1035 callee
: NameAndSpan
{
1036 format
: MacroAttribute(mname
),
1037 span
: Some(attr
.span
),
1038 // attributes can do whatever they like,
1040 allow_internal_unstable
: true,
1043 it
= mac
.expand(fld
.cx
, attr
.span
, &attr
.node
.value
, it
);
1052 // Expansion may have added new ItemKind::Modifiers.
1053 expand_item_multi_modifier(it
, fld
)
1056 fn expand_impl_item(ii
: ast
::ImplItem
, fld
: &mut MacroExpander
)
1057 -> SmallVector
<ast
::ImplItem
> {
1059 ast
::ImplItemKind
::Method(..) => SmallVector
::one(ast
::ImplItem
{
1064 defaultness
: ii
.defaultness
,
1065 node
: match ii
.node
{
1066 ast
::ImplItemKind
::Method(sig
, body
) => {
1067 let (sig
, body
) = expand_and_rename_method(sig
, body
, fld
);
1068 ast
::ImplItemKind
::Method(sig
, body
)
1072 span
: fld
.new_span(ii
.span
)
1074 ast
::ImplItemKind
::Macro(_
) => {
1075 let (span
, mac
) = match ii
.node
{
1076 ast
::ImplItemKind
::Macro(mac
) => (ii
.span
, mac
),
1079 let maybe_new_items
=
1080 expand_mac_invoc(mac
, span
,
1081 |r
| r
.make_impl_items(),
1082 |meths
, mark
| meths
.move_map(|m
| mark_impl_item(m
, mark
)),
1085 match maybe_new_items
{
1086 Some(impl_items
) => {
1087 // expand again if necessary
1088 let new_items
= impl_items
.into_iter().flat_map(|ii
| {
1089 expand_impl_item(ii
, fld
).into_iter()
1094 None
=> SmallVector
::zero()
1097 _
=> fold
::noop_fold_impl_item(ii
, fld
)
1101 /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
1102 /// PatIdents in its arguments to perform renaming in the FnDecl and
1103 /// the block, returning both the new FnDecl and the new Block.
1104 fn expand_and_rename_fn_decl_and_block(fn_decl
: P
<ast
::FnDecl
>, block
: P
<ast
::Block
>,
1105 fld
: &mut MacroExpander
)
1106 -> (P
<ast
::FnDecl
>, P
<ast
::Block
>) {
1107 let expanded_decl
= fld
.fold_fn_decl(fn_decl
);
1108 let idents
= fn_decl_arg_bindings(&expanded_decl
);
1110 idents
.iter().map(|id
| (*id
,fresh_name(*id
))).collect();
1111 // first, a renamer for the PatIdents, for the fn_decl:
1112 let mut rename_pat_fld
= PatIdentRenamer{renames: &renames}
;
1113 let rewritten_fn_decl
= rename_pat_fld
.fold_fn_decl(expanded_decl
);
1114 // now, a renamer for *all* idents, for the body:
1115 let mut rename_fld
= IdentRenamer{renames: &renames}
;
1116 let rewritten_body
= fld
.fold_block(rename_fld
.fold_block(block
));
1117 (rewritten_fn_decl
,rewritten_body
)
1120 fn expand_and_rename_method(sig
: ast
::MethodSig
, body
: P
<ast
::Block
>,
1121 fld
: &mut MacroExpander
)
1122 -> (ast
::MethodSig
, P
<ast
::Block
>) {
1123 let (rewritten_fn_decl
, rewritten_body
)
1124 = expand_and_rename_fn_decl_and_block(sig
.decl
, body
, fld
);
1126 generics
: fld
.fold_generics(sig
.generics
),
1128 explicit_self
: fld
.fold_explicit_self(sig
.explicit_self
),
1129 unsafety
: sig
.unsafety
,
1130 constness
: sig
.constness
,
1131 decl
: rewritten_fn_decl
1135 pub fn expand_type(t
: P
<ast
::Ty
>, fld
: &mut MacroExpander
) -> P
<ast
::Ty
> {
1136 let t
= match t
.node
.clone() {
1137 ast
::TyKind
::Mac(mac
) => {
1138 if fld
.cx
.ecfg
.features
.unwrap().type_macros
{
1139 let expanded_ty
= match expand_mac_invoc(mac
, t
.span
,
1145 return DummyResult
::raw_ty(t
.span
);
1149 // Keep going, outside-in.
1150 let fully_expanded
= fld
.fold_ty(expanded_ty
);
1153 fully_expanded
.map(|t
| ast
::Ty
{
1154 id
: ast
::DUMMY_NODE_ID
,
1159 feature_gate
::emit_feature_err(
1160 &fld
.cx
.parse_sess
.span_diagnostic
,
1163 feature_gate
::GateIssue
::Language
,
1164 "type macros are experimental");
1166 DummyResult
::raw_ty(t
.span
)
1172 fold
::noop_fold_ty(t
, fld
)
1175 /// A tree-folder that performs macro expansion
1176 pub struct MacroExpander
<'a
, 'b
:'a
> {
1177 pub cx
: &'a
mut ExtCtxt
<'b
>,
1180 impl<'a
, 'b
> MacroExpander
<'a
, 'b
> {
1181 pub fn new(cx
: &'a
mut ExtCtxt
<'b
>) -> MacroExpander
<'a
, 'b
> {
1182 MacroExpander { cx: cx }
1186 impl<'a
, 'b
> Folder
for MacroExpander
<'a
, 'b
> {
1187 fn fold_crate(&mut self, c
: Crate
) -> Crate
{
1188 self.cx
.filename
= Some(self.cx
.parse_sess
.codemap().span_to_filename(c
.span
));
1189 noop_fold_crate(c
, self)
1192 fn fold_expr(&mut self, expr
: P
<ast
::Expr
>) -> P
<ast
::Expr
> {
1193 expand_expr(expr
, self)
1196 fn fold_pat(&mut self, pat
: P
<ast
::Pat
>) -> P
<ast
::Pat
> {
1197 expand_pat(pat
, self)
1200 fn fold_item(&mut self, item
: P
<ast
::Item
>) -> SmallVector
<P
<ast
::Item
>> {
1201 use std
::mem
::replace
;
1203 if let ast
::ItemKind
::Mod(ast
::Mod { inner, .. }
) = item
.node
{
1204 if item
.span
.contains(inner
) {
1205 self.push_mod_path(item
.ident
, &item
.attrs
);
1206 result
= expand_item(item
, self);
1207 self.pop_mod_path();
1209 let filename
= if inner
!= codemap
::DUMMY_SP
{
1210 Some(self.cx
.parse_sess
.codemap().span_to_filename(inner
))
1212 let orig_filename
= replace(&mut self.cx
.filename
, filename
);
1213 let orig_mod_path_stack
= replace(&mut self.cx
.mod_path_stack
, Vec
::new());
1214 result
= expand_item(item
, self);
1215 self.cx
.filename
= orig_filename
;
1216 self.cx
.mod_path_stack
= orig_mod_path_stack
;
1219 result
= expand_item(item
, self);
1224 fn fold_item_kind(&mut self, item
: ast
::ItemKind
) -> ast
::ItemKind
{
1225 expand_item_kind(item
, self)
1228 fn fold_stmt(&mut self, stmt
: ast
::Stmt
) -> SmallVector
<ast
::Stmt
> {
1229 expand_stmt(stmt
, self)
1232 fn fold_block(&mut self, block
: P
<Block
>) -> P
<Block
> {
1233 let was_in_block
= ::std
::mem
::replace(&mut self.cx
.in_block
, true);
1234 let result
= expand_block(block
, self);
1235 self.cx
.in_block
= was_in_block
;
1239 fn fold_arm(&mut self, arm
: ast
::Arm
) -> ast
::Arm
{
1240 expand_arm(arm
, self)
1243 fn fold_trait_item(&mut self, i
: ast
::TraitItem
) -> SmallVector
<ast
::TraitItem
> {
1244 expand_annotatable(Annotatable
::TraitItem(P(i
)), self)
1245 .into_iter().map(|i
| i
.expect_trait_item()).collect()
1248 fn fold_impl_item(&mut self, i
: ast
::ImplItem
) -> SmallVector
<ast
::ImplItem
> {
1249 expand_annotatable(Annotatable
::ImplItem(P(i
)), self)
1250 .into_iter().map(|i
| i
.expect_impl_item()).collect()
1253 fn fold_ty(&mut self, ty
: P
<ast
::Ty
>) -> P
<ast
::Ty
> {
1254 expand_type(ty
, self)
1257 fn new_span(&mut self, span
: Span
) -> Span
{
1258 new_span(self.cx
, span
)
1262 impl<'a
, 'b
> MacroExpander
<'a
, 'b
> {
1263 fn push_mod_path(&mut self, id
: Ident
, attrs
: &[ast
::Attribute
]) {
1264 let default_path
= id
.name
.as_str();
1265 let file_path
= match ::attr
::first_attr_value_str_by_name(attrs
, "path") {
1267 None
=> default_path
,
1269 self.cx
.mod_path_stack
.push(file_path
)
1272 fn pop_mod_path(&mut self) {
1273 self.cx
.mod_path_stack
.pop().unwrap();
1277 fn new_span(cx
: &ExtCtxt
, sp
: Span
) -> Span
{
1278 debug
!("new_span(sp={:?})", sp
);
1280 if cx
.codemap().more_specific_trace(sp
.expn_id
, cx
.backtrace()) {
1281 // If the span we are looking at has a backtrace that has more
1282 // detail than our current backtrace, then we keep that
1283 // backtrace. Honestly, I have no idea if this makes sense,
1284 // because I have no idea why we are stripping the backtrace
1285 // below. But the reason I made this change is because, in
1286 // deriving, we were generating attributes with a specific
1287 // backtrace, which was essential for `#[structural_match]` to
1288 // be properly supported, but these backtraces were being
1289 // stripped and replaced with a null backtrace. Sort of
1290 // unclear why this is the case. --nmatsakis
1291 debug
!("new_span: keeping trace from {:?} because it is more specific",
1295 // This discards information in the case of macro-defining macros.
1297 // The comment above was originally added in
1298 // b7ec2488ff2f29681fe28691d20fd2c260a9e454 in Feb 2012. I
1299 // *THINK* the reason we are doing this is because we want to
1300 // replace the backtrace of the macro contents with the
1301 // backtrace that contains the macro use. But it's pretty
1302 // unclear to me. --nmatsakis
1306 expn_id
: cx
.backtrace(),
1308 debug
!("new_span({:?}) = {:?}", sp
, sp1
);
1309 if sp
.expn_id
.into_u32() == 0 && env
::var_os("NDM").is_some() {
1316 pub struct ExpansionConfig
<'feat
> {
1317 pub crate_name
: String
,
1318 pub features
: Option
<&'feat Features
>,
1319 pub recursion_limit
: usize,
1320 pub trace_mac
: bool
,
1323 macro_rules
! feature_tests
{
1324 ($
( fn $getter
:ident
= $field
:ident
, )*) => {
1326 pub fn $
getter(&self) -> bool
{
1327 match self.features
{
1328 Some(&Features { $field: true, .. }
) => true,
1336 impl<'feat
> ExpansionConfig
<'feat
> {
1337 pub fn default(crate_name
: String
) -> ExpansionConfig
<'
static> {
1339 crate_name
: crate_name
,
1341 recursion_limit
: 64,
1347 fn enable_quotes
= allow_quote
,
1348 fn enable_asm
= allow_asm
,
1349 fn enable_log_syntax
= allow_log_syntax
,
1350 fn enable_concat_idents
= allow_concat_idents
,
1351 fn enable_trace_macros
= allow_trace_macros
,
1352 fn enable_allow_internal_unstable
= allow_internal_unstable
,
1353 fn enable_custom_derive
= allow_custom_derive
,
1354 fn enable_pushpop_unsafe
= allow_pushpop_unsafe
,
1358 pub fn expand_crate(mut cx
: ExtCtxt
,
1359 // these are the macros being imported to this crate:
1360 imported_macros
: Vec
<ast
::MacroDef
>,
1361 user_exts
: Vec
<NamedSyntaxExtension
>,
1362 c
: Crate
) -> (Crate
, HashSet
<Name
>) {
1363 if std_inject
::no_core(&c
) {
1364 cx
.crate_root
= None
;
1365 } else if std_inject
::no_std(&c
) {
1366 cx
.crate_root
= Some("core");
1368 cx
.crate_root
= Some("std");
1371 let mut expander
= MacroExpander
::new(&mut cx
);
1373 for def
in imported_macros
{
1374 expander
.cx
.insert_macro(def
);
1377 for (name
, extension
) in user_exts
{
1378 expander
.cx
.syntax_env
.insert(name
, extension
);
1381 let err_count
= cx
.parse_sess
.span_diagnostic
.err_count();
1382 let mut ret
= expander
.fold_crate(c
);
1383 ret
.exported_macros
= expander
.cx
.exported_macros
.clone();
1385 if cx
.parse_sess
.span_diagnostic
.err_count() > err_count
{
1386 cx
.parse_sess
.span_diagnostic
.abort_if_errors();
1391 return (ret
, cx
.syntax_env
.names
);
1394 // HYGIENIC CONTEXT EXTENSION:
1395 // all of these functions are for walking over
1396 // ASTs and making some change to the context of every
1397 // element that has one. a CtxtFn is a trait-ified
1398 // version of a closure in (SyntaxContext -> SyntaxContext).
1399 // the ones defined here include:
1400 // Marker - add a mark to a context
1402 // A Marker adds the given mark to the syntax context
1403 struct Marker { mark: Mrk }
1405 impl Folder
for Marker
{
1406 fn fold_ident(&mut self, id
: Ident
) -> Ident
{
1407 ast
::Ident
::new(id
.name
, mtwt
::apply_mark(self.mark
, id
.ctxt
))
1409 fn fold_mac(&mut self, Spanned {node, span}
: ast
::Mac
) -> ast
::Mac
{
1412 path
: self.fold_path(node
.path
),
1413 tts
: self.fold_tts(&node
.tts
),
1414 ctxt
: mtwt
::apply_mark(self.mark
, node
.ctxt
),
1421 // apply a given mark to the given token trees. Used prior to expansion of a macro.
1422 fn mark_tts(tts
: &[TokenTree
], m
: Mrk
) -> Vec
<TokenTree
> {
1423 noop_fold_tts(tts
, &mut Marker{mark:m}
)
1426 // apply a given mark to the given expr. Used following the expansion of a macro.
1427 fn mark_expr(expr
: P
<ast
::Expr
>, m
: Mrk
) -> P
<ast
::Expr
> {
1428 Marker{mark:m}
.fold_expr(expr
)
1431 // apply a given mark to the given pattern. Used following the expansion of a macro.
1432 fn mark_pat(pat
: P
<ast
::Pat
>, m
: Mrk
) -> P
<ast
::Pat
> {
1433 Marker{mark:m}
.fold_pat(pat
)
1436 // apply a given mark to the given stmt. Used following the expansion of a macro.
1437 fn mark_stmt(stmt
: ast
::Stmt
, m
: Mrk
) -> ast
::Stmt
{
1438 Marker{mark:m}
.fold_stmt(stmt
)
1439 .expect_one("marking a stmt didn't return exactly one stmt")
1442 // apply a given mark to the given item. Used following the expansion of a macro.
1443 fn mark_item(expr
: P
<ast
::Item
>, m
: Mrk
) -> P
<ast
::Item
> {
1444 Marker{mark:m}
.fold_item(expr
)
1445 .expect_one("marking an item didn't return exactly one item")
1448 // apply a given mark to the given item. Used following the expansion of a macro.
1449 fn mark_impl_item(ii
: ast
::ImplItem
, m
: Mrk
) -> ast
::ImplItem
{
1450 Marker{mark:m}
.fold_impl_item(ii
)
1451 .expect_one("marking an impl item didn't return exactly one impl item")
1454 fn mark_ty(ty
: P
<ast
::Ty
>, m
: Mrk
) -> P
<ast
::Ty
> {
1455 Marker { mark: m }
.fold_ty(ty
)
1458 /// Check that there are no macro invocations left in the AST:
1459 pub fn check_for_macros(sess
: &parse
::ParseSess
, krate
: &ast
::Crate
) {
1460 visit
::walk_crate(&mut MacroExterminator{sess:sess}
, krate
);
1463 /// A visitor that ensures that no macro invocations remain in an AST.
1464 struct MacroExterminator
<'a
>{
1465 sess
: &'a parse
::ParseSess
1468 impl<'a
, 'v
> Visitor
<'v
> for MacroExterminator
<'a
> {
1469 fn visit_mac(&mut self, mac
: &ast
::Mac
) {
1470 self.sess
.span_diagnostic
.span_bug(mac
.span
,
1471 "macro exterminator: expected AST \
1472 with no macro invocations");
1479 use super::{pattern_bindings, expand_crate}
;
1480 use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig}
;
1484 use ext
::base
::ExtCtxt
;
1489 use util
::parser_testing
::{string_to_parser}
;
1490 use util
::parser_testing
::{string_to_pat, string_to_crate, strs_to_idents}
;
1494 // a visitor that extracts the paths
1495 // from a given thingy and puts them in a mutable
1496 // array (passed in to the traversal)
1498 struct PathExprFinderContext
{
1499 path_accumulator
: Vec
<ast
::Path
> ,
1502 impl<'v
> Visitor
<'v
> for PathExprFinderContext
{
1503 fn visit_expr(&mut self, expr
: &ast
::Expr
) {
1504 if let ast
::ExprKind
::Path(None
, ref p
) = expr
.node
{
1505 self.path_accumulator
.push(p
.clone());
1507 visit
::walk_expr(self, expr
);
1511 // find the variable references in a crate
1512 fn crate_varrefs(the_crate
: &ast
::Crate
) -> Vec
<ast
::Path
> {
1513 let mut path_finder
= PathExprFinderContext{path_accumulator:Vec::new()}
;
1514 visit
::walk_crate(&mut path_finder
, the_crate
);
1515 path_finder
.path_accumulator
1518 /// A Visitor that extracts the identifiers from a thingy.
1519 // as a side note, I'm starting to want to abstract over these....
1520 struct IdentFinder
{
1521 ident_accumulator
: Vec
<ast
::Ident
>
1524 impl<'v
> Visitor
<'v
> for IdentFinder
{
1525 fn visit_ident(&mut self, _
: codemap
::Span
, id
: ast
::Ident
){
1526 self.ident_accumulator
.push(id
);
1530 /// Find the idents in a crate
1531 fn crate_idents(the_crate
: &ast
::Crate
) -> Vec
<ast
::Ident
> {
1532 let mut ident_finder
= IdentFinder{ident_accumulator: Vec::new()}
;
1533 visit
::walk_crate(&mut ident_finder
, the_crate
);
1534 ident_finder
.ident_accumulator
1537 // these following tests are quite fragile, in that they don't test what
1538 // *kind* of failure occurs.
1540 fn test_ecfg() -> ExpansionConfig
<'
static> {
1541 ExpansionConfig
::default("test".to_string())
1544 // make sure that macros can't escape fns
1546 #[test] fn macros_cant_escape_fns_test () {
1547 let src
= "fn bogus() {macro_rules! z (() => (3+4));}\
1548 fn inty() -> i32 { z!() }".to_string();
1549 let sess
= parse
::ParseSess
::new();
1550 let crate_ast
= parse
::parse_crate_from_source_str(
1551 "<test>".to_string(),
1553 Vec
::new(), &sess
).unwrap();
1555 let mut gated_cfgs
= vec
![];
1556 let ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut gated_cfgs
);
1557 expand_crate(ecx
, vec
![], vec
![], crate_ast
);
1560 // make sure that macros can't escape modules
1562 #[test] fn macros_cant_escape_mods_test () {
1563 let src
= "mod foo {macro_rules! z (() => (3+4));}\
1564 fn inty() -> i32 { z!() }".to_string();
1565 let sess
= parse
::ParseSess
::new();
1566 let crate_ast
= parse
::parse_crate_from_source_str(
1567 "<test>".to_string(),
1569 Vec
::new(), &sess
).unwrap();
1570 let mut gated_cfgs
= vec
![];
1571 let ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut gated_cfgs
);
1572 expand_crate(ecx
, vec
![], vec
![], crate_ast
);
1575 // macro_use modules should allow macros to escape
1576 #[test] fn macros_can_escape_flattened_mods_test () {
1577 let src
= "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
1578 fn inty() -> i32 { z!() }".to_string();
1579 let sess
= parse
::ParseSess
::new();
1580 let crate_ast
= parse
::parse_crate_from_source_str(
1581 "<test>".to_string(),
1583 Vec
::new(), &sess
).unwrap();
1584 let mut gated_cfgs
= vec
![];
1585 let ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut gated_cfgs
);
1586 expand_crate(ecx
, vec
![], vec
![], crate_ast
);
1589 fn expand_crate_str(crate_str
: String
) -> ast
::Crate
{
1590 let ps
= parse
::ParseSess
::new();
1591 let crate_ast
= panictry
!(string_to_parser(&ps
, crate_str
).parse_crate_mod());
1592 // the cfg argument actually does matter, here...
1593 let mut gated_cfgs
= vec
![];
1594 let ecx
= ExtCtxt
::new(&ps
, vec
![], test_ecfg(), &mut gated_cfgs
);
1595 expand_crate(ecx
, vec
![], vec
![], crate_ast
).0
1598 // find the pat_ident paths in a crate
1599 fn crate_bindings(the_crate
: &ast
::Crate
) -> Vec
<ast
::Ident
> {
1600 let mut name_finder
= PatIdentFinder{ident_accumulator:Vec::new()}
;
1601 visit
::walk_crate(&mut name_finder
, the_crate
);
1602 name_finder
.ident_accumulator
1605 #[test] fn macro_tokens_should_match(){
1607 "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
1610 // should be able to use a bound identifier as a literal in a macro definition:
1611 #[test] fn self_macro_parsing(){
1613 "macro_rules! foo ((zz) => (287;));
1614 fn f(zz: i32) {foo!(zz);}".to_string()
1618 // renaming tests expand a crate and then check that the bindings match
1619 // the right varrefs. The specification of the test case includes the
1620 // text of the crate, and also an array of arrays. Each element in the
1621 // outer array corresponds to a binding in the traversal of the AST
1622 // induced by visit. Each of these arrays contains a list of indexes,
1623 // interpreted as the varrefs in the varref traversal that this binding
1624 // should match. So, for instance, in a program with two bindings and
1625 // three varrefs, the array [[1, 2], [0]] would indicate that the first
1626 // binding should match the second two varrefs, and the second binding
1627 // should match the first varref.
1629 // Put differently; this is a sparse representation of a boolean matrix
1630 // indicating which bindings capture which identifiers.
1632 // Note also that this matrix is dependent on the implicit ordering of
1633 // the bindings and the varrefs discovered by the name-finder and the path-finder.
1635 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1636 // names; differences in marks don't matter any more.
1638 // oog... I also want tests that check "bound-identifier-=?". That is,
1639 // not just "do these have the same name", but "do they have the same
1640 // name *and* the same marks"? Understanding this is really pretty painful.
1641 // in principle, you might want to control this boolean on a per-varref basis,
1642 // but that would make things even harder to understand, and might not be
1643 // necessary for thorough testing.
1644 type RenamingTest
= (&'
static str, Vec
<Vec
<usize>>, bool
);
1647 fn automatic_renaming () {
1648 let tests
: Vec
<RenamingTest
> =
1649 vec
!(// b & c should get new names throughout, in the expr too:
1650 ("fn a() -> i32 { let b = 13; let c = b; b+c }",
1651 vec
!(vec
!(0,1),vec
!(2)), false),
1652 // both x's should be renamed (how is this causing a bug?)
1653 ("fn main () {let x: i32 = 13;x;}",
1654 vec
!(vec
!(0)), false),
1655 // the use of b after the + should be renamed, the other one not:
1656 ("macro_rules! f (($x:ident) => (b + $x)); fn a() -> i32 { let b = 13; f!(b)}",
1657 vec
!(vec
!(1)), false),
1658 // the b before the plus should not be renamed (requires marks)
1659 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> i32 { f!(b)}",
1660 vec
!(vec
!(1)), false),
1661 // the marks going in and out of letty should cancel, allowing that $x to
1662 // capture the one following the semicolon.
1663 // this was an awesome test case, and caught a *lot* of bugs.
1664 ("macro_rules! letty(($x:ident) => (let $x = 15;));
1665 macro_rules! user(($x:ident) => ({letty!($x); $x}));
1666 fn main() -> i32 {user!(z)}",
1667 vec
!(vec
!(0)), false)
1669 for (idx
,s
) in tests
.iter().enumerate() {
1670 run_renaming_test(s
,idx
);
1674 // no longer a fixme #8062: this test exposes a *potential* bug; our system does
1675 // not behave exactly like MTWT, but a conversation with Matthew Flatt
1676 // suggests that this can only occur in the presence of local-expand, which
1677 // we have no plans to support. ... unless it's needed for item hygiene....
1682 &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
1683 vec
!(vec
!(0)), true), 0)
1687 // the z flows into and out of two macros (g & f) along one path, and one
1688 // (just g) along the other, so the result of the whole thing should
1689 // be "let z_123 = 3; z_123"
1694 &("macro_rules! g (($x:ident) =>
1695 ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}));
1697 vec
!(vec
!(0)),false),
1701 // match variable hygiene. Should expand into
1702 // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
1706 &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}));
1707 fn z() {match 8 {x => bad_macro!(x)}}",
1708 // NB: the third "binding" is the repeat of the second one.
1709 vec
!(vec
!(1,3),vec
!(0,2),vec
!(0,2)),
1714 // interpolated nodes weren't getting labeled.
1715 // should expand into
1716 // fn main(){let g1_1 = 13; g1_1}}
1718 fn pat_expand_issue_15221(){
1720 &("macro_rules! inner ( ($e:pat ) => ($e));
1721 macro_rules! outer ( ($e:pat ) => (inner!($e)));
1722 fn main() { let outer!(g) = 13; g;}",
1728 // create a really evil test case where a $x appears inside a binding of $x
1729 // but *shouldn't* bind because it was inserted by a different macro....
1730 // can't write this test case until we have macro-generating macros.
1732 // method arg hygiene
1733 // method expands to fn get_x(&self_0, x_1: i32) {self_0 + self_2 + x_3 + x_1}
1735 fn method_arg_hygiene(){
1737 &("macro_rules! inject_x (()=>(x));
1738 macro_rules! inject_self (()=>(self));
1740 impl A{fn get_x(&self, x: i32) {self + inject_self!() + inject_x!() + x;} }",
1741 vec
!(vec
!(0),vec
!(3)),
1746 // ooh, got another bite?
1747 // expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
1749 fn method_arg_hygiene_2(){
1752 macro_rules! add_method (($T:ty) =>
1753 (impl $T { fn thingy(&self) {self;} }));
1761 // expands to fn q(x_1: i32){fn g(x_2: i32){x_2 + x_1};}
1765 &("macro_rules! bad_macro (($ex:expr) => (fn g(x: i32){ x + $ex }));
1766 fn q(x: i32) { bad_macro!(x); }",
1767 vec
!(vec
!(1),vec
!(0)),true),
1771 // closure arg hygiene (ExprKind::Closure)
1772 // expands to fn f(){(|x_1 : i32| {(x_2 + x_1)})(3);}
1774 fn closure_arg_hygiene(){
1776 &("macro_rules! inject_x (()=>(x));
1777 fn f(){(|x : i32| {(inject_x!() + x)})(3);}",
1783 // macro_rules in method position. Sadly, unimplemented.
1785 fn macro_in_method_posn(){
1787 "macro_rules! my_method (() => (fn thirteen(&self) -> i32 {13}));
1789 impl A{ my_method!(); }
1790 fn f(){A.thirteen;}".to_string());
1793 // another nested macro
1794 // expands to impl Entries {fn size_hint(&self_1) {self_1;}
1796 fn item_macro_workaround(){
1798 &("macro_rules! item { ($i:item) => {$i}}
1800 macro_rules! iterator_impl {
1801 () => { item!( impl Entries { fn size_hint(&self) { self;}});}}
1802 iterator_impl! { }",
1803 vec
!(vec
!(0)), true),
1807 // run one of the renaming tests
1808 fn run_renaming_test(t
: &RenamingTest
, test_idx
: usize) {
1809 let invalid_name
= token
::special_idents
::invalid
.name
;
1810 let (teststr
, bound_connections
, bound_ident_check
) = match *t
{
1811 (ref str,ref conns
, bic
) => (str.to_string(), conns
.clone(), bic
)
1813 let cr
= expand_crate_str(teststr
.to_string());
1814 let bindings
= crate_bindings(&cr
);
1815 let varrefs
= crate_varrefs(&cr
);
1817 // must be one check clause for each binding:
1818 assert_eq
!(bindings
.len(),bound_connections
.len());
1819 for (binding_idx
,shouldmatch
) in bound_connections
.iter().enumerate() {
1820 let binding_name
= mtwt
::resolve(bindings
[binding_idx
]);
1821 let binding_marks
= mtwt
::marksof(bindings
[binding_idx
].ctxt
, invalid_name
);
1822 // shouldmatch can't name varrefs that don't exist:
1823 assert
!((shouldmatch
.is_empty()) ||
1824 (varrefs
.len() > *shouldmatch
.iter().max().unwrap()));
1825 for (idx
,varref
) in varrefs
.iter().enumerate() {
1826 let print_hygiene_debug_info
= || {
1827 // good lord, you can't make a path with 0 segments, can you?
1828 let final_varref_ident
= match varref
.segments
.last() {
1829 Some(pathsegment
) => pathsegment
.identifier
,
1830 None
=> panic
!("varref with 0 path segments?")
1832 let varref_name
= mtwt
::resolve(final_varref_ident
);
1833 let varref_idents
: Vec
<ast
::Ident
>
1834 = varref
.segments
.iter().map(|s
| s
.identifier
)
1836 println
!("varref #{}: {:?}, resolves to {}",idx
, varref_idents
, varref_name
);
1837 println
!("varref's first segment's string: \"{}\"", final_varref_ident
);
1838 println
!("binding #{}: {}, resolves to {}",
1839 binding_idx
, bindings
[binding_idx
], binding_name
);
1840 mtwt
::with_sctable(|x
| mtwt
::display_sctable(x
));
1842 if shouldmatch
.contains(&idx
) {
1843 // it should be a path of length 1, and it should
1844 // be free-identifier=? or bound-identifier=? to the given binding
1845 assert_eq
!(varref
.segments
.len(),1);
1846 let varref_name
= mtwt
::resolve(varref
.segments
[0].identifier
);
1847 let varref_marks
= mtwt
::marksof(varref
.segments
[0]
1851 if !(varref_name
==binding_name
) {
1852 println
!("uh oh, should match but doesn't:");
1853 print_hygiene_debug_info();
1855 assert_eq
!(varref_name
,binding_name
);
1856 if bound_ident_check
{
1857 // we're checking bound-identifier=?, and the marks
1858 // should be the same, too:
1859 assert_eq
!(varref_marks
,binding_marks
.clone());
1862 let varref_name
= mtwt
::resolve(varref
.segments
[0].identifier
);
1863 let fail
= (varref
.segments
.len() == 1)
1864 && (varref_name
== binding_name
);
1867 println
!("failure on test {}",test_idx
);
1868 println
!("text of test case: \"{}\"", teststr
);
1870 println
!("uh oh, matches but shouldn't:");
1871 print_hygiene_debug_info();
1880 fn fmt_in_macro_used_inside_module_macro() {
1881 let crate_str
= "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
1882 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
1885 let cr
= expand_crate_str(crate_str
);
1886 // find the xx binding
1887 let bindings
= crate_bindings(&cr
);
1888 let cxbinds
: Vec
<&ast
::Ident
> =
1889 bindings
.iter().filter(|b
| b
.name
.as_str() == "xx").collect();
1890 let cxbinds
: &[&ast
::Ident
] = &cxbinds
[..];
1891 let cxbind
= match (cxbinds
.len(), cxbinds
.get(0)) {
1893 _
=> panic
!("expected just one binding for ext_cx")
1895 let resolved_binding
= mtwt
::resolve(*cxbind
);
1896 let varrefs
= crate_varrefs(&cr
);
1898 // the xx binding should bind all of the xx varrefs:
1899 for (idx
,v
) in varrefs
.iter().filter(|p
| {
1900 p
.segments
.len() == 1
1901 && p
.segments
[0].identifier
.name
.as_str() == "xx"
1903 if mtwt
::resolve(v
.segments
[0].identifier
) != resolved_binding
{
1904 println
!("uh oh, xx binding didn't match xx varref:");
1905 println
!("this is xx varref \\# {}", idx
);
1906 println
!("binding: {}", cxbind
);
1907 println
!("resolves to: {}", resolved_binding
);
1908 println
!("varref: {}", v
.segments
[0].identifier
);
1909 println
!("resolves to: {}",
1910 mtwt
::resolve(v
.segments
[0].identifier
));
1911 mtwt
::with_sctable(|x
| mtwt
::display_sctable(x
));
1913 assert_eq
!(mtwt
::resolve(v
.segments
[0].identifier
),
1920 let pat
= string_to_pat(
1921 "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
1922 let idents
= pattern_bindings(&pat
);
1923 assert_eq
!(idents
, strs_to_idents(vec
!("a","c","b","d")));
1926 // test the list of identifier patterns gathered by the visitor. Note that
1927 // 'None' is listed as an identifier pattern because we don't yet know that
1928 // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
1930 fn crate_bindings_test(){
1931 let the_crate
= string_to_crate("fn main (a: i32) -> i32 {|b| {
1932 match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
1933 let idents
= crate_bindings(&the_crate
);
1934 assert_eq
!(idents
, strs_to_idents(vec
!("a","b","None","i","i","z","y")));
1937 // test the IdentRenamer directly
1939 fn ident_renamer_test () {
1940 let the_crate
= string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1941 let f_ident
= token
::str_to_ident("f");
1942 let x_ident
= token
::str_to_ident("x");
1943 let int_ident
= token
::str_to_ident("i32");
1944 let renames
= vec
!((x_ident
,Name(16)));
1945 let mut renamer
= IdentRenamer{renames: &renames}
;
1946 let renamed_crate
= renamer
.fold_crate(the_crate
);
1947 let idents
= crate_idents(&renamed_crate
);
1948 let resolved
: Vec
<ast
::Name
> = idents
.iter().map(|id
| mtwt
::resolve(*id
)).collect();
1949 assert_eq
!(resolved
, [f_ident
.name
,Name(16),int_ident
.name
,Name(16),Name(16),Name(16)]);
1952 // test the PatIdentRenamer; only PatIdents get renamed
1954 fn pat_ident_renamer_test () {
1955 let the_crate
= string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1956 let f_ident
= token
::str_to_ident("f");
1957 let x_ident
= token
::str_to_ident("x");
1958 let int_ident
= token
::str_to_ident("i32");
1959 let renames
= vec
!((x_ident
,Name(16)));
1960 let mut renamer
= PatIdentRenamer{renames: &renames}
;
1961 let renamed_crate
= renamer
.fold_crate(the_crate
);
1962 let idents
= crate_idents(&renamed_crate
);
1963 let resolved
: Vec
<ast
::Name
> = idents
.iter().map(|id
| mtwt
::resolve(*id
)).collect();
1964 let x_name
= x_ident
.name
;
1965 assert_eq
!(resolved
, [f_ident
.name
,Name(16),int_ident
.name
,Name(16),x_name
,x_name
]);