]> git.proxmox.com Git - rustc.git/blame - src/libsyntax/ext/expand.rs
Imported Upstream version 1.2.0+dfsg1
[rustc.git] / src / libsyntax / ext / expand.rs
CommitLineData
1a4d82fc 1// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
223e47cc
LB
2// file at the top-level directory of this distribution and at
3// http://rust-lang.org/COPYRIGHT.
4//
5// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8// option. This file may not be copied, modified, or distributed
9// except according to those terms.
10
1a4d82fc
JJ
11use ast::{Block, Crate, DeclLocal, ExprMac, PatMac};
12use ast::{Local, Ident, MacInvocTT};
13use ast::{ItemMac, MacStmtWithSemicolon, Mrk, Stmt, StmtDecl, StmtMac};
14use ast::{StmtExpr, StmtSemi};
15use ast::TokenTree;
223e47cc 16use ast;
1a4d82fc
JJ
17use ext::mtwt;
18use ext::build::AstBuilder;
223e47cc 19use attr;
1a4d82fc 20use attr::AttrMetaMethods;
223e47cc 21use codemap;
d9579d0f 22use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute, CompilerExpansion};
223e47cc 23use ext::base::*;
c34b1796 24use feature_gate::{self, Features};
1a4d82fc 25use fold;
223e47cc
LB
26use fold::*;
27use parse;
1a4d82fc
JJ
28use parse::token::{fresh_mark, fresh_name, intern};
29use parse::token;
30use ptr::P;
31use util::small_vector::SmallVector;
970d7e83
LB
32use visit;
33use visit::Visitor;
85aaf69f 34use std_inject;
223e47cc 35
1a4d82fc 36pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
d9579d0f
AL
37 fn push_compiler_expansion(fld: &mut MacroExpander, span: Span, expansion_desc: &str) {
38 fld.cx.bt_push(ExpnInfo {
39 call_site: span,
40 callee: NameAndSpan {
41 name: expansion_desc.to_string(),
42 format: CompilerExpansion,
43 allow_internal_unstable: true,
44 span: None,
45 },
46 });
47 }
48
1a4d82fc 49 e.and_then(|ast::Expr {id, node, span}| match node {
223e47cc
LB
50 // expr_mac should really be expr_ext or something; it's the
51 // entry-point for all syntax extensions.
1a4d82fc
JJ
52 ast::ExprMac(mac) => {
53 let expanded_expr = match expand_mac_invoc(mac, span,
54 |r| r.make_expr(),
55 mark_expr, fld) {
56 Some(expr) => expr,
57 None => {
58 return DummyResult::raw_expr(span);
59 }
60 };
61
62 // Keep going, outside-in.
63 //
64 let fully_expanded = fld.fold_expr(expanded_expr);
65 fld.cx.bt_pop();
66
67 fully_expanded.map(|e| ast::Expr {
68 id: ast::DUMMY_NODE_ID,
69 node: e.node,
9346a6ac 70 span: fld.new_span(span),
1a4d82fc
JJ
71 })
72 }
73
74 ast::ExprWhile(cond, body, opt_ident) => {
75 let cond = fld.fold_expr(cond);
76 let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
77 fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident))
78 }
79
80 // Desugar ExprWhileLet
81 // From: `[opt_ident]: while let <pat> = <expr> <body>`
82 ast::ExprWhileLet(pat, expr, body, opt_ident) => {
83 // to:
84 //
85 // [opt_ident]: loop {
86 // match <expr> {
87 // <pat> => <body>,
88 // _ => break
89 // }
90 // }
91
d9579d0f
AL
92 push_compiler_expansion(fld, span, "while let expansion");
93
1a4d82fc
JJ
94 // `<pat> => <body>`
95 let pat_arm = {
96 let body_expr = fld.cx.expr_block(body);
97 fld.cx.arm(pat.span, vec![pat], body_expr)
98 };
99
100 // `_ => break`
101 let break_arm = {
102 let pat_under = fld.cx.pat_wild(span);
103 let break_expr = fld.cx.expr_break(span);
104 fld.cx.arm(span, vec![pat_under], break_expr)
105 };
106
107 // `match <expr> { ... }`
108 let arms = vec![pat_arm, break_arm];
109 let match_expr = fld.cx.expr(span,
110 ast::ExprMatch(expr, arms, ast::MatchSource::WhileLetDesugar));
111
112 // `[opt_ident]: loop { ... }`
113 let loop_block = fld.cx.block_expr(match_expr);
114 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
d9579d0f
AL
115 let result = fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident));
116 fld.cx.bt_pop();
117 result
1a4d82fc
JJ
118 }
119
120 // Desugar ExprIfLet
121 // From: `if let <pat> = <expr> <body> [<elseopt>]`
122 ast::ExprIfLet(pat, expr, body, mut elseopt) => {
123 // to:
124 //
125 // match <expr> {
126 // <pat> => <body>,
127 // [_ if <elseopt_if_cond> => <elseopt_if_body>,]
128 // _ => [<elseopt> | ()]
129 // }
130
d9579d0f
AL
131 push_compiler_expansion(fld, span, "if let expansion");
132
1a4d82fc
JJ
133 // `<pat> => <body>`
134 let pat_arm = {
135 let body_expr = fld.cx.expr_block(body);
136 fld.cx.arm(pat.span, vec![pat], body_expr)
137 };
138
139 // `[_ if <elseopt_if_cond> => <elseopt_if_body>,]`
140 let else_if_arms = {
141 let mut arms = vec![];
142 loop {
143 let elseopt_continue = elseopt
144 .and_then(|els| els.and_then(|els| match els.node {
145 // else if
146 ast::ExprIf(cond, then, elseopt) => {
147 let pat_under = fld.cx.pat_wild(span);
148 arms.push(ast::Arm {
149 attrs: vec![],
150 pats: vec![pat_under],
151 guard: Some(cond),
152 body: fld.cx.expr_block(then)
153 });
154 elseopt.map(|elseopt| (elseopt, true))
155 }
156 _ => Some((P(els), false))
157 }));
158 match elseopt_continue {
159 Some((e, true)) => {
160 elseopt = Some(e);
161 }
162 Some((e, false)) => {
163 elseopt = Some(e);
164 break;
165 }
223e47cc 166 None => {
1a4d82fc
JJ
167 elseopt = None;
168 break;
223e47cc 169 }
1a4d82fc
JJ
170 }
171 }
172 arms
173 };
174
175 let contains_else_clause = elseopt.is_some();
176
177 // `_ => [<elseopt> | ()]`
178 let else_arm = {
179 let pat_under = fld.cx.pat_wild(span);
180 let else_expr = elseopt.unwrap_or_else(|| fld.cx.expr_tuple(span, vec![]));
181 fld.cx.arm(span, vec![pat_under], else_expr)
182 };
183
184 let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
185 arms.push(pat_arm);
62682a34 186 arms.extend(else_if_arms);
1a4d82fc
JJ
187 arms.push(else_arm);
188
189 let match_expr = fld.cx.expr(span,
190 ast::ExprMatch(expr, arms,
191 ast::MatchSource::IfLetDesugar {
192 contains_else_clause: contains_else_clause,
193 }));
d9579d0f
AL
194 let result = fld.fold_expr(match_expr);
195 fld.cx.bt_pop();
196 result
1a4d82fc
JJ
197 }
198
199 // Desugar support for ExprIfLet in the ExprIf else position
200 ast::ExprIf(cond, blk, elseopt) => {
201 let elseopt = elseopt.map(|els| els.and_then(|els| match els.node {
202 ast::ExprIfLet(..) => {
d9579d0f 203 push_compiler_expansion(fld, span, "if let expansion");
1a4d82fc
JJ
204 // wrap the if-let expr in a block
205 let span = els.span;
206 let blk = P(ast::Block {
1a4d82fc
JJ
207 stmts: vec![],
208 expr: Some(P(els)),
209 id: ast::DUMMY_NODE_ID,
210 rules: ast::DefaultBlock,
211 span: span
212 });
d9579d0f
AL
213 let result = fld.cx.expr_block(blk);
214 fld.cx.bt_pop();
215 result
1a4d82fc
JJ
216 }
217 _ => P(els)
218 }));
219 let if_expr = fld.cx.expr(span, ast::ExprIf(cond, blk, elseopt));
220 if_expr.map(|e| noop_fold_expr(e, fld))
221 }
222
223 ast::ExprLoop(loop_block, opt_ident) => {
224 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
225 fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
226 }
227
85aaf69f
SL
228 // Desugar ExprForLoop
229 // From: `[opt_ident]: for <pat> in <head> <body>`
1a4d82fc 230 ast::ExprForLoop(pat, head, body, opt_ident) => {
85aaf69f
SL
231 // to:
232 //
233 // {
234 // let result = match ::std::iter::IntoIterator::into_iter(<head>) {
235 // mut iter => {
236 // [opt_ident]: loop {
237 // match ::std::iter::Iterator::next(&mut iter) {
238 // ::std::option::Option::Some(<pat>) => <body>,
239 // ::std::option::Option::None => break
240 // }
241 // }
242 // }
243 // };
244 // result
245 // }
246
d9579d0f
AL
247 push_compiler_expansion(fld, span, "for loop expansion");
248
249 let span = fld.new_span(span);
250
85aaf69f 251 // expand <head>
1a4d82fc 252 let head = fld.fold_expr(head);
85aaf69f
SL
253
254 // create an hygienic ident
255 let iter = {
256 let ident = fld.cx.ident_of("iter");
257 let new_ident = fresh_name(&ident);
258 let rename = (ident, new_ident);
259 let mut rename_list = vec![rename];
260 let mut rename_fld = IdentRenamer{ renames: &mut rename_list };
261
262 rename_fld.fold_ident(ident)
263 };
264
d9579d0f
AL
265 let pat_span = fld.new_span(pat.span);
266 // `::std::option::Option::Some(<pat>) => <body>`
85aaf69f
SL
267 let pat_arm = {
268 let body_expr = fld.cx.expr_block(body);
d9579d0f 269 let pat = noop_fold_pat(pat, fld);
85aaf69f
SL
270 let some_pat = fld.cx.pat_some(pat_span, pat);
271
272 fld.cx.arm(pat_span, vec![some_pat], body_expr)
273 };
274
275 // `::std::option::Option::None => break`
276 let break_arm = {
277 let break_expr = fld.cx.expr_break(span);
278
279 fld.cx.arm(span, vec![fld.cx.pat_none(span)], break_expr)
280 };
281
282 // `match ::std::iter::Iterator::next(&mut iter) { ... }`
283 let match_expr = {
284 let next_path = {
285 let strs = vec![
286 fld.cx.ident_of_std("core"),
287 fld.cx.ident_of("iter"),
288 fld.cx.ident_of("Iterator"),
289 fld.cx.ident_of("next"),
290 ];
291
292 fld.cx.path_global(span, strs)
293 };
294 let ref_mut_iter = fld.cx.expr_mut_addr_of(span, fld.cx.expr_ident(span, iter));
295 let next_expr =
296 fld.cx.expr_call(span, fld.cx.expr_path(next_path), vec![ref_mut_iter]);
297 let arms = vec![pat_arm, break_arm];
298
299 fld.cx.expr(pat_span,
300 ast::ExprMatch(next_expr, arms, ast::MatchSource::ForLoopDesugar))
301 };
302
303 // `[opt_ident]: loop { ... }`
304 let loop_block = fld.cx.block_expr(match_expr);
305 let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
306 let loop_expr = fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident));
307
308 // `mut iter => { ... }`
309 let iter_arm = {
310 let iter_pat =
311 fld.cx.pat_ident_binding_mode(span, iter, ast::BindByValue(ast::MutMutable));
312 fld.cx.arm(span, vec![iter_pat], loop_expr)
313 };
314
315 // `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
316 let into_iter_expr = {
317 let into_iter_path = {
318 let strs = vec![
319 fld.cx.ident_of_std("core"),
320 fld.cx.ident_of("iter"),
321 fld.cx.ident_of("IntoIterator"),
322 fld.cx.ident_of("into_iter"),
323 ];
324
325 fld.cx.path_global(span, strs)
326 };
327
328 fld.cx.expr_call(span, fld.cx.expr_path(into_iter_path), vec![head])
329 };
330
331 let match_expr = fld.cx.expr_match(span, into_iter_expr, vec![iter_arm]);
332
333 // `{ let result = ...; result }`
334 let result_ident = token::gensym_ident("result");
d9579d0f 335 let result = fld.cx.expr_block(
85aaf69f
SL
336 fld.cx.block_all(
337 span,
338 vec![fld.cx.stmt_let(span, false, result_ident, match_expr)],
d9579d0f
AL
339 Some(fld.cx.expr_ident(span, result_ident))));
340 fld.cx.bt_pop();
341 result
1a4d82fc
JJ
342 }
343
85aaf69f 344 ast::ExprClosure(capture_clause, fn_decl, block) => {
d9579d0f 345 push_compiler_expansion(fld, span, "closure expansion");
1a4d82fc
JJ
346 let (rewritten_fn_decl, rewritten_block)
347 = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
348 let new_node = ast::ExprClosure(capture_clause,
1a4d82fc
JJ
349 rewritten_fn_decl,
350 rewritten_block);
d9579d0f
AL
351 let result = P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)});
352 fld.cx.bt_pop();
353 result
1a4d82fc
JJ
354 }
355
356 _ => {
357 P(noop_fold_expr(ast::Expr {
358 id: id,
359 node: node,
360 span: span
361 }, fld))
362 }
363 })
364}
365
366/// Expand a (not-ident-style) macro invocation. Returns the result
367/// of expansion and the mark which must be applied to the result.
368/// Our current interface doesn't allow us to apply the mark to the
369/// result until after calling make_expr, make_items, etc.
370fn expand_mac_invoc<T, F, G>(mac: ast::Mac, span: codemap::Span,
371 parse_thunk: F,
372 mark_thunk: G,
373 fld: &mut MacroExpander)
374 -> Option<T> where
85aaf69f 375 F: for<'a> FnOnce(Box<MacResult+'a>) -> Option<T>,
1a4d82fc
JJ
376 G: FnOnce(T, Mrk) -> T,
377{
378 match mac.node {
379 // it would almost certainly be cleaner to pass the whole
380 // macro invocation in, rather than pulling it apart and
381 // marking the tts and the ctxt separately. This also goes
382 // for the other three macro invocation chunks of code
383 // in this file.
384 // Token-tree macros:
385 MacInvocTT(pth, tts, _) => {
85aaf69f 386 if pth.segments.len() > 1 {
1a4d82fc
JJ
387 fld.cx.span_err(pth.span,
388 "expected macro name without module \
389 separators");
390 // let compilation continue
391 return None;
392 }
393 let extname = pth.segments[0].identifier;
394 let extnamestr = token::get_ident(extname);
395 match fld.cx.syntax_env.find(&extname.name) {
396 None => {
397 fld.cx.span_err(
398 pth.span,
399 &format!("macro undefined: '{}!'",
c34b1796 400 &extnamestr));
1a4d82fc
JJ
401
402 // let compilation continue
403 None
404 }
405 Some(rc) => match *rc {
c34b1796 406 NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
1a4d82fc
JJ
407 fld.cx.bt_push(ExpnInfo {
408 call_site: span,
223e47cc 409 callee: NameAndSpan {
85aaf69f 410 name: extnamestr.to_string(),
1a4d82fc
JJ
411 format: MacroBang,
412 span: exp_span,
c34b1796 413 allow_internal_unstable: allow_internal_unstable,
223e47cc 414 },
1a4d82fc
JJ
415 });
416 let fm = fresh_mark();
85aaf69f 417 let marked_before = mark_tts(&tts[..], fm);
1a4d82fc
JJ
418
419 // The span that we pass to the expanders we want to
420 // be the root of the call stack. That's the most
421 // relevant span and it's the actual invocation of
422 // the macro.
423 let mac_span = fld.cx.original_span();
424
425 let opt_parsed = {
426 let expanded = expandfun.expand(fld.cx,
427 mac_span,
85aaf69f 428 &marked_before[..]);
1a4d82fc
JJ
429 parse_thunk(expanded)
430 };
431 let parsed = match opt_parsed {
432 Some(e) => e,
433 None => {
434 fld.cx.span_err(
435 pth.span,
436 &format!("non-expression macro in expression position: {}",
85aaf69f 437 &extnamestr[..]
c34b1796 438 ));
1a4d82fc
JJ
439 return None;
440 }
441 };
442 Some(mark_thunk(parsed,fm))
443 }
444 _ => {
445 fld.cx.span_err(
446 pth.span,
447 &format!("'{}' is not a tt-style macro",
c34b1796 448 &extnamestr));
1a4d82fc 449 None
223e47cc
LB
450 }
451 }
452 }
453 }
223e47cc
LB
454 }
455}
456
1a4d82fc
JJ
457/// Rename loop label and expand its loop body
458///
459/// The renaming procedure for loop is different in the sense that the loop
460/// body is in a block enclosed by loop head so the renaming of loop label
461/// must be propagated to the enclosed context.
462fn expand_loop_block(loop_block: P<Block>,
463 opt_ident: Option<Ident>,
464 fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
465 match opt_ident {
466 Some(label) => {
467 let new_label = fresh_name(&label);
468 let rename = (label, new_label);
469
470 // The rename *must not* be added to the pending list of current
471 // syntax context otherwise an unrelated `break` or `continue` in
472 // the same context will pick that up in the deferred renaming pass
473 // and be renamed incorrectly.
474 let mut rename_list = vec!(rename);
475 let mut rename_fld = IdentRenamer{renames: &mut rename_list};
476 let renamed_ident = rename_fld.fold_ident(label);
477
478 // The rename *must* be added to the enclosed syntax context for
479 // `break` or `continue` to pick up because by definition they are
480 // in a block enclosed by loop head.
481 fld.cx.syntax_env.push_frame();
482 fld.cx.syntax_env.info().pending_renames.push(rename);
483 let expanded_block = expand_block_elts(loop_block, fld);
484 fld.cx.syntax_env.pop_frame();
485
486 (expanded_block, Some(renamed_ident))
223e47cc 487 }
1a4d82fc
JJ
488 None => (fld.fold_block(loop_block), opt_ident)
489 }
223e47cc
LB
490}
491
1a4d82fc
JJ
492// eval $e with a new exts frame.
493// must be a macro so that $e isn't evaluated too early.
494macro_rules! with_exts_frame {
970d7e83 495 ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
1a4d82fc
JJ
496 ({$extsboxexpr.push_frame();
497 $extsboxexpr.info().macros_escape = $macros_escape;
223e47cc 498 let result = $e;
1a4d82fc 499 $extsboxexpr.pop_frame();
223e47cc
LB
500 result
501 })
1a4d82fc 502}
970d7e83 503
223e47cc 504// When we enter a module, record it, for the sake of `module!`
1a4d82fc
JJ
505pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
506 -> SmallVector<P<ast::Item>> {
507 let it = expand_item_modifiers(it, fld);
508
85aaf69f
SL
509 expand_annotatable(Annotatable::Item(it), fld)
510 .into_iter().map(|i| i.expect_item()).collect()
1a4d82fc
JJ
511}
512
1a4d82fc
JJ
513/// Expand item_underscore
514fn expand_item_underscore(item: ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
515 match item {
62682a34 516 ast::ItemFn(decl, unsafety, constness, abi, generics, body) => {
1a4d82fc
JJ
517 let (rewritten_fn_decl, rewritten_body)
518 = expand_and_rename_fn_decl_and_block(decl, body, fld);
519 let expanded_generics = fold::noop_fold_generics(generics,fld);
62682a34
SL
520 ast::ItemFn(rewritten_fn_decl, unsafety, constness, abi,
521 expanded_generics, rewritten_body)
223e47cc 522 }
1a4d82fc
JJ
523 _ => noop_fold_item_underscore(item, fld)
524 }
223e47cc
LB
525}
526
1a4d82fc
JJ
527// does this attribute list contain "macro_use" ?
528fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
85aaf69f 529 for attr in attrs {
1a4d82fc
JJ
530 let mut is_use = attr.check_name("macro_use");
531 if attr.check_name("macro_escape") {
532 fld.cx.span_warn(attr.span, "macro_escape is a deprecated synonym for macro_use");
533 is_use = true;
534 if let ast::AttrInner = attr.node.style {
c34b1796 535 fld.cx.fileline_help(attr.span, "consider an outer attribute, \
1a4d82fc
JJ
536 #[macro_use] mod ...");
537 }
538 };
970d7e83 539
1a4d82fc
JJ
540 if is_use {
541 match attr.node.value.node {
542 ast::MetaWord(..) => (),
543 _ => fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"),
223e47cc 544 }
1a4d82fc
JJ
545 return true;
546 }
547 }
548 false
549}
550
551// Support for item-position macro invocations, exactly the same
552// logic as for expression-position macro invocations.
553pub fn expand_item_mac(it: P<ast::Item>,
554 fld: &mut MacroExpander) -> SmallVector<P<ast::Item>> {
555 let (extname, path_span, tts) = match it.node {
556 ItemMac(codemap::Spanned {
557 node: MacInvocTT(ref pth, ref tts, _),
558 ..
559 }) => {
560 (pth.segments[0].identifier, pth.span, (*tts).clone())
223e47cc 561 }
1a4d82fc 562 _ => fld.cx.span_bug(it.span, "invalid item macro invocation")
223e47cc 563 };
223e47cc 564
1a4d82fc
JJ
565 let extnamestr = token::get_ident(extname);
566 let fm = fresh_mark();
567 let items = {
568 let expanded = match fld.cx.syntax_env.find(&extname.name) {
569 None => {
570 fld.cx.span_err(path_span,
571 &format!("macro undefined: '{}!'",
c34b1796 572 extnamestr));
1a4d82fc
JJ
573 // let compilation continue
574 return SmallVector::zero();
575 }
576
577 Some(rc) => match *rc {
c34b1796 578 NormalTT(ref expander, span, allow_internal_unstable) => {
1a4d82fc
JJ
579 if it.ident.name != parse::token::special_idents::invalid.name {
580 fld.cx
581 .span_err(path_span,
c34b1796
AL
582 &format!("macro {}! expects no ident argument, given '{}'",
583 extnamestr,
584 token::get_ident(it.ident)));
1a4d82fc
JJ
585 return SmallVector::zero();
586 }
587 fld.cx.bt_push(ExpnInfo {
588 call_site: it.span,
589 callee: NameAndSpan {
85aaf69f 590 name: extnamestr.to_string(),
1a4d82fc 591 format: MacroBang,
c34b1796
AL
592 span: span,
593 allow_internal_unstable: allow_internal_unstable,
1a4d82fc
JJ
594 }
595 });
596 // mark before expansion:
85aaf69f
SL
597 let marked_before = mark_tts(&tts[..], fm);
598 expander.expand(fld.cx, it.span, &marked_before[..])
970d7e83 599 }
c34b1796 600 IdentTT(ref expander, span, allow_internal_unstable) => {
1a4d82fc
JJ
601 if it.ident.name == parse::token::special_idents::invalid.name {
602 fld.cx.span_err(path_span,
603 &format!("macro {}! expects an ident argument",
c34b1796 604 &extnamestr));
1a4d82fc
JJ
605 return SmallVector::zero();
606 }
607 fld.cx.bt_push(ExpnInfo {
608 call_site: it.span,
609 callee: NameAndSpan {
85aaf69f 610 name: extnamestr.to_string(),
1a4d82fc 611 format: MacroBang,
c34b1796
AL
612 span: span,
613 allow_internal_unstable: allow_internal_unstable,
1a4d82fc
JJ
614 }
615 });
616 // mark before expansion:
85aaf69f 617 let marked_tts = mark_tts(&tts[..], fm);
1a4d82fc 618 expander.expand(fld.cx, it.span, it.ident, marked_tts)
970d7e83 619 }
1a4d82fc
JJ
620 MacroRulesTT => {
621 if it.ident.name == parse::token::special_idents::invalid.name {
622 fld.cx.span_err(path_span,
623 &format!("macro_rules! expects an ident argument")
c34b1796 624 );
1a4d82fc
JJ
625 return SmallVector::zero();
626 }
c34b1796 627
1a4d82fc
JJ
628 fld.cx.bt_push(ExpnInfo {
629 call_site: it.span,
630 callee: NameAndSpan {
85aaf69f 631 name: extnamestr.to_string(),
1a4d82fc
JJ
632 format: MacroBang,
633 span: None,
c34b1796
AL
634 // `macro_rules!` doesn't directly allow
635 // unstable (this is orthogonal to whether
636 // the macro it creates allows it)
637 allow_internal_unstable: false,
1a4d82fc
JJ
638 }
639 });
640 // DON'T mark before expansion.
641
c34b1796
AL
642 let allow_internal_unstable = attr::contains_name(&it.attrs,
643 "allow_internal_unstable");
644
645 // ensure any #[allow_internal_unstable]s are
646 // detected (including nested macro definitions
647 // etc.)
648 if allow_internal_unstable && !fld.cx.ecfg.enable_allow_internal_unstable() {
649 feature_gate::emit_feature_err(
650 &fld.cx.parse_sess.span_diagnostic,
651 "allow_internal_unstable",
652 it.span,
653 feature_gate::EXPLAIN_ALLOW_INTERNAL_UNSTABLE)
654 }
655
1a4d82fc
JJ
656 let def = ast::MacroDef {
657 ident: it.ident,
658 attrs: it.attrs.clone(),
659 id: ast::DUMMY_NODE_ID,
660 span: it.span,
661 imported_from: None,
85aaf69f 662 export: attr::contains_name(&it.attrs, "macro_export"),
1a4d82fc 663 use_locally: true,
c34b1796 664 allow_internal_unstable: allow_internal_unstable,
1a4d82fc
JJ
665 body: tts,
666 };
667 fld.cx.insert_macro(def);
223e47cc 668
1a4d82fc
JJ
669 // macro_rules! has a side effect but expands to nothing.
670 fld.cx.bt_pop();
671 return SmallVector::zero();
672 }
673 _ => {
674 fld.cx.span_err(it.span,
675 &format!("{}! is not legal in item position",
c34b1796 676 &extnamestr));
1a4d82fc
JJ
677 return SmallVector::zero();
678 }
679 }
680 };
681
682 expanded.make_items()
683 };
684
685 let items = match items {
686 Some(items) => {
687 items.into_iter()
688 .map(|i| mark_item(i, fm))
689 .flat_map(|i| fld.fold_item(i).into_iter())
690 .collect()
691 }
692 None => {
693 fld.cx.span_err(path_span,
694 &format!("non-item macro in item position: {}",
c34b1796 695 &extnamestr));
1a4d82fc 696 return SmallVector::zero();
223e47cc 697 }
1a4d82fc 698 };
223e47cc 699
1a4d82fc
JJ
700 fld.cx.bt_pop();
701 items
702}
703
704/// Expand a stmt
9346a6ac
AL
705fn expand_stmt(stmt: P<Stmt>, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
706 let stmt = stmt.and_then(|stmt| stmt);
707 let (mac, style) = match stmt.node {
1a4d82fc 708 StmtMac(mac, style) => (mac, style),
9346a6ac 709 _ => return expand_non_macro_stmt(stmt, fld)
1a4d82fc 710 };
9346a6ac
AL
711
712 let maybe_new_items =
713 expand_mac_invoc(mac.and_then(|m| m), stmt.span,
714 |r| r.make_stmts(),
715 |stmts, mark| stmts.move_map(|m| mark_stmt(m, mark)),
716 fld);
717
718 let mut fully_expanded = match maybe_new_items {
719 Some(stmts) => {
720 // Keep going, outside-in.
721 let new_items = stmts.into_iter().flat_map(|s| {
722 fld.fold_stmt(s).into_iter()
723 }).collect();
724 fld.cx.bt_pop();
725 new_items
223e47cc 726 }
9346a6ac 727 None => SmallVector::zero()
223e47cc
LB
728 };
729
9346a6ac
AL
730 // If this is a macro invocation with a semicolon, then apply that
731 // semicolon to the final statement produced by expansion.
1a4d82fc 732 if style == MacStmtWithSemicolon {
9346a6ac
AL
733 if let Some(stmt) = fully_expanded.pop() {
734 let new_stmt = stmt.map(|Spanned {node, span}| {
735 Spanned {
736 node: match node {
737 StmtExpr(e, stmt_id) => StmtSemi(e, stmt_id),
738 _ => node /* might already have a semi */
739 },
740 span: span
741 }
742 });
743 fully_expanded.push(new_stmt);
744 }
1a4d82fc 745 }
9346a6ac
AL
746
747 fully_expanded
1a4d82fc
JJ
748}
749
750// expand a non-macro stmt. this is essentially the fallthrough for
751// expand_stmt, above.
752fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroExpander)
753 -> SmallVector<P<Stmt>> {
754 // is it a let?
755 match node {
756 StmtDecl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl {
757 DeclLocal(local) => {
758 // take it apart:
759 let rewritten_local = local.map(|Local {id, pat, ty, init, source, span}| {
760 // expand the ty since TyFixedLengthVec contains an Expr
761 // and thus may have a macro use
762 let expanded_ty = ty.map(|t| fld.fold_ty(t));
763 // expand the pat (it might contain macro uses):
764 let expanded_pat = fld.fold_pat(pat);
765 // find the PatIdents in the pattern:
766 // oh dear heaven... this is going to include the enum
767 // names, as well... but that should be okay, as long as
768 // the new names are gensyms for the old ones.
769 // generate fresh names, push them to a new pending list
770 let idents = pattern_bindings(&*expanded_pat);
771 let mut new_pending_renames =
772 idents.iter().map(|ident| (*ident, fresh_name(ident))).collect();
773 // rewrite the pattern using the new names (the old
774 // ones have already been applied):
775 let rewritten_pat = {
776 // nested binding to allow borrow to expire:
777 let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
778 rename_fld.fold_pat(expanded_pat)
779 };
780 // add them to the existing pending renames:
781 fld.cx.syntax_env.info().pending_renames
62682a34 782 .extend(new_pending_renames);
1a4d82fc
JJ
783 Local {
784 id: id,
785 ty: expanded_ty,
786 pat: rewritten_pat,
787 // also, don't forget to expand the init:
788 init: init.map(|e| fld.fold_expr(e)),
789 source: source,
790 span: span
970d7e83 791 }
1a4d82fc
JJ
792 });
793 SmallVector::one(P(Spanned {
794 node: StmtDecl(P(Spanned {
795 node: DeclLocal(rewritten_local),
796 span: span
797 }),
798 node_id),
799 span: stmt_span
800 }))
970d7e83 801 }
1a4d82fc
JJ
802 _ => {
803 noop_fold_stmt(Spanned {
804 node: StmtDecl(P(Spanned {
805 node: decl,
806 span: span
807 }),
808 node_id),
809 span: stmt_span
810 }, fld)
811 }
812 }),
813 _ => {
814 noop_fold_stmt(Spanned {
815 node: node,
816 span: stmt_span
817 }, fld)
818 }
970d7e83
LB
819 }
820}
223e47cc 821
1a4d82fc
JJ
822// expand the arm of a 'match', renaming for macro hygiene
823fn expand_arm(arm: ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
824 // expand pats... they might contain macro uses:
825 let expanded_pats = arm.pats.move_map(|pat| fld.fold_pat(pat));
9346a6ac 826 if expanded_pats.is_empty() {
1a4d82fc
JJ
827 panic!("encountered match arm with 0 patterns");
828 }
829 // all of the pats must have the same set of bindings, so use the
830 // first one to extract them and generate new names:
831 let idents = pattern_bindings(&*expanded_pats[0]);
832 let new_renames = idents.into_iter().map(|id| (id, fresh_name(&id))).collect();
833 // apply the renaming, but only to the PatIdents:
834 let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
835 let rewritten_pats = expanded_pats.move_map(|pat| rename_pats_fld.fold_pat(pat));
836 // apply renaming and then expansion to the guard and the body:
837 let mut rename_fld = IdentRenamer{renames:&new_renames};
838 let rewritten_guard =
839 arm.guard.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
840 let rewritten_body = fld.fold_expr(rename_fld.fold_expr(arm.body));
841 ast::Arm {
85aaf69f 842 attrs: fold::fold_attrs(arm.attrs, fld),
1a4d82fc
JJ
843 pats: rewritten_pats,
844 guard: rewritten_guard,
845 body: rewritten_body,
846 }
970d7e83
LB
847}
848
1a4d82fc
JJ
849/// A visitor that extracts the PatIdent (binding) paths
850/// from a given thingy and puts them in a mutable
851/// array
852#[derive(Clone)]
853struct PatIdentFinder {
854 ident_accumulator: Vec<ast::Ident>
855}
970d7e83 856
1a4d82fc
JJ
857impl<'v> Visitor<'v> for PatIdentFinder {
858 fn visit_pat(&mut self, pattern: &ast::Pat) {
859 match *pattern {
860 ast::Pat { id: _, node: ast::PatIdent(_, ref path1, ref inner), span: _ } => {
861 self.ident_accumulator.push(path1.node);
862 // visit optional subpattern of PatIdent:
85aaf69f 863 if let Some(ref subpat) = *inner {
1a4d82fc
JJ
864 self.visit_pat(&**subpat)
865 }
866 }
867 // use the default traversal for non-PatIdents
868 _ => visit::walk_pat(self, pattern)
869 }
970d7e83
LB
870 }
871}
872
1a4d82fc
JJ
873/// find the PatIdent paths in a pattern
874fn pattern_bindings(pat: &ast::Pat) -> Vec<ast::Ident> {
875 let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
876 name_finder.visit_pat(pat);
877 name_finder.ident_accumulator
970d7e83
LB
878}
879
1a4d82fc
JJ
880/// find the PatIdent paths in a
881fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
882 let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
85aaf69f 883 for arg in &fn_decl.inputs {
1a4d82fc 884 pat_idents.visit_pat(&*arg.pat);
223e47cc 885 }
1a4d82fc 886 pat_idents.ident_accumulator
223e47cc
LB
887}
888
1a4d82fc
JJ
889// expand a block. pushes a new exts_frame, then calls expand_block_elts
890pub fn expand_block(blk: P<Block>, fld: &mut MacroExpander) -> P<Block> {
891 // see note below about treatment of exts table
892 with_exts_frame!(fld.cx.syntax_env,false,
893 expand_block_elts(blk, fld))
894}
970d7e83 895
1a4d82fc
JJ
896// expand the elements of a block.
897pub fn expand_block_elts(b: P<Block>, fld: &mut MacroExpander) -> P<Block> {
85aaf69f 898 b.map(|Block {id, stmts, expr, rules, span}| {
1a4d82fc
JJ
899 let new_stmts = stmts.into_iter().flat_map(|x| {
900 // perform all pending renames
901 let renamed_stmt = {
902 let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
903 let mut rename_fld = IdentRenamer{renames:pending_renames};
904 rename_fld.fold_stmt(x).expect_one("rename_fold didn't return one value")
905 };
906 // expand macros in the statement
907 fld.fold_stmt(renamed_stmt).into_iter()
908 }).collect();
909 let new_expr = expr.map(|x| {
910 let expr = {
911 let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
912 let mut rename_fld = IdentRenamer{renames:pending_renames};
913 rename_fld.fold_expr(x)
914 };
915 fld.fold_expr(expr)
916 });
917 Block {
918 id: fld.new_id(id),
1a4d82fc
JJ
919 stmts: new_stmts,
920 expr: new_expr,
921 rules: rules,
922 span: span
223e47cc 923 }
1a4d82fc
JJ
924 })
925}
223e47cc 926
1a4d82fc
JJ
927fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
928 match p.node {
929 PatMac(_) => {}
930 _ => return noop_fold_pat(p, fld)
931 }
932 p.map(|ast::Pat {node, span, ..}| {
933 let (pth, tts) = match node {
934 PatMac(mac) => match mac.node {
935 MacInvocTT(pth, tts, _) => {
936 (pth, tts)
970d7e83 937 }
1a4d82fc
JJ
938 },
939 _ => unreachable!()
940 };
85aaf69f 941 if pth.segments.len() > 1 {
1a4d82fc
JJ
942 fld.cx.span_err(pth.span, "expected macro name without module separators");
943 return DummyResult::raw_pat(span);
944 }
945 let extname = pth.segments[0].identifier;
946 let extnamestr = token::get_ident(extname);
947 let marked_after = match fld.cx.syntax_env.find(&extname.name) {
948 None => {
949 fld.cx.span_err(pth.span,
950 &format!("macro undefined: '{}!'",
c34b1796 951 extnamestr));
1a4d82fc
JJ
952 // let compilation continue
953 return DummyResult::raw_pat(span);
970d7e83 954 }
1a4d82fc
JJ
955
956 Some(rc) => match *rc {
c34b1796 957 NormalTT(ref expander, tt_span, allow_internal_unstable) => {
1a4d82fc
JJ
958 fld.cx.bt_push(ExpnInfo {
959 call_site: span,
960 callee: NameAndSpan {
85aaf69f 961 name: extnamestr.to_string(),
1a4d82fc 962 format: MacroBang,
c34b1796
AL
963 span: tt_span,
964 allow_internal_unstable: allow_internal_unstable,
1a4d82fc
JJ
965 }
966 });
967
968 let fm = fresh_mark();
85aaf69f 969 let marked_before = mark_tts(&tts[..], fm);
1a4d82fc 970 let mac_span = fld.cx.original_span();
bd371182
AL
971 let pat = expander.expand(fld.cx,
972 mac_span,
973 &marked_before[..]).make_pat();
974 let expanded = match pat {
1a4d82fc
JJ
975 Some(e) => e,
976 None => {
977 fld.cx.span_err(
978 pth.span,
979 &format!(
980 "non-pattern macro in pattern position: {}",
85aaf69f 981 &extnamestr
c34b1796 982 )
1a4d82fc
JJ
983 );
984 return DummyResult::raw_pat(span);
985 }
986 };
987
988 // mark after:
989 mark_pat(expanded,fm)
970d7e83 990 }
1a4d82fc
JJ
991 _ => {
992 fld.cx.span_err(span,
993 &format!("{}! is not legal in pattern position",
c34b1796 994 &extnamestr));
1a4d82fc 995 return DummyResult::raw_pat(span);
970d7e83
LB
996 }
997 }
1a4d82fc
JJ
998 };
999
1000 let fully_expanded =
1001 fld.fold_pat(marked_after).node.clone();
1002 fld.cx.bt_pop();
223e47cc 1003
1a4d82fc
JJ
1004 ast::Pat {
1005 id: ast::DUMMY_NODE_ID,
1006 node: fully_expanded,
1007 span: span
1008 }
1009 })
1010}
223e47cc 1011
1a4d82fc
JJ
1012/// A tree-folder that applies every rename in its (mutable) list
1013/// to every identifier, including both bindings and varrefs
1014/// (and lots of things that will turn out to be neither)
1015pub struct IdentRenamer<'a> {
1016 renames: &'a mtwt::RenameList,
1017}
970d7e83 1018
1a4d82fc
JJ
1019impl<'a> Folder for IdentRenamer<'a> {
1020 fn fold_ident(&mut self, id: Ident) -> Ident {
1021 Ident {
1022 name: id.name,
1023 ctxt: mtwt::apply_renames(self.renames, id.ctxt),
1024 }
1025 }
1026 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
1027 fold::noop_fold_mac(mac, self)
1028 }
1029}
970d7e83 1030
1a4d82fc
JJ
1031/// A tree-folder that applies every rename in its list to
1032/// the idents that are in PatIdent patterns. This is more narrowly
1033/// focused than IdentRenamer, and is needed for FnDecl,
1034/// where we want to rename the args but not the fn name or the generics etc.
1035pub struct PatIdentRenamer<'a> {
1036 renames: &'a mtwt::RenameList,
1037}
970d7e83 1038
1a4d82fc
JJ
1039impl<'a> Folder for PatIdentRenamer<'a> {
1040 fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
1041 match pat.node {
1042 ast::PatIdent(..) => {},
1043 _ => return noop_fold_pat(pat, self)
1044 }
223e47cc 1045
1a4d82fc
JJ
1046 pat.map(|ast::Pat {id, node, span}| match node {
1047 ast::PatIdent(binding_mode, Spanned{span: sp, node: ident}, sub) => {
1048 let new_ident = Ident{name: ident.name,
1049 ctxt: mtwt::apply_renames(self.renames, ident.ctxt)};
1050 let new_node =
1051 ast::PatIdent(binding_mode,
1052 Spanned{span: self.new_span(sp), node: new_ident},
1053 sub.map(|p| self.fold_pat(p)));
1054 ast::Pat {
1055 id: id,
1056 node: new_node,
1057 span: self.new_span(span)
1058 }
1059 },
1060 _ => unreachable!()
1061 })
1062 }
1063 fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
1064 fold::noop_fold_mac(mac, self)
1065 }
1066}
223e47cc 1067
85aaf69f
SL
1068fn expand_annotatable(a: Annotatable,
1069 fld: &mut MacroExpander)
1070 -> SmallVector<Annotatable> {
1071 let a = expand_item_multi_modifier(a, fld);
1072
1073 let mut decorator_items = SmallVector::zero();
1074 let mut new_attrs = Vec::new();
d9579d0f 1075 expand_decorators(a.clone(), fld, &mut decorator_items, &mut new_attrs);
85aaf69f
SL
1076
1077 let mut new_items: SmallVector<Annotatable> = match a {
1078 Annotatable::Item(it) => match it.node {
1079 ast::ItemMac(..) => {
1080 expand_item_mac(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
1081 }
1082 ast::ItemMod(_) | ast::ItemForeignMod(_) => {
1083 let valid_ident =
1084 it.ident.name != parse::token::special_idents::invalid.name;
1085
1086 if valid_ident {
1087 fld.cx.mod_push(it.ident);
1088 }
1089 let macro_use = contains_macro_use(fld, &new_attrs[..]);
1090 let result = with_exts_frame!(fld.cx.syntax_env,
1091 macro_use,
1092 noop_fold_item(it, fld));
1093 if valid_ident {
1094 fld.cx.mod_pop();
1095 }
1096 result.into_iter().map(|i| Annotatable::Item(i)).collect()
1097 },
1098 _ => {
1099 let it = P(ast::Item {
1100 attrs: new_attrs,
1101 ..(*it).clone()
1102 });
1103 noop_fold_item(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
1104 }
1105 },
c34b1796
AL
1106
1107 Annotatable::TraitItem(it) => match it.node {
1108 ast::MethodTraitItem(_, Some(_)) => SmallVector::one(it.map(|ti| ast::TraitItem {
1109 id: ti.id,
1110 ident: ti.ident,
1111 attrs: ti.attrs,
1112 node: match ti.node {
1113 ast::MethodTraitItem(sig, Some(body)) => {
1114 let (sig, body) = expand_and_rename_method(sig, body, fld);
1115 ast::MethodTraitItem(sig, Some(body))
1116 }
1117 _ => unreachable!()
1118 },
1119 span: fld.new_span(ti.span)
1120 })),
1121 _ => fold::noop_fold_trait_item(it, fld)
1122 }.into_iter().map(Annotatable::TraitItem).collect(),
1123
1124 Annotatable::ImplItem(ii) => {
1125 expand_impl_item(ii, fld).into_iter().map(Annotatable::ImplItem).collect()
85aaf69f
SL
1126 }
1127 };
1128
d9579d0f 1129 new_items.push_all(decorator_items);
85aaf69f
SL
1130 new_items
1131}
1132
d9579d0f
AL
1133// Partition a set of attributes into one kind of attribute, and other kinds.
1134macro_rules! partition {
1135 ($fn_name: ident, $variant: ident) => {
1136 #[allow(deprecated)] // The `allow` is needed because the `Modifier` variant might be used.
1137 fn $fn_name(attrs: &[ast::Attribute],
1138 fld: &MacroExpander)
1139 -> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
1140 attrs.iter().cloned().partition(|attr| {
1141 match fld.cx.syntax_env.find(&intern(&attr.name())) {
1142 Some(rc) => match *rc {
1143 $variant(..) => true,
1144 _ => false
1145 },
1146 _ => false
1147 }
1148 })
85aaf69f 1149 }
d9579d0f 1150 }
85aaf69f
SL
1151}
1152
d9579d0f
AL
1153partition!(modifiers, Modifier);
1154partition!(multi_modifiers, MultiModifier);
1155
1156
1157#[allow(deprecated)] // The `allow` is needed because the `Decorator` variant is used.
1158fn expand_decorators(a: Annotatable,
1159 fld: &mut MacroExpander,
1160 decorator_items: &mut SmallVector<Annotatable>,
1161 new_attrs: &mut Vec<ast::Attribute>)
1162{
1163 for attr in a.attrs() {
1164 let mname = attr.name();
1165 match fld.cx.syntax_env.find(&intern(&mname)) {
85aaf69f 1166 Some(rc) => match *rc {
d9579d0f
AL
1167 Decorator(ref dec) => {
1168 attr::mark_used(&attr);
1169
1170 fld.cx.bt_push(ExpnInfo {
1171 call_site: attr.span,
1172 callee: NameAndSpan {
1173 name: mname.to_string(),
1174 format: MacroAttribute,
1175 span: Some(attr.span),
1176 // attributes can do whatever they like,
1177 // for now.
1178 allow_internal_unstable: true,
1179 }
1180 });
1181
1182 // we'd ideally decorator_items.push_all(expand_item(item, fld)),
1183 // but that double-mut-borrows fld
1184 let mut items: SmallVector<Annotatable> = SmallVector::zero();
1185 dec.expand(fld.cx,
1186 attr.span,
1187 &attr.node.value,
1188 &a.clone().expect_item(),
1189 &mut |item| items.push(Annotatable::Item(item)));
1190 decorator_items.extend(items.into_iter()
1191 .flat_map(|ann| expand_annotatable(ann, fld).into_iter()));
1192
1193 fld.cx.bt_pop();
1194 }
1195 MultiDecorator(ref dec) => {
1196 attr::mark_used(&attr);
1197
1198 fld.cx.bt_push(ExpnInfo {
1199 call_site: attr.span,
1200 callee: NameAndSpan {
1201 name: mname.to_string(),
1202 format: MacroAttribute,
1203 span: Some(attr.span),
1204 // attributes can do whatever they like,
1205 // for now.
1206 allow_internal_unstable: true,
1207 }
1208 });
1209
1210 // we'd ideally decorator_items.push_all(expand_annotatable(ann, fld)),
1211 // but that double-mut-borrows fld
1212 let mut items: SmallVector<Annotatable> = SmallVector::zero();
1213 dec.expand(fld.cx,
1214 attr.span,
1215 &attr.node.value,
62682a34 1216 &a,
d9579d0f
AL
1217 &mut |ann| items.push(ann));
1218 decorator_items.extend(items.into_iter()
1219 .flat_map(|ann| expand_annotatable(ann, fld).into_iter()));
1220
1221 fld.cx.bt_pop();
1222 }
1223 _ => new_attrs.push((*attr).clone()),
85aaf69f 1224 },
d9579d0f 1225 _ => new_attrs.push((*attr).clone()),
85aaf69f 1226 }
d9579d0f 1227 }
85aaf69f
SL
1228}
1229
1230fn expand_item_multi_modifier(mut it: Annotatable,
1231 fld: &mut MacroExpander)
1232 -> Annotatable {
1233 let (modifiers, other_attrs) = multi_modifiers(it.attrs(), fld);
1234
1235 // Update the attrs, leave everything else alone. Is this mutation really a good idea?
1236 it = it.fold_attrs(other_attrs);
1237
1238 if modifiers.is_empty() {
1239 return it
1240 }
1241
1242 for attr in &modifiers {
1243 let mname = attr.name();
1244
1245 match fld.cx.syntax_env.find(&intern(&mname)) {
1246 Some(rc) => match *rc {
1247 MultiModifier(ref mac) => {
1248 attr::mark_used(attr);
1249 fld.cx.bt_push(ExpnInfo {
1250 call_site: attr.span,
1251 callee: NameAndSpan {
1252 name: mname.to_string(),
1253 format: MacroAttribute,
d9579d0f 1254 span: Some(attr.span),
c34b1796
AL
1255 // attributes can do whatever they like,
1256 // for now
1257 allow_internal_unstable: true,
85aaf69f
SL
1258 }
1259 });
1260 it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
1261 fld.cx.bt_pop();
1262 }
1263 _ => unreachable!()
1264 },
1265 _ => unreachable!()
1266 }
1267 }
1268
1269 // Expansion may have added new ItemModifiers.
1270 expand_item_multi_modifier(it, fld)
1271}
1272
d9579d0f
AL
1273#[allow(deprecated)] // This is needed because the `ItemModifier` trait is used
1274fn expand_item_modifiers(mut it: P<ast::Item>,
1275 fld: &mut MacroExpander)
1276 -> P<ast::Item> {
1277 // partition the attributes into ItemModifiers and others
1278 let (modifiers, other_attrs) = modifiers(&it.attrs, fld);
1279
1280 // update the attrs, leave everything else alone. Is this mutation really a good idea?
1281 it = P(ast::Item {
1282 attrs: other_attrs,
1283 ..(*it).clone()
1284 });
1285
1286 if modifiers.is_empty() {
1287 let it = expand_item_multi_modifier(Annotatable::Item(it), fld);
1288 return it.expect_item();
1289 }
1290
1291 for attr in &modifiers {
1292 let mname = attr.name();
1293
1294 match fld.cx.syntax_env.find(&intern(&mname)) {
1295 Some(rc) => match *rc {
1296 Modifier(ref mac) => {
1297 attr::mark_used(attr);
1298 fld.cx.bt_push(ExpnInfo {
1299 call_site: attr.span,
1300 callee: NameAndSpan {
1301 name: mname.to_string(),
1302 format: MacroAttribute,
1303 span: Some(attr.span),
1304 // attributes can do whatever they like,
1305 // for now
1306 allow_internal_unstable: true,
1307 }
1308 });
1309 it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
1310 fld.cx.bt_pop();
1311 }
1312 _ => unreachable!()
1313 },
1314 _ => unreachable!()
1315 }
1316 }
1317
1318 // Expansion may have added new ItemModifiers.
1319 // It is possible, that an item modifier could expand to a multi-modifier or
1320 // vice versa. In this case we will expand all modifiers before multi-modifiers,
1321 // which might give an odd ordering. However, I think it is unlikely that the
1322 // two kinds will be mixed, and old-style multi-modifiers are deprecated.
1323 expand_item_modifiers(it, fld)
1324}
1325
c34b1796
AL
1326fn expand_impl_item(ii: P<ast::ImplItem>, fld: &mut MacroExpander)
1327 -> SmallVector<P<ast::ImplItem>> {
1328 match ii.node {
1329 ast::MethodImplItem(..) => SmallVector::one(ii.map(|ii| ast::ImplItem {
1330 id: ii.id,
1331 ident: ii.ident,
1332 attrs: ii.attrs,
1333 vis: ii.vis,
1334 node: match ii.node {
1335 ast::MethodImplItem(sig, body) => {
1336 let (sig, body) = expand_and_rename_method(sig, body, fld);
1337 ast::MethodImplItem(sig, body)
1338 }
1339 _ => unreachable!()
1340 },
1341 span: fld.new_span(ii.span)
1342 })),
1343 ast::MacImplItem(_) => {
1344 let (span, mac) = ii.and_then(|ii| match ii.node {
1345 ast::MacImplItem(mac) => (ii.span, mac),
1346 _ => unreachable!()
1347 });
1348 let maybe_new_items =
1349 expand_mac_invoc(mac, span,
1350 |r| r.make_impl_items(),
1351 |meths, mark| meths.move_map(|m| mark_impl_item(m, mark)),
1a4d82fc
JJ
1352 fld);
1353
c34b1796
AL
1354 match maybe_new_items {
1355 Some(impl_items) => {
1a4d82fc 1356 // expand again if necessary
c34b1796
AL
1357 let new_items = impl_items.into_iter().flat_map(|ii| {
1358 expand_impl_item(ii, fld).into_iter()
1359 }).collect();
1a4d82fc 1360 fld.cx.bt_pop();
c34b1796 1361 new_items
1a4d82fc
JJ
1362 }
1363 None => SmallVector::zero()
223e47cc
LB
1364 }
1365 }
c34b1796
AL
1366 _ => fold::noop_fold_impl_item(ii, fld)
1367 }
1a4d82fc 1368}
223e47cc 1369
1a4d82fc
JJ
1370/// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
1371/// PatIdents in its arguments to perform renaming in the FnDecl and
1372/// the block, returning both the new FnDecl and the new Block.
1373fn expand_and_rename_fn_decl_and_block(fn_decl: P<ast::FnDecl>, block: P<ast::Block>,
1374 fld: &mut MacroExpander)
c34b1796 1375 -> (P<ast::FnDecl>, P<ast::Block>) {
1a4d82fc
JJ
1376 let expanded_decl = fld.fold_fn_decl(fn_decl);
1377 let idents = fn_decl_arg_bindings(&*expanded_decl);
1378 let renames =
1379 idents.iter().map(|id : &ast::Ident| (*id,fresh_name(id))).collect();
1380 // first, a renamer for the PatIdents, for the fn_decl:
1381 let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
1382 let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
1383 // now, a renamer for *all* idents, for the body:
1384 let mut rename_fld = IdentRenamer{renames: &renames};
1385 let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
1386 (rewritten_fn_decl,rewritten_body)
1387}
1388
c34b1796
AL
1389fn expand_and_rename_method(sig: ast::MethodSig, body: P<ast::Block>,
1390 fld: &mut MacroExpander)
1391 -> (ast::MethodSig, P<ast::Block>) {
1392 let (rewritten_fn_decl, rewritten_body)
1393 = expand_and_rename_fn_decl_and_block(sig.decl, body, fld);
1394 (ast::MethodSig {
1395 generics: fld.fold_generics(sig.generics),
1396 abi: sig.abi,
1397 explicit_self: fld.fold_explicit_self(sig.explicit_self),
1398 unsafety: sig.unsafety,
62682a34 1399 constness: sig.constness,
c34b1796
AL
1400 decl: rewritten_fn_decl
1401 }, rewritten_body)
1402}
1403
1a4d82fc
JJ
1404/// A tree-folder that performs macro expansion
1405pub struct MacroExpander<'a, 'b:'a> {
1406 pub cx: &'a mut ExtCtxt<'b>,
1a4d82fc
JJ
1407}
1408
1409impl<'a, 'b> MacroExpander<'a, 'b> {
1410 pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
9346a6ac 1411 MacroExpander { cx: cx }
1a4d82fc
JJ
1412 }
1413}
1414
1415impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
1416 fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
1417 expand_expr(expr, self)
1418 }
1419
1420 fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
1421 expand_pat(pat, self)
1422 }
1423
1424 fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
9346a6ac 1425 expand_item(item, self)
1a4d82fc
JJ
1426 }
1427
1428 fn fold_item_underscore(&mut self, item: ast::Item_) -> ast::Item_ {
1429 expand_item_underscore(item, self)
1430 }
223e47cc 1431
1a4d82fc 1432 fn fold_stmt(&mut self, stmt: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>> {
9346a6ac 1433 expand_stmt(stmt, self)
1a4d82fc
JJ
1434 }
1435
1436 fn fold_block(&mut self, block: P<Block>) -> P<Block> {
1437 expand_block(block, self)
1438 }
1439
1440 fn fold_arm(&mut self, arm: ast::Arm) -> ast::Arm {
1441 expand_arm(arm, self)
1442 }
1443
c34b1796
AL
1444 fn fold_trait_item(&mut self, i: P<ast::TraitItem>) -> SmallVector<P<ast::TraitItem>> {
1445 expand_annotatable(Annotatable::TraitItem(i), self)
1446 .into_iter().map(|i| i.expect_trait_item()).collect()
85aaf69f
SL
1447 }
1448
c34b1796
AL
1449 fn fold_impl_item(&mut self, i: P<ast::ImplItem>) -> SmallVector<P<ast::ImplItem>> {
1450 expand_annotatable(Annotatable::ImplItem(i), self)
1451 .into_iter().map(|i| i.expect_impl_item()).collect()
1a4d82fc
JJ
1452 }
1453
1a4d82fc
JJ
1454 fn new_span(&mut self, span: Span) -> Span {
1455 new_span(self.cx, span)
1456 }
970d7e83
LB
1457}
1458
1a4d82fc
JJ
1459fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
1460 /* this discards information in the case of macro-defining macros */
1461 Span {
1462 lo: sp.lo,
1463 hi: sp.hi,
1464 expn_id: cx.backtrace(),
1465 }
223e47cc
LB
1466}
1467
85aaf69f 1468pub struct ExpansionConfig<'feat> {
1a4d82fc 1469 pub crate_name: String,
85aaf69f
SL
1470 pub features: Option<&'feat Features>,
1471 pub recursion_limit: usize,
d9579d0f 1472 pub trace_mac: bool,
1a4d82fc
JJ
1473}
1474
c34b1796
AL
1475macro_rules! feature_tests {
1476 ($( fn $getter:ident = $field:ident, )*) => {
1477 $(
1478 pub fn $getter(&self) -> bool {
1479 match self.features {
1480 Some(&Features { $field: true, .. }) => true,
1481 _ => false,
1482 }
1483 }
1484 )*
1485 }
1486}
1487
85aaf69f
SL
1488impl<'feat> ExpansionConfig<'feat> {
1489 pub fn default(crate_name: String) -> ExpansionConfig<'static> {
1a4d82fc
JJ
1490 ExpansionConfig {
1491 crate_name: crate_name,
85aaf69f 1492 features: None,
1a4d82fc 1493 recursion_limit: 64,
d9579d0f 1494 trace_mac: false,
1a4d82fc 1495 }
970d7e83 1496 }
85aaf69f 1497
c34b1796
AL
1498 feature_tests! {
1499 fn enable_quotes = allow_quote,
1500 fn enable_asm = allow_asm,
1501 fn enable_log_syntax = allow_log_syntax,
1502 fn enable_concat_idents = allow_concat_idents,
1503 fn enable_trace_macros = allow_trace_macros,
1504 fn enable_allow_internal_unstable = allow_internal_unstable,
1505 fn enable_custom_derive = allow_custom_derive,
85aaf69f 1506 }
970d7e83
LB
1507}
1508
85aaf69f
SL
1509pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
1510 cfg: ExpansionConfig<'feat>,
1511 // these are the macros being imported to this crate:
1512 imported_macros: Vec<ast::MacroDef>,
1513 user_exts: Vec<NamedSyntaxExtension>,
1514 c: Crate) -> Crate {
1a4d82fc 1515 let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
85aaf69f
SL
1516 cx.use_std = std_inject::use_std(&c);
1517
1a4d82fc
JJ
1518 let mut expander = MacroExpander::new(&mut cx);
1519
85aaf69f 1520 for def in imported_macros {
1a4d82fc
JJ
1521 expander.cx.insert_macro(def);
1522 }
970d7e83 1523
85aaf69f 1524 for (name, extension) in user_exts {
1a4d82fc 1525 expander.cx.syntax_env.insert(name, extension);
970d7e83 1526 }
1a4d82fc
JJ
1527
1528 let mut ret = expander.fold_crate(c);
1529 ret.exported_macros = expander.cx.exported_macros.clone();
1530 parse_sess.span_diagnostic.handler().abort_if_errors();
1531 return ret;
1532}
1533
1534// HYGIENIC CONTEXT EXTENSION:
1535// all of these functions are for walking over
1536// ASTs and making some change to the context of every
1537// element that has one. a CtxtFn is a trait-ified
1538// version of a closure in (SyntaxContext -> SyntaxContext).
1539// the ones defined here include:
1540// Marker - add a mark to a context
1541
1542// A Marker adds the given mark to the syntax context
1543struct Marker { mark: Mrk }
1544
1545impl Folder for Marker {
1546 fn fold_ident(&mut self, id: Ident) -> Ident {
1547 ast::Ident {
1548 name: id.name,
1549 ctxt: mtwt::apply_mark(self.mark, id.ctxt)
1550 }
1551 }
1552 fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
1553 Spanned {
1554 node: match node {
1555 MacInvocTT(path, tts, ctxt) => {
1556 MacInvocTT(self.fold_path(path),
85aaf69f 1557 self.fold_tts(&tts[..]),
1a4d82fc
JJ
1558 mtwt::apply_mark(self.mark, ctxt))
1559 }
1560 },
1561 span: span,
1562 }
1563 }
1564}
1565
1566// apply a given mark to the given token trees. Used prior to expansion of a macro.
1567fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
1568 noop_fold_tts(tts, &mut Marker{mark:m})
1569}
1570
1571// apply a given mark to the given expr. Used following the expansion of a macro.
1572fn mark_expr(expr: P<ast::Expr>, m: Mrk) -> P<ast::Expr> {
1573 Marker{mark:m}.fold_expr(expr)
1574}
1575
1576// apply a given mark to the given pattern. Used following the expansion of a macro.
1577fn mark_pat(pat: P<ast::Pat>, m: Mrk) -> P<ast::Pat> {
1578 Marker{mark:m}.fold_pat(pat)
970d7e83
LB
1579}
1580
1a4d82fc 1581// apply a given mark to the given stmt. Used following the expansion of a macro.
9346a6ac
AL
1582fn mark_stmt(stmt: P<ast::Stmt>, m: Mrk) -> P<ast::Stmt> {
1583 Marker{mark:m}.fold_stmt(stmt)
1a4d82fc
JJ
1584 .expect_one("marking a stmt didn't return exactly one stmt")
1585}
1586
1587// apply a given mark to the given item. Used following the expansion of a macro.
1588fn mark_item(expr: P<ast::Item>, m: Mrk) -> P<ast::Item> {
1589 Marker{mark:m}.fold_item(expr)
1590 .expect_one("marking an item didn't return exactly one item")
1591}
1592
1593// apply a given mark to the given item. Used following the expansion of a macro.
c34b1796
AL
1594fn mark_impl_item(ii: P<ast::ImplItem>, m: Mrk) -> P<ast::ImplItem> {
1595 Marker{mark:m}.fold_impl_item(ii)
1596 .expect_one("marking an impl item didn't return exactly one impl item")
1a4d82fc
JJ
1597}
1598
1599/// Check that there are no macro invocations left in the AST:
1600pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) {
1601 visit::walk_crate(&mut MacroExterminator{sess:sess}, krate);
1602}
1603
1604/// A visitor that ensures that no macro invocations remain in an AST.
1605struct MacroExterminator<'a>{
1606 sess: &'a parse::ParseSess
1607}
1608
1609impl<'a, 'v> Visitor<'v> for MacroExterminator<'a> {
1610 fn visit_mac(&mut self, mac: &ast::Mac) {
1611 self.sess.span_diagnostic.span_bug(mac.span,
1612 "macro exterminator: expected AST \
1613 with no macro invocations");
970d7e83
LB
1614 }
1615}
1616
1617
223e47cc 1618#[cfg(test)]
d9579d0f 1619mod tests {
1a4d82fc
JJ
1620 use super::{pattern_bindings, expand_crate};
1621 use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
223e47cc 1622 use ast;
85aaf69f 1623 use ast::Name;
223e47cc 1624 use codemap;
1a4d82fc
JJ
1625 use ext::mtwt;
1626 use fold::Folder;
223e47cc 1627 use parse;
1a4d82fc 1628 use parse::token;
1a4d82fc
JJ
1629 use util::parser_testing::{string_to_parser};
1630 use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
1631 use visit;
1632 use visit::Visitor;
1633
1634 // a visitor that extracts the paths
1635 // from a given thingy and puts them in a mutable
1636 // array (passed in to the traversal)
1637 #[derive(Clone)]
1638 struct PathExprFinderContext {
1639 path_accumulator: Vec<ast::Path> ,
1640 }
1641
1642 impl<'v> Visitor<'v> for PathExprFinderContext {
1643 fn visit_expr(&mut self, expr: &ast::Expr) {
c34b1796
AL
1644 if let ast::ExprPath(None, ref p) = expr.node {
1645 self.path_accumulator.push(p.clone());
1a4d82fc 1646 }
c34b1796 1647 visit::walk_expr(self, expr);
1a4d82fc
JJ
1648 }
1649 }
1650
1651 // find the variable references in a crate
1652 fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
1653 let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
1654 visit::walk_crate(&mut path_finder, the_crate);
1655 path_finder.path_accumulator
1656 }
1657
1658 /// A Visitor that extracts the identifiers from a thingy.
1659 // as a side note, I'm starting to want to abstract over these....
1660 struct IdentFinder {
1661 ident_accumulator: Vec<ast::Ident>
1662 }
1663
1664 impl<'v> Visitor<'v> for IdentFinder {
1665 fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident){
1666 self.ident_accumulator.push(id);
1667 }
1668 }
1669
1670 /// Find the idents in a crate
1671 fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
1672 let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
1673 visit::walk_crate(&mut ident_finder, the_crate);
1674 ident_finder.ident_accumulator
223e47cc
LB
1675 }
1676
1677 // these following tests are quite fragile, in that they don't test what
1678 // *kind* of failure occurs.
1679
85aaf69f 1680 fn test_ecfg() -> ExpansionConfig<'static> {
1a4d82fc
JJ
1681 ExpansionConfig::default("test".to_string())
1682 }
1683
1684 // make sure that macros can't escape fns
c34b1796 1685 #[should_panic]
223e47cc 1686 #[test] fn macros_cant_escape_fns_test () {
1a4d82fc 1687 let src = "fn bogus() {macro_rules! z (() => (3+4));}\
85aaf69f 1688 fn inty() -> i32 { z!() }".to_string();
62682a34 1689 let sess = parse::ParseSess::new();
223e47cc 1690 let crate_ast = parse::parse_crate_from_source_str(
1a4d82fc 1691 "<test>".to_string(),
970d7e83 1692 src,
1a4d82fc 1693 Vec::new(), &sess);
223e47cc 1694 // should fail:
1a4d82fc 1695 expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
223e47cc
LB
1696 }
1697
1a4d82fc 1698 // make sure that macros can't escape modules
c34b1796 1699 #[should_panic]
223e47cc 1700 #[test] fn macros_cant_escape_mods_test () {
1a4d82fc 1701 let src = "mod foo {macro_rules! z (() => (3+4));}\
85aaf69f 1702 fn inty() -> i32 { z!() }".to_string();
62682a34 1703 let sess = parse::ParseSess::new();
223e47cc 1704 let crate_ast = parse::parse_crate_from_source_str(
1a4d82fc 1705 "<test>".to_string(),
970d7e83 1706 src,
1a4d82fc
JJ
1707 Vec::new(), &sess);
1708 expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
223e47cc
LB
1709 }
1710
1a4d82fc 1711 // macro_use modules should allow macros to escape
223e47cc 1712 #[test] fn macros_can_escape_flattened_mods_test () {
1a4d82fc 1713 let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
85aaf69f 1714 fn inty() -> i32 { z!() }".to_string();
62682a34 1715 let sess = parse::ParseSess::new();
223e47cc 1716 let crate_ast = parse::parse_crate_from_source_str(
1a4d82fc 1717 "<test>".to_string(),
970d7e83 1718 src,
1a4d82fc
JJ
1719 Vec::new(), &sess);
1720 expand_crate(&sess, test_ecfg(), vec!(), vec!(), crate_ast);
223e47cc
LB
1721 }
1722
1a4d82fc 1723 fn expand_crate_str(crate_str: String) -> ast::Crate {
62682a34 1724 let ps = parse::ParseSess::new();
9346a6ac 1725 let crate_ast = panictry!(string_to_parser(&ps, crate_str).parse_crate_mod());
1a4d82fc
JJ
1726 // the cfg argument actually does matter, here...
1727 expand_crate(&ps,test_ecfg(),vec!(),vec!(),crate_ast)
1728 }
223e47cc 1729
1a4d82fc
JJ
1730 // find the pat_ident paths in a crate
1731 fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
1732 let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
1733 visit::walk_crate(&mut name_finder, the_crate);
1734 name_finder.ident_accumulator
1735 }
1736
1737 #[test] fn macro_tokens_should_match(){
1738 expand_crate_str(
1739 "macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
1740 }
1741
1742 // should be able to use a bound identifier as a literal in a macro definition:
1743 #[test] fn self_macro_parsing(){
1744 expand_crate_str(
85aaf69f
SL
1745 "macro_rules! foo ((zz) => (287;));
1746 fn f(zz: i32) {foo!(zz);}".to_string()
1a4d82fc
JJ
1747 );
1748 }
1749
1750 // renaming tests expand a crate and then check that the bindings match
1751 // the right varrefs. The specification of the test case includes the
1752 // text of the crate, and also an array of arrays. Each element in the
1753 // outer array corresponds to a binding in the traversal of the AST
1754 // induced by visit. Each of these arrays contains a list of indexes,
1755 // interpreted as the varrefs in the varref traversal that this binding
1756 // should match. So, for instance, in a program with two bindings and
d9579d0f 1757 // three varrefs, the array [[1, 2], [0]] would indicate that the first
1a4d82fc
JJ
1758 // binding should match the second two varrefs, and the second binding
1759 // should match the first varref.
1760 //
1761 // Put differently; this is a sparse representation of a boolean matrix
1762 // indicating which bindings capture which identifiers.
1763 //
1764 // Note also that this matrix is dependent on the implicit ordering of
1765 // the bindings and the varrefs discovered by the name-finder and the path-finder.
1766 //
1767 // The comparisons are done post-mtwt-resolve, so we're comparing renamed
1768 // names; differences in marks don't matter any more.
1769 //
1770 // oog... I also want tests that check "bound-identifier-=?". That is,
1771 // not just "do these have the same name", but "do they have the same
1772 // name *and* the same marks"? Understanding this is really pretty painful.
1773 // in principle, you might want to control this boolean on a per-varref basis,
1774 // but that would make things even harder to understand, and might not be
1775 // necessary for thorough testing.
85aaf69f 1776 type RenamingTest = (&'static str, Vec<Vec<usize>>, bool);
1a4d82fc
JJ
1777
1778 #[test]
1779 fn automatic_renaming () {
1780 let tests: Vec<RenamingTest> =
1781 vec!(// b & c should get new names throughout, in the expr too:
85aaf69f 1782 ("fn a() -> i32 { let b = 13; let c = b; b+c }",
1a4d82fc
JJ
1783 vec!(vec!(0,1),vec!(2)), false),
1784 // both x's should be renamed (how is this causing a bug?)
85aaf69f 1785 ("fn main () {let x: i32 = 13;x;}",
1a4d82fc
JJ
1786 vec!(vec!(0)), false),
1787 // the use of b after the + should be renamed, the other one not:
85aaf69f 1788 ("macro_rules! f (($x:ident) => (b + $x)); fn a() -> i32 { let b = 13; f!(b)}",
1a4d82fc
JJ
1789 vec!(vec!(1)), false),
1790 // the b before the plus should not be renamed (requires marks)
85aaf69f 1791 ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> i32 { f!(b)}",
1a4d82fc
JJ
1792 vec!(vec!(1)), false),
1793 // the marks going in and out of letty should cancel, allowing that $x to
1794 // capture the one following the semicolon.
1795 // this was an awesome test case, and caught a *lot* of bugs.
1796 ("macro_rules! letty(($x:ident) => (let $x = 15;));
1797 macro_rules! user(($x:ident) => ({letty!($x); $x}));
85aaf69f 1798 fn main() -> i32 {user!(z)}",
1a4d82fc
JJ
1799 vec!(vec!(0)), false)
1800 );
1801 for (idx,s) in tests.iter().enumerate() {
1802 run_renaming_test(s,idx);
1803 }
1804 }
1805
1806 // no longer a fixme #8062: this test exposes a *potential* bug; our system does
1807 // not behave exactly like MTWT, but a conversation with Matthew Flatt
1808 // suggests that this can only occur in the presence of local-expand, which
1809 // we have no plans to support. ... unless it's needed for item hygiene....
1810 #[ignore]
9346a6ac
AL
1811 #[test]
1812 fn issue_8062(){
1a4d82fc
JJ
1813 run_renaming_test(
1814 &("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
1815 vec!(vec!(0)), true), 0)
1816 }
1817
1818 // FIXME #6994:
1819 // the z flows into and out of two macros (g & f) along one path, and one
1820 // (just g) along the other, so the result of the whole thing should
1821 // be "let z_123 = 3; z_123"
1822 #[ignore]
9346a6ac
AL
1823 #[test]
1824 fn issue_6994(){
1a4d82fc
JJ
1825 run_renaming_test(
1826 &("macro_rules! g (($x:ident) =>
1827 ({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}));
1828 fn a(){g!(z)}",
1829 vec!(vec!(0)),false),
1830 0)
1831 }
1832
1833 // match variable hygiene. Should expand into
1834 // fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
9346a6ac
AL
1835 #[test]
1836 fn issue_9384(){
1a4d82fc
JJ
1837 run_renaming_test(
1838 &("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}));
1839 fn z() {match 8 {x => bad_macro!(x)}}",
1840 // NB: the third "binding" is the repeat of the second one.
1841 vec!(vec!(1,3),vec!(0,2),vec!(0,2)),
1842 true),
1843 0)
1844 }
1845
1846 // interpolated nodes weren't getting labeled.
1847 // should expand into
1848 // fn main(){let g1_1 = 13; g1_1}}
9346a6ac
AL
1849 #[test]
1850 fn pat_expand_issue_15221(){
1a4d82fc
JJ
1851 run_renaming_test(
1852 &("macro_rules! inner ( ($e:pat ) => ($e));
1853 macro_rules! outer ( ($e:pat ) => (inner!($e)));
1854 fn main() { let outer!(g) = 13; g;}",
1855 vec!(vec!(0)),
1856 true),
1857 0)
1858 }
1859
1860 // create a really evil test case where a $x appears inside a binding of $x
1861 // but *shouldn't* bind because it was inserted by a different macro....
1862 // can't write this test case until we have macro-generating macros.
1863
1864 // method arg hygiene
85aaf69f 1865 // method expands to fn get_x(&self_0, x_1: i32) {self_0 + self_2 + x_3 + x_1}
9346a6ac
AL
1866 #[test]
1867 fn method_arg_hygiene(){
1a4d82fc
JJ
1868 run_renaming_test(
1869 &("macro_rules! inject_x (()=>(x));
1870 macro_rules! inject_self (()=>(self));
1871 struct A;
85aaf69f 1872 impl A{fn get_x(&self, x: i32) {self + inject_self!() + inject_x!() + x;} }",
1a4d82fc
JJ
1873 vec!(vec!(0),vec!(3)),
1874 true),
1875 0)
1876 }
1877
1878 // ooh, got another bite?
1879 // expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
9346a6ac
AL
1880 #[test]
1881 fn method_arg_hygiene_2(){
1a4d82fc
JJ
1882 run_renaming_test(
1883 &("struct A;
1884 macro_rules! add_method (($T:ty) =>
1885 (impl $T { fn thingy(&self) {self;} }));
1886 add_method!(A);",
1887 vec!(vec!(0)),
1888 true),
1889 0)
1890 }
1891
1892 // item fn hygiene
85aaf69f 1893 // expands to fn q(x_1: i32){fn g(x_2: i32){x_2 + x_1};}
9346a6ac
AL
1894 #[test]
1895 fn issue_9383(){
1a4d82fc 1896 run_renaming_test(
85aaf69f
SL
1897 &("macro_rules! bad_macro (($ex:expr) => (fn g(x: i32){ x + $ex }));
1898 fn q(x: i32) { bad_macro!(x); }",
1a4d82fc
JJ
1899 vec!(vec!(1),vec!(0)),true),
1900 0)
1901 }
1902
1903 // closure arg hygiene (ExprClosure)
85aaf69f 1904 // expands to fn f(){(|x_1 : i32| {(x_2 + x_1)})(3);}
9346a6ac
AL
1905 #[test]
1906 fn closure_arg_hygiene(){
1a4d82fc
JJ
1907 run_renaming_test(
1908 &("macro_rules! inject_x (()=>(x));
85aaf69f 1909 fn f(){(|x : i32| {(inject_x!() + x)})(3);}",
1a4d82fc
JJ
1910 vec!(vec!(1)),
1911 true),
1912 0)
1913 }
1914
1915 // macro_rules in method position. Sadly, unimplemented.
9346a6ac
AL
1916 #[test]
1917 fn macro_in_method_posn(){
1a4d82fc 1918 expand_crate_str(
85aaf69f 1919 "macro_rules! my_method (() => (fn thirteen(&self) -> i32 {13}));
1a4d82fc
JJ
1920 struct A;
1921 impl A{ my_method!(); }
1922 fn f(){A.thirteen;}".to_string());
1923 }
1924
1925 // another nested macro
1926 // expands to impl Entries {fn size_hint(&self_1) {self_1;}
9346a6ac
AL
1927 #[test]
1928 fn item_macro_workaround(){
1a4d82fc
JJ
1929 run_renaming_test(
1930 &("macro_rules! item { ($i:item) => {$i}}
1931 struct Entries;
1932 macro_rules! iterator_impl {
1933 () => { item!( impl Entries { fn size_hint(&self) { self;}});}}
1934 iterator_impl! { }",
1935 vec!(vec!(0)), true),
1936 0)
1937 }
1938
1939 // run one of the renaming tests
85aaf69f 1940 fn run_renaming_test(t: &RenamingTest, test_idx: usize) {
1a4d82fc
JJ
1941 let invalid_name = token::special_idents::invalid.name;
1942 let (teststr, bound_connections, bound_ident_check) = match *t {
1943 (ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic)
1944 };
1945 let cr = expand_crate_str(teststr.to_string());
1946 let bindings = crate_bindings(&cr);
1947 let varrefs = crate_varrefs(&cr);
1948
1949 // must be one check clause for each binding:
1950 assert_eq!(bindings.len(),bound_connections.len());
1951 for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
1952 let binding_name = mtwt::resolve(bindings[binding_idx]);
1953 let binding_marks = mtwt::marksof(bindings[binding_idx].ctxt, invalid_name);
1954 // shouldmatch can't name varrefs that don't exist:
9346a6ac 1955 assert!((shouldmatch.is_empty()) ||
1a4d82fc
JJ
1956 (varrefs.len() > *shouldmatch.iter().max().unwrap()));
1957 for (idx,varref) in varrefs.iter().enumerate() {
85aaf69f 1958 let print_hygiene_debug_info = || {
1a4d82fc
JJ
1959 // good lord, you can't make a path with 0 segments, can you?
1960 let final_varref_ident = match varref.segments.last() {
1961 Some(pathsegment) => pathsegment.identifier,
1962 None => panic!("varref with 0 path segments?")
1963 };
1964 let varref_name = mtwt::resolve(final_varref_ident);
1965 let varref_idents : Vec<ast::Ident>
1966 = varref.segments.iter().map(|s| s.identifier)
1967 .collect();
1968 println!("varref #{}: {:?}, resolves to {}",idx, varref_idents, varref_name);
1969 let string = token::get_ident(final_varref_ident);
85aaf69f 1970 println!("varref's first segment's string: \"{}\"", &string[..]);
1a4d82fc
JJ
1971 println!("binding #{}: {}, resolves to {}",
1972 binding_idx, bindings[binding_idx], binding_name);
1973 mtwt::with_sctable(|x| mtwt::display_sctable(x));
1974 };
1975 if shouldmatch.contains(&idx) {
1976 // it should be a path of length 1, and it should
1977 // be free-identifier=? or bound-identifier=? to the given binding
1978 assert_eq!(varref.segments.len(),1);
1979 let varref_name = mtwt::resolve(varref.segments[0].identifier);
1980 let varref_marks = mtwt::marksof(varref.segments[0]
1981 .identifier
1982 .ctxt,
1983 invalid_name);
1984 if !(varref_name==binding_name) {
1985 println!("uh oh, should match but doesn't:");
1986 print_hygiene_debug_info();
1987 }
1988 assert_eq!(varref_name,binding_name);
1989 if bound_ident_check {
1990 // we're checking bound-identifier=?, and the marks
1991 // should be the same, too:
1992 assert_eq!(varref_marks,binding_marks.clone());
1993 }
1994 } else {
1995 let varref_name = mtwt::resolve(varref.segments[0].identifier);
1996 let fail = (varref.segments.len() == 1)
1997 && (varref_name == binding_name);
1998 // temp debugging:
1999 if fail {
2000 println!("failure on test {}",test_idx);
2001 println!("text of test case: \"{}\"", teststr);
2002 println!("");
2003 println!("uh oh, matches but shouldn't:");
2004 print_hygiene_debug_info();
2005 }
2006 assert!(!fail);
2007 }
223e47cc
LB
2008 }
2009 }
2010 }
2011
9346a6ac
AL
2012 #[test]
2013 fn fmt_in_macro_used_inside_module_macro() {
1a4d82fc
JJ
2014 let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
2015macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
2016foo_module!();
2017".to_string();
2018 let cr = expand_crate_str(crate_str);
2019 // find the xx binding
2020 let bindings = crate_bindings(&cr);
2021 let cxbinds: Vec<&ast::Ident> =
2022 bindings.iter().filter(|b| {
2023 let ident = token::get_ident(**b);
85aaf69f 2024 let string = &ident[..];
1a4d82fc
JJ
2025 "xx" == string
2026 }).collect();
85aaf69f 2027 let cxbinds: &[&ast::Ident] = &cxbinds[..];
d9579d0f
AL
2028 let cxbind = match (cxbinds.len(), cxbinds.get(0)) {
2029 (1, Some(b)) => *b,
1a4d82fc
JJ
2030 _ => panic!("expected just one binding for ext_cx")
2031 };
2032 let resolved_binding = mtwt::resolve(*cxbind);
2033 let varrefs = crate_varrefs(&cr);
2034
2035 // the xx binding should bind all of the xx varrefs:
2036 for (idx,v) in varrefs.iter().filter(|p| {
2037 p.segments.len() == 1
c34b1796 2038 && "xx" == &*token::get_ident(p.segments[0].identifier)
1a4d82fc
JJ
2039 }).enumerate() {
2040 if mtwt::resolve(v.segments[0].identifier) != resolved_binding {
2041 println!("uh oh, xx binding didn't match xx varref:");
2042 println!("this is xx varref \\# {}", idx);
2043 println!("binding: {}", cxbind);
2044 println!("resolves to: {}", resolved_binding);
2045 println!("varref: {}", v.segments[0].identifier);
2046 println!("resolves to: {}",
2047 mtwt::resolve(v.segments[0].identifier));
2048 mtwt::with_sctable(|x| mtwt::display_sctable(x));
2049 }
2050 assert_eq!(mtwt::resolve(v.segments[0].identifier),
2051 resolved_binding);
970d7e83 2052 };
1a4d82fc 2053 }
223e47cc 2054
1a4d82fc
JJ
2055 #[test]
2056 fn pat_idents(){
2057 let pat = string_to_pat(
2058 "(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
2059 let idents = pattern_bindings(&*pat);
2060 assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
2061 }
970d7e83 2062
1a4d82fc
JJ
2063 // test the list of identifier patterns gathered by the visitor. Note that
2064 // 'None' is listed as an identifier pattern because we don't yet know that
2065 // it's the name of a 0-ary variant, and that 'i' appears twice in succession.
2066 #[test]
2067 fn crate_bindings_test(){
85aaf69f 2068 let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| {
1a4d82fc
JJ
2069 match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
2070 let idents = crate_bindings(&the_crate);
2071 assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
970d7e83
LB
2072 }
2073
1a4d82fc
JJ
2074 // test the IdentRenamer directly
2075 #[test]
2076 fn ident_renamer_test () {
85aaf69f 2077 let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1a4d82fc
JJ
2078 let f_ident = token::str_to_ident("f");
2079 let x_ident = token::str_to_ident("x");
85aaf69f 2080 let int_ident = token::str_to_ident("i32");
1a4d82fc
JJ
2081 let renames = vec!((x_ident,Name(16)));
2082 let mut renamer = IdentRenamer{renames: &renames};
2083 let renamed_crate = renamer.fold_crate(the_crate);
2084 let idents = crate_idents(&renamed_crate);
2085 let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
c34b1796 2086 assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)]);
1a4d82fc 2087 }
970d7e83 2088
1a4d82fc 2089 // test the PatIdentRenamer; only PatIdents get renamed
970d7e83 2090 #[test]
1a4d82fc 2091 fn pat_ident_renamer_test () {
85aaf69f 2092 let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
1a4d82fc
JJ
2093 let f_ident = token::str_to_ident("f");
2094 let x_ident = token::str_to_ident("x");
85aaf69f 2095 let int_ident = token::str_to_ident("i32");
1a4d82fc
JJ
2096 let renames = vec!((x_ident,Name(16)));
2097 let mut renamer = PatIdentRenamer{renames: &renames};
2098 let renamed_crate = renamer.fold_crate(the_crate);
2099 let idents = crate_idents(&renamed_crate);
2100 let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
2101 let x_name = x_ident.name;
c34b1796 2102 assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name]);
970d7e83
LB
2103 }
2104}