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 node
: match ii
.node
{
1065 ast
::ImplItemKind
::Method(sig
, body
) => {
1066 let (sig
, body
) = expand_and_rename_method(sig
, body
, fld
);
1067 ast
::ImplItemKind
::Method(sig
, body
)
1071 span
: fld
.new_span(ii
.span
)
1073 ast
::ImplItemKind
::Macro(_
) => {
1074 let (span
, mac
) = match ii
.node
{
1075 ast
::ImplItemKind
::Macro(mac
) => (ii
.span
, mac
),
1078 let maybe_new_items
=
1079 expand_mac_invoc(mac
, span
,
1080 |r
| r
.make_impl_items(),
1081 |meths
, mark
| meths
.move_map(|m
| mark_impl_item(m
, mark
)),
1084 match maybe_new_items
{
1085 Some(impl_items
) => {
1086 // expand again if necessary
1087 let new_items
= impl_items
.into_iter().flat_map(|ii
| {
1088 expand_impl_item(ii
, fld
).into_iter()
1093 None
=> SmallVector
::zero()
1096 _
=> fold
::noop_fold_impl_item(ii
, fld
)
1100 /// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
1101 /// PatIdents in its arguments to perform renaming in the FnDecl and
1102 /// the block, returning both the new FnDecl and the new Block.
1103 fn expand_and_rename_fn_decl_and_block(fn_decl
: P
<ast
::FnDecl
>, block
: P
<ast
::Block
>,
1104 fld
: &mut MacroExpander
)
1105 -> (P
<ast
::FnDecl
>, P
<ast
::Block
>) {
1106 let expanded_decl
= fld
.fold_fn_decl(fn_decl
);
1107 let idents
= fn_decl_arg_bindings(&expanded_decl
);
1109 idents
.iter().map(|id
| (*id
,fresh_name(*id
))).collect();
1110 // first, a renamer for the PatIdents, for the fn_decl:
1111 let mut rename_pat_fld
= PatIdentRenamer{renames: &renames}
;
1112 let rewritten_fn_decl
= rename_pat_fld
.fold_fn_decl(expanded_decl
);
1113 // now, a renamer for *all* idents, for the body:
1114 let mut rename_fld
= IdentRenamer{renames: &renames}
;
1115 let rewritten_body
= fld
.fold_block(rename_fld
.fold_block(block
));
1116 (rewritten_fn_decl
,rewritten_body
)
1119 fn expand_and_rename_method(sig
: ast
::MethodSig
, body
: P
<ast
::Block
>,
1120 fld
: &mut MacroExpander
)
1121 -> (ast
::MethodSig
, P
<ast
::Block
>) {
1122 let (rewritten_fn_decl
, rewritten_body
)
1123 = expand_and_rename_fn_decl_and_block(sig
.decl
, body
, fld
);
1125 generics
: fld
.fold_generics(sig
.generics
),
1127 explicit_self
: fld
.fold_explicit_self(sig
.explicit_self
),
1128 unsafety
: sig
.unsafety
,
1129 constness
: sig
.constness
,
1130 decl
: rewritten_fn_decl
1134 pub fn expand_type(t
: P
<ast
::Ty
>, fld
: &mut MacroExpander
) -> P
<ast
::Ty
> {
1135 let t
= match t
.node
.clone() {
1136 ast
::TyKind
::Mac(mac
) => {
1137 if fld
.cx
.ecfg
.features
.unwrap().type_macros
{
1138 let expanded_ty
= match expand_mac_invoc(mac
, t
.span
,
1144 return DummyResult
::raw_ty(t
.span
);
1148 // Keep going, outside-in.
1149 let fully_expanded
= fld
.fold_ty(expanded_ty
);
1152 fully_expanded
.map(|t
| ast
::Ty
{
1153 id
: ast
::DUMMY_NODE_ID
,
1158 feature_gate
::emit_feature_err(
1159 &fld
.cx
.parse_sess
.span_diagnostic
,
1162 feature_gate
::GateIssue
::Language
,
1163 "type macros are experimental");
1165 DummyResult
::raw_ty(t
.span
)
1171 fold
::noop_fold_ty(t
, fld
)
1174 /// A tree-folder that performs macro expansion
1175 pub struct MacroExpander
<'a
, 'b
:'a
> {
1176 pub cx
: &'a
mut ExtCtxt
<'b
>,
1179 impl<'a
, 'b
> MacroExpander
<'a
, 'b
> {
1180 pub fn new(cx
: &'a
mut ExtCtxt
<'b
>) -> MacroExpander
<'a
, 'b
> {
1181 MacroExpander { cx: cx }
1185 impl<'a
, 'b
> Folder
for MacroExpander
<'a
, 'b
> {
1186 fn fold_expr(&mut self, expr
: P
<ast
::Expr
>) -> P
<ast
::Expr
> {
1187 expand_expr(expr
, self)
1190 fn fold_pat(&mut self, pat
: P
<ast
::Pat
>) -> P
<ast
::Pat
> {
1191 expand_pat(pat
, self)
1194 fn fold_item(&mut self, item
: P
<ast
::Item
>) -> SmallVector
<P
<ast
::Item
>> {
1195 expand_item(item
, self)
1198 fn fold_item_kind(&mut self, item
: ast
::ItemKind
) -> ast
::ItemKind
{
1199 expand_item_kind(item
, self)
1202 fn fold_stmt(&mut self, stmt
: ast
::Stmt
) -> SmallVector
<ast
::Stmt
> {
1203 expand_stmt(stmt
, self)
1206 fn fold_block(&mut self, block
: P
<Block
>) -> P
<Block
> {
1207 expand_block(block
, self)
1210 fn fold_arm(&mut self, arm
: ast
::Arm
) -> ast
::Arm
{
1211 expand_arm(arm
, self)
1214 fn fold_trait_item(&mut self, i
: ast
::TraitItem
) -> SmallVector
<ast
::TraitItem
> {
1215 expand_annotatable(Annotatable
::TraitItem(P(i
)), self)
1216 .into_iter().map(|i
| i
.expect_trait_item()).collect()
1219 fn fold_impl_item(&mut self, i
: ast
::ImplItem
) -> SmallVector
<ast
::ImplItem
> {
1220 expand_annotatable(Annotatable
::ImplItem(P(i
)), self)
1221 .into_iter().map(|i
| i
.expect_impl_item()).collect()
1224 fn fold_ty(&mut self, ty
: P
<ast
::Ty
>) -> P
<ast
::Ty
> {
1225 expand_type(ty
, self)
1228 fn new_span(&mut self, span
: Span
) -> Span
{
1229 new_span(self.cx
, span
)
1233 fn new_span(cx
: &ExtCtxt
, sp
: Span
) -> Span
{
1234 /* this discards information in the case of macro-defining macros */
1238 expn_id
: cx
.backtrace(),
1242 pub struct ExpansionConfig
<'feat
> {
1243 pub crate_name
: String
,
1244 pub features
: Option
<&'feat Features
>,
1245 pub recursion_limit
: usize,
1246 pub trace_mac
: bool
,
1249 macro_rules
! feature_tests
{
1250 ($
( fn $getter
:ident
= $field
:ident
, )*) => {
1252 pub fn $
getter(&self) -> bool
{
1253 match self.features
{
1254 Some(&Features { $field: true, .. }
) => true,
1262 impl<'feat
> ExpansionConfig
<'feat
> {
1263 pub fn default(crate_name
: String
) -> ExpansionConfig
<'
static> {
1265 crate_name
: crate_name
,
1267 recursion_limit
: 64,
1273 fn enable_quotes
= allow_quote
,
1274 fn enable_asm
= allow_asm
,
1275 fn enable_log_syntax
= allow_log_syntax
,
1276 fn enable_concat_idents
= allow_concat_idents
,
1277 fn enable_trace_macros
= allow_trace_macros
,
1278 fn enable_allow_internal_unstable
= allow_internal_unstable
,
1279 fn enable_custom_derive
= allow_custom_derive
,
1280 fn enable_pushpop_unsafe
= allow_pushpop_unsafe
,
1284 pub fn expand_crate(mut cx
: ExtCtxt
,
1285 // these are the macros being imported to this crate:
1286 imported_macros
: Vec
<ast
::MacroDef
>,
1287 user_exts
: Vec
<NamedSyntaxExtension
>,
1288 c
: Crate
) -> (Crate
, HashSet
<Name
>) {
1289 if std_inject
::no_core(&c
) {
1290 cx
.crate_root
= None
;
1291 } else if std_inject
::no_std(&c
) {
1292 cx
.crate_root
= Some("core");
1294 cx
.crate_root
= Some("std");
1297 let mut expander
= MacroExpander
::new(&mut cx
);
1299 for def
in imported_macros
{
1300 expander
.cx
.insert_macro(def
);
1303 for (name
, extension
) in user_exts
{
1304 expander
.cx
.syntax_env
.insert(name
, extension
);
1307 let err_count
= cx
.parse_sess
.span_diagnostic
.err_count();
1308 let mut ret
= expander
.fold_crate(c
);
1309 ret
.exported_macros
= expander
.cx
.exported_macros
.clone();
1311 if cx
.parse_sess
.span_diagnostic
.err_count() > err_count
{
1312 cx
.parse_sess
.span_diagnostic
.abort_if_errors();
1317 return (ret
, cx
.syntax_env
.names
);
1320 // HYGIENIC CONTEXT EXTENSION:
1321 // all of these functions are for walking over
1322 // ASTs and making some change to the context of every
1323 // element that has one. a CtxtFn is a trait-ified
1324 // version of a closure in (SyntaxContext -> SyntaxContext).
1325 // the ones defined here include:
1326 // Marker - add a mark to a context
1328 // A Marker adds the given mark to the syntax context
1329 struct Marker { mark: Mrk }
1331 impl Folder
for Marker
{
1332 fn fold_ident(&mut self, id
: Ident
) -> Ident
{
1333 ast
::Ident
::new(id
.name
, mtwt
::apply_mark(self.mark
, id
.ctxt
))
1335 fn fold_mac(&mut self, Spanned {node, span}
: ast
::Mac
) -> ast
::Mac
{
1338 path
: self.fold_path(node
.path
),
1339 tts
: self.fold_tts(&node
.tts
),
1340 ctxt
: mtwt
::apply_mark(self.mark
, node
.ctxt
),
1347 // apply a given mark to the given token trees. Used prior to expansion of a macro.
1348 fn mark_tts(tts
: &[TokenTree
], m
: Mrk
) -> Vec
<TokenTree
> {
1349 noop_fold_tts(tts
, &mut Marker{mark:m}
)
1352 // apply a given mark to the given expr. Used following the expansion of a macro.
1353 fn mark_expr(expr
: P
<ast
::Expr
>, m
: Mrk
) -> P
<ast
::Expr
> {
1354 Marker{mark:m}
.fold_expr(expr
)
1357 // apply a given mark to the given pattern. Used following the expansion of a macro.
1358 fn mark_pat(pat
: P
<ast
::Pat
>, m
: Mrk
) -> P
<ast
::Pat
> {
1359 Marker{mark:m}
.fold_pat(pat
)
1362 // apply a given mark to the given stmt. Used following the expansion of a macro.
1363 fn mark_stmt(stmt
: ast
::Stmt
, m
: Mrk
) -> ast
::Stmt
{
1364 Marker{mark:m}
.fold_stmt(stmt
)
1365 .expect_one("marking a stmt didn't return exactly one stmt")
1368 // apply a given mark to the given item. Used following the expansion of a macro.
1369 fn mark_item(expr
: P
<ast
::Item
>, m
: Mrk
) -> P
<ast
::Item
> {
1370 Marker{mark:m}
.fold_item(expr
)
1371 .expect_one("marking an item didn't return exactly one item")
1374 // apply a given mark to the given item. Used following the expansion of a macro.
1375 fn mark_impl_item(ii
: ast
::ImplItem
, m
: Mrk
) -> ast
::ImplItem
{
1376 Marker{mark:m}
.fold_impl_item(ii
)
1377 .expect_one("marking an impl item didn't return exactly one impl item")
1380 fn mark_ty(ty
: P
<ast
::Ty
>, m
: Mrk
) -> P
<ast
::Ty
> {
1381 Marker { mark: m }
.fold_ty(ty
)
1384 /// Check that there are no macro invocations left in the AST:
1385 pub fn check_for_macros(sess
: &parse
::ParseSess
, krate
: &ast
::Crate
) {
1386 visit
::walk_crate(&mut MacroExterminator{sess:sess}
, krate
);
1389 /// A visitor that ensures that no macro invocations remain in an AST.
1390 struct MacroExterminator
<'a
>{
1391 sess
: &'a parse
::ParseSess
1394 impl<'a
, 'v
> Visitor
<'v
> for MacroExterminator
<'a
> {
1395 fn visit_mac(&mut self, mac
: &ast
::Mac
) {
1396 self.sess
.span_diagnostic
.span_bug(mac
.span
,
1397 "macro exterminator: expected AST \
1398 with no macro invocations");
1405 use super::{pattern_bindings, expand_crate}
;
1406 use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig}
;
1410 use ext
::base
::ExtCtxt
;
1415 use util
::parser_testing
::{string_to_parser}
;
1416 use util
::parser_testing
::{string_to_pat, string_to_crate, strs_to_idents}
;
1420 // a visitor that extracts the paths
1421 // from a given thingy and puts them in a mutable
1422 // array (passed in to the traversal)
1424 struct PathExprFinderContext
{
1425 path_accumulator
: Vec
<ast
::Path
> ,
1428 impl<'v
> Visitor
<'v
> for PathExprFinderContext
{
1429 fn visit_expr(&mut self, expr
: &ast
::Expr
) {
1430 if let ast
::ExprKind
::Path(None
, ref p
) = expr
.node
{
1431 self.path_accumulator
.push(p
.clone());
1433 visit
::walk_expr(self, expr
);
1437 // find the variable references in a crate
1438 fn crate_varrefs(the_crate
: &ast
::Crate
) -> Vec
<ast
::Path
> {
1439 let mut path_finder
= PathExprFinderContext{path_accumulator:Vec::new()}
;
1440 visit
::walk_crate(&mut path_finder
, the_crate
);
1441 path_finder
.path_accumulator
1444 /// A Visitor that extracts the identifiers from a thingy.
1445 // as a side note, I'm starting to want to abstract over these....
1446 struct IdentFinder
{
1447 ident_accumulator
: Vec
<ast
::Ident
>
1450 impl<'v
> Visitor
<'v
> for IdentFinder
{
1451 fn visit_ident(&mut self, _
: codemap
::Span
, id
: ast
::Ident
){
1452 self.ident_accumulator
.push(id
);
1456 /// Find the idents in a crate
1457 fn crate_idents(the_crate
: &ast
::Crate
) -> Vec
<ast
::Ident
> {
1458 let mut ident_finder
= IdentFinder{ident_accumulator: Vec::new()}
;
1459 visit
::walk_crate(&mut ident_finder
, the_crate
);
1460 ident_finder
.ident_accumulator
1463 // these following tests are quite fragile, in that they don't test what
1464 // *kind* of failure occurs.
1466 fn test_ecfg() -> ExpansionConfig
<'
static> {
1467 ExpansionConfig
::default("test".to_string())
1470 // make sure that macros can't escape fns
1472 #[test] fn macros_cant_escape_fns_test () {
1473 let src
= "fn bogus() {macro_rules! z (() => (3+4));}\
1474 fn inty() -> i32 { z!() }".to_string();
1475 let sess
= parse
::ParseSess
::new();
1476 let crate_ast
= parse
::parse_crate_from_source_str(
1477 "<test>".to_string(),
1481 let mut gated_cfgs
= vec
![];
1482 let ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut gated_cfgs
);
1483 expand_crate(ecx
, vec
![], vec
![], crate_ast
);
1486 // make sure that macros can't escape modules
1488 #[test] fn macros_cant_escape_mods_test () {
1489 let src
= "mod foo {macro_rules! z (() => (3+4));}\
1490 fn inty() -> i32 { z!() }".to_string();
1491 let sess
= parse
::ParseSess
::new();
1492 let crate_ast
= parse
::parse_crate_from_source_str(
1493 "<test>".to_string(),
1496 let mut gated_cfgs
= vec
![];
1497 let ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut gated_cfgs
);
1498 expand_crate(ecx
, vec
![], vec
![], crate_ast
);
1501 // macro_use modules should allow macros to escape
1502 #[test] fn macros_can_escape_flattened_mods_test () {
1503 let src
= "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
1504 fn inty() -> i32 { z!() }".to_string();
1505 let sess
= parse
::ParseSess
::new();
1506 let crate_ast
= parse
::parse_crate_from_source_str(
1507 "<test>".to_string(),
1510 let mut gated_cfgs
= vec
![];
1511 let ecx
= ExtCtxt
::new(&sess
, vec
![], test_ecfg(), &mut gated_cfgs
);
1512 expand_crate(ecx
, vec
![], vec
![], crate_ast
);
1515 fn expand_crate_str(crate_str
: String
) -> ast
::Crate
{
1516 let ps
= parse
::ParseSess
::new();
1517 let crate_ast
= panictry
!(string_to_parser(&ps
, crate_str
).parse_crate_mod());
1518 // the cfg argument actually does matter, here...
1519 let mut gated_cfgs
= vec
![];
1520 let ecx
= ExtCtxt
::new(&ps
, vec
![], test_ecfg(), &mut gated_cfgs
);
1521 expand_crate(ecx
, vec
![], vec
![], crate_ast
).0
1524 // find the pat_ident paths in a crate
1525 fn crate_bindings(the_crate
: &ast
::Crate
) -> Vec
<ast
::Ident
> {
1526 let mut name_finder
= PatIdentFinder{ident_accumulator:Vec::new()}
;
1527 visit
::walk_crate(&mut name_finder
, the_crate
);
1528 name_finder
.ident_accumulator
1531 #[test] fn macro_tokens_should_match(){
1533 "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
1536 // should be able to use a bound identifier as a literal in a macro definition:
1537 #[test] fn self_macro_parsing(){
1539 "macro_rules! foo ((zz) => (287;));
1540 fn f(zz: i32) {foo!(zz);}".to_string()
1544 // renaming tests expand a crate and then check that the bindings match
1545 // the right varrefs. The specification of the test case includes the
1546 // text of the crate, and also an array of arrays. Each element in the
1547 // outer array corresponds to a binding in the traversal of the AST
1548 // induced by visit. Each of these arrays contains a list of indexes,
1549 // interpreted as the varrefs in the varref traversal that this binding
1550 // should match. So, for instance, in a program with two bindings and
1551 // three varrefs, the array [[1, 2], [0]] would indicate that the first
1552 // binding should match the second two varrefs, and the second binding
1553 // should match the first varref.
1555 // Put differently; this is a sparse representation of a boolean matrix
1556 // indicating which bindings capture which identifiers.
1558 // Note also that this matrix is dependent on the implicit ordering of
1559 // the bindings and the varrefs discovered by the name-finder and the path-finder.
1561 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1562 // names; differences in marks don't matter any more.
1564 // oog... I also want tests that check "bound-identifier-=?". That is,
1565 // not just "do these have the same name", but "do they have the same
1566 // name *and* the same marks"? Understanding this is really pretty painful.
1567 // in principle, you might want to control this boolean on a per-varref basis,
1568 // but that would make things even harder to understand, and might not be
1569 // necessary for thorough testing.
1570 type RenamingTest
= (&'
static str, Vec
<Vec
<usize>>, bool
);
1573 fn automatic_renaming () {
1574 let tests
: Vec
<RenamingTest
> =
1575 vec
!(// b & c should get new names throughout, in the expr too:
1576 ("fn a() -> i32 { let b = 13; let c = b; b+c }",
1577 vec
!(vec
!(0,1),vec
!(2)), false),
1578 // both x's should be renamed (how is this causing a bug?)
1579 ("fn main () {let x: i32 = 13;x;}",
1580 vec
!(vec
!(0)), false),
1581 // the use of b after the + should be renamed, the other one not:
1582 ("macro_rules! f (($x:ident) => (b + $x)); fn a() -> i32 { let b = 13; f!(b)}",
1583 vec
!(vec
!(1)), false),
1584 // the b before the plus should not be renamed (requires marks)
1585 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> i32 { f!(b)}",
1586 vec
!(vec
!(1)), false),
1587 // the marks going in and out of letty should cancel, allowing that $x to
1588 // capture the one following the semicolon.
1589 // this was an awesome test case, and caught a *lot* of bugs.
1590 ("macro_rules! letty(($x:ident) => (let $x = 15;));
1591 macro_rules! user(($x:ident) => ({letty!($x); $x}));
1592 fn main() -> i32 {user!(z)}",
1593 vec
!(vec
!(0)), false)
1595 for (idx
,s
) in tests
.iter().enumerate() {
1596 run_renaming_test(s
,idx
);
1600 // no longer a fixme #8062: this test exposes a *potential* bug; our system does
1601 // not behave exactly like MTWT, but a conversation with Matthew Flatt
1602 // suggests that this can only occur in the presence of local-expand, which
1603 // we have no plans to support. ... unless it's needed for item hygiene....
1608 &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
1609 vec
!(vec
!(0)), true), 0)
1613 // the z flows into and out of two macros (g & f) along one path, and one
1614 // (just g) along the other, so the result of the whole thing should
1615 // be "let z_123 = 3; z_123"
1620 &("macro_rules! g (($x:ident) =>
1621 ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}));
1623 vec
!(vec
!(0)),false),
1627 // match variable hygiene. Should expand into
1628 // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
1632 &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}));
1633 fn z() {match 8 {x => bad_macro!(x)}}",
1634 // NB: the third "binding" is the repeat of the second one.
1635 vec
!(vec
!(1,3),vec
!(0,2),vec
!(0,2)),
1640 // interpolated nodes weren't getting labeled.
1641 // should expand into
1642 // fn main(){let g1_1 = 13; g1_1}}
1644 fn pat_expand_issue_15221(){
1646 &("macro_rules! inner ( ($e:pat ) => ($e));
1647 macro_rules! outer ( ($e:pat ) => (inner!($e)));
1648 fn main() { let outer!(g) = 13; g;}",
1654 // create a really evil test case where a $x appears inside a binding of $x
1655 // but *shouldn't* bind because it was inserted by a different macro....
1656 // can't write this test case until we have macro-generating macros.
1658 // method arg hygiene
1659 // method expands to fn get_x(&self_0, x_1: i32) {self_0 + self_2 + x_3 + x_1}
1661 fn method_arg_hygiene(){
1663 &("macro_rules! inject_x (()=>(x));
1664 macro_rules! inject_self (()=>(self));
1666 impl A{fn get_x(&self, x: i32) {self + inject_self!() + inject_x!() + x;} }",
1667 vec
!(vec
!(0),vec
!(3)),
1672 // ooh, got another bite?
1673 // expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
1675 fn method_arg_hygiene_2(){
1678 macro_rules! add_method (($T:ty) =>
1679 (impl $T { fn thingy(&self) {self;} }));
1687 // expands to fn q(x_1: i32){fn g(x_2: i32){x_2 + x_1};}
1691 &("macro_rules! bad_macro (($ex:expr) => (fn g(x: i32){ x + $ex }));
1692 fn q(x: i32) { bad_macro!(x); }",
1693 vec
!(vec
!(1),vec
!(0)),true),
1697 // closure arg hygiene (ExprKind::Closure)
1698 // expands to fn f(){(|x_1 : i32| {(x_2 + x_1)})(3);}
1700 fn closure_arg_hygiene(){
1702 &("macro_rules! inject_x (()=>(x));
1703 fn f(){(|x : i32| {(inject_x!() + x)})(3);}",
1709 // macro_rules in method position. Sadly, unimplemented.
1711 fn macro_in_method_posn(){
1713 "macro_rules! my_method (() => (fn thirteen(&self) -> i32 {13}));
1715 impl A{ my_method!(); }
1716 fn f(){A.thirteen;}".to_string());
1719 // another nested macro
1720 // expands to impl Entries {fn size_hint(&self_1) {self_1;}
1722 fn item_macro_workaround(){
1724 &("macro_rules! item { ($i:item) => {$i}}
1726 macro_rules! iterator_impl {
1727 () => { item!( impl Entries { fn size_hint(&self) { self;}});}}
1728 iterator_impl! { }",
1729 vec
!(vec
!(0)), true),
1733 // run one of the renaming tests
1734 fn run_renaming_test(t
: &RenamingTest
, test_idx
: usize) {
1735 let invalid_name
= token
::special_idents
::invalid
.name
;
1736 let (teststr
, bound_connections
, bound_ident_check
) = match *t
{
1737 (ref str,ref conns
, bic
) => (str.to_string(), conns
.clone(), bic
)
1739 let cr
= expand_crate_str(teststr
.to_string());
1740 let bindings
= crate_bindings(&cr
);
1741 let varrefs
= crate_varrefs(&cr
);
1743 // must be one check clause for each binding:
1744 assert_eq
!(bindings
.len(),bound_connections
.len());
1745 for (binding_idx
,shouldmatch
) in bound_connections
.iter().enumerate() {
1746 let binding_name
= mtwt
::resolve(bindings
[binding_idx
]);
1747 let binding_marks
= mtwt
::marksof(bindings
[binding_idx
].ctxt
, invalid_name
);
1748 // shouldmatch can't name varrefs that don't exist:
1749 assert
!((shouldmatch
.is_empty()) ||
1750 (varrefs
.len() > *shouldmatch
.iter().max().unwrap()));
1751 for (idx
,varref
) in varrefs
.iter().enumerate() {
1752 let print_hygiene_debug_info
= || {
1753 // good lord, you can't make a path with 0 segments, can you?
1754 let final_varref_ident
= match varref
.segments
.last() {
1755 Some(pathsegment
) => pathsegment
.identifier
,
1756 None
=> panic
!("varref with 0 path segments?")
1758 let varref_name
= mtwt
::resolve(final_varref_ident
);
1759 let varref_idents
: Vec
<ast
::Ident
>
1760 = varref
.segments
.iter().map(|s
| s
.identifier
)
1762 println
!("varref #{}: {:?}, resolves to {}",idx
, varref_idents
, varref_name
);
1763 println
!("varref's first segment's string: \"{}\"", final_varref_ident
);
1764 println
!("binding #{}: {}, resolves to {}",
1765 binding_idx
, bindings
[binding_idx
], binding_name
);
1766 mtwt
::with_sctable(|x
| mtwt
::display_sctable(x
));
1768 if shouldmatch
.contains(&idx
) {
1769 // it should be a path of length 1, and it should
1770 // be free-identifier=? or bound-identifier=? to the given binding
1771 assert_eq
!(varref
.segments
.len(),1);
1772 let varref_name
= mtwt
::resolve(varref
.segments
[0].identifier
);
1773 let varref_marks
= mtwt
::marksof(varref
.segments
[0]
1777 if !(varref_name
==binding_name
) {
1778 println
!("uh oh, should match but doesn't:");
1779 print_hygiene_debug_info();
1781 assert_eq
!(varref_name
,binding_name
);
1782 if bound_ident_check
{
1783 // we're checking bound-identifier=?, and the marks
1784 // should be the same, too:
1785 assert_eq
!(varref_marks
,binding_marks
.clone());
1788 let varref_name
= mtwt
::resolve(varref
.segments
[0].identifier
);
1789 let fail
= (varref
.segments
.len() == 1)
1790 && (varref_name
== binding_name
);
1793 println
!("failure on test {}",test_idx
);
1794 println
!("text of test case: \"{}\"", teststr
);
1796 println
!("uh oh, matches but shouldn't:");
1797 print_hygiene_debug_info();
1806 fn fmt_in_macro_used_inside_module_macro() {
1807 let crate_str
= "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
1808 macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
1811 let cr
= expand_crate_str(crate_str
);
1812 // find the xx binding
1813 let bindings
= crate_bindings(&cr
);
1814 let cxbinds
: Vec
<&ast
::Ident
> =
1815 bindings
.iter().filter(|b
| b
.name
.as_str() == "xx").collect();
1816 let cxbinds
: &[&ast
::Ident
] = &cxbinds
[..];
1817 let cxbind
= match (cxbinds
.len(), cxbinds
.get(0)) {
1819 _
=> panic
!("expected just one binding for ext_cx")
1821 let resolved_binding
= mtwt
::resolve(*cxbind
);
1822 let varrefs
= crate_varrefs(&cr
);
1824 // the xx binding should bind all of the xx varrefs:
1825 for (idx
,v
) in varrefs
.iter().filter(|p
| {
1826 p
.segments
.len() == 1
1827 && p
.segments
[0].identifier
.name
.as_str() == "xx"
1829 if mtwt
::resolve(v
.segments
[0].identifier
) != resolved_binding
{
1830 println
!("uh oh, xx binding didn't match xx varref:");
1831 println
!("this is xx varref \\# {}", idx
);
1832 println
!("binding: {}", cxbind
);
1833 println
!("resolves to: {}", resolved_binding
);
1834 println
!("varref: {}", v
.segments
[0].identifier
);
1835 println
!("resolves to: {}",
1836 mtwt
::resolve(v
.segments
[0].identifier
));
1837 mtwt
::with_sctable(|x
| mtwt
::display_sctable(x
));
1839 assert_eq
!(mtwt
::resolve(v
.segments
[0].identifier
),
1846 let pat
= string_to_pat(
1847 "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
1848 let idents
= pattern_bindings(&pat
);
1849 assert_eq
!(idents
, strs_to_idents(vec
!("a","c","b","d")));
1852 // test the list of identifier patterns gathered by the visitor. Note that
1853 // 'None' is listed as an identifier pattern because we don't yet know that
1854 // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
1856 fn crate_bindings_test(){
1857 let the_crate
= string_to_crate("fn main (a: i32) -> i32 {|b| {
1858 match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
1859 let idents
= crate_bindings(&the_crate
);
1860 assert_eq
!(idents
, strs_to_idents(vec
!("a","b","None","i","i","z","y")));
1863 // test the IdentRenamer directly
1865 fn ident_renamer_test () {
1866 let the_crate
= string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1867 let f_ident
= token
::str_to_ident("f");
1868 let x_ident
= token
::str_to_ident("x");
1869 let int_ident
= token
::str_to_ident("i32");
1870 let renames
= vec
!((x_ident
,Name(16)));
1871 let mut renamer
= IdentRenamer{renames: &renames}
;
1872 let renamed_crate
= renamer
.fold_crate(the_crate
);
1873 let idents
= crate_idents(&renamed_crate
);
1874 let resolved
: Vec
<ast
::Name
> = idents
.iter().map(|id
| mtwt
::resolve(*id
)).collect();
1875 assert_eq
!(resolved
, [f_ident
.name
,Name(16),int_ident
.name
,Name(16),Name(16),Name(16)]);
1878 // test the PatIdentRenamer; only PatIdents get renamed
1880 fn pat_ident_renamer_test () {
1881 let the_crate
= string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1882 let f_ident
= token
::str_to_ident("f");
1883 let x_ident
= token
::str_to_ident("x");
1884 let int_ident
= token
::str_to_ident("i32");
1885 let renames
= vec
!((x_ident
,Name(16)));
1886 let mut renamer
= PatIdentRenamer{renames: &renames}
;
1887 let renamed_crate
= renamer
.fold_crate(the_crate
);
1888 let idents
= crate_idents(&renamed_crate
);
1889 let resolved
: Vec
<ast
::Name
> = idents
.iter().map(|id
| mtwt
::resolve(*id
)).collect();
1890 let x_name
= x_ident
.name
;
1891 assert_eq
!(resolved
, [f_ident
.name
,Name(16),int_ident
.name
,Name(16),x_name
,x_name
]);