2014-02-12 06:43:23 +00:00
|
|
|
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
2012-12-04 00:48:01 +00:00
|
|
|
// file at the top-level directory of this distribution and at
|
|
|
|
// http://rust-lang.org/COPYRIGHT.
|
|
|
|
//
|
|
|
|
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
use ast::{Block, Crate, DeclLocal, ExprMac, PatMac};
|
2014-01-09 13:05:33 +00:00
|
|
|
use ast::{Local, Ident, MacInvocTT};
|
2014-11-14 17:18:10 +00:00
|
|
|
use ast::{ItemMac, MacStmtWithSemicolon, Mrk, Stmt, StmtDecl, StmtMac};
|
|
|
|
use ast::{StmtExpr, StmtSemi};
|
2014-02-24 20:47:19 +00:00
|
|
|
use ast::TokenTree;
|
2012-12-23 22:41:37 +00:00
|
|
|
use ast;
|
2014-12-14 02:42:41 +00:00
|
|
|
use ast_util::path_to_ident;
|
2014-02-24 20:47:19 +00:00
|
|
|
use ext::mtwt;
|
2013-09-08 12:46:20 +00:00
|
|
|
use ext::build::AstBuilder;
|
2013-02-11 16:36:42 +00:00
|
|
|
use attr;
|
2013-07-19 11:51:37 +00:00
|
|
|
use attr::AttrMetaMethods;
|
2013-02-25 19:11:21 +00:00
|
|
|
use codemap;
|
2013-12-07 02:41:11 +00:00
|
|
|
use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
|
2012-09-04 18:37:29 +00:00
|
|
|
use ext::base::*;
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
use feature_gate::{self, Features};
|
2014-07-03 06:16:01 +00:00
|
|
|
use fold;
|
2012-12-23 22:41:37 +00:00
|
|
|
use fold::*;
|
2013-02-25 19:11:21 +00:00
|
|
|
use parse;
|
2014-01-31 23:26:59 +00:00
|
|
|
use parse::token::{fresh_mark, fresh_name, intern};
|
2013-07-30 00:25:00 +00:00
|
|
|
use parse::token;
|
2014-09-13 16:06:01 +00:00
|
|
|
use ptr::P;
|
|
|
|
use util::small_vector::SmallVector;
|
2013-06-04 21:56:33 +00:00
|
|
|
use visit;
|
2013-06-08 16:21:11 +00:00
|
|
|
use visit::Visitor;
|
2014-09-07 21:57:26 +00:00
|
|
|
use std_inject;
|
2011-08-05 20:06:11 +00:00
|
|
|
|
2014-12-14 02:42:41 +00:00
|
|
|
pub fn expand_type(t: P<ast::Ty>,
|
|
|
|
fld: &mut MacroExpander,
|
|
|
|
impl_ty: Option<P<ast::Ty>>)
|
|
|
|
-> P<ast::Ty> {
|
2014-12-20 08:09:35 +00:00
|
|
|
debug!("expanding type {:?} with impl_ty {:?}", t, impl_ty);
|
2014-12-14 02:42:41 +00:00
|
|
|
let t = match (t.node.clone(), impl_ty) {
|
|
|
|
// Expand uses of `Self` in impls to the concrete type.
|
2015-02-17 17:29:13 +00:00
|
|
|
(ast::Ty_::TyPath(None, ref path), Some(ref impl_ty)) => {
|
2014-12-14 02:42:41 +00:00
|
|
|
let path_as_ident = path_to_ident(path);
|
|
|
|
// Note unhygenic comparison here. I think this is correct, since
|
|
|
|
// even though `Self` is almost just a type parameter, the treatment
|
|
|
|
// for this expansion is as if it were a keyword.
|
|
|
|
if path_as_ident.is_some() &&
|
|
|
|
path_as_ident.unwrap().name == token::special_idents::type_self.name {
|
|
|
|
impl_ty.clone()
|
|
|
|
} else {
|
|
|
|
t
|
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => t
|
|
|
|
};
|
|
|
|
fold::noop_fold_ty(t, fld)
|
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
pub fn expand_expr(e: P<ast::Expr>, fld: &mut MacroExpander) -> P<ast::Expr> {
|
|
|
|
e.and_then(|ast::Expr {id, node, span}| match node {
|
2013-02-18 06:20:36 +00:00
|
|
|
// expr_mac should really be expr_ext or something; it's the
|
|
|
|
// entry-point for all syntax extensions.
|
2014-08-25 02:08:48 +00:00
|
|
|
ast::ExprMac(mac) => {
|
2014-09-13 16:06:01 +00:00
|
|
|
let expanded_expr = match expand_mac_invoc(mac, span,
|
|
|
|
|r| r.make_expr(),
|
|
|
|
mark_expr, fld) {
|
2014-07-12 22:00:23 +00:00
|
|
|
Some(expr) => expr,
|
|
|
|
None => {
|
2014-09-13 16:06:01 +00:00
|
|
|
return DummyResult::raw_expr(span);
|
2014-07-12 22:00:23 +00:00
|
|
|
}
|
|
|
|
};
|
2014-01-06 02:02:57 +00:00
|
|
|
|
2014-07-12 22:00:23 +00:00
|
|
|
// Keep going, outside-in.
|
|
|
|
//
|
2014-09-13 16:06:01 +00:00
|
|
|
let fully_expanded = fld.fold_expr(expanded_expr);
|
2014-07-12 22:00:23 +00:00
|
|
|
fld.cx.bt_pop();
|
2014-01-06 02:02:57 +00:00
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fully_expanded.map(|e| ast::Expr {
|
2014-07-12 22:00:23 +00:00
|
|
|
id: ast::DUMMY_NODE_ID,
|
2014-09-13 16:06:01 +00:00
|
|
|
node: e.node,
|
|
|
|
span: span,
|
|
|
|
})
|
2013-02-18 06:20:36 +00:00
|
|
|
}
|
2013-07-30 00:25:00 +00:00
|
|
|
|
2014-07-26 00:12:51 +00:00
|
|
|
ast::ExprWhile(cond, body, opt_ident) => {
|
|
|
|
let cond = fld.fold_expr(cond);
|
|
|
|
let (body, opt_ident) = expand_loop_block(body, opt_ident, fld);
|
2014-09-13 16:06:01 +00:00
|
|
|
fld.cx.expr(span, ast::ExprWhile(cond, body, opt_ident))
|
2014-07-26 00:12:51 +00:00
|
|
|
}
|
|
|
|
|
2014-10-03 02:45:46 +00:00
|
|
|
// Desugar ExprWhileLet
|
|
|
|
// From: `[opt_ident]: while let <pat> = <expr> <body>`
|
|
|
|
ast::ExprWhileLet(pat, expr, body, opt_ident) => {
|
|
|
|
// to:
|
|
|
|
//
|
|
|
|
// [opt_ident]: loop {
|
|
|
|
// match <expr> {
|
|
|
|
// <pat> => <body>,
|
|
|
|
// _ => break
|
|
|
|
// }
|
|
|
|
// }
|
|
|
|
|
|
|
|
// `<pat> => <body>`
|
|
|
|
let pat_arm = {
|
|
|
|
let body_expr = fld.cx.expr_block(body);
|
|
|
|
fld.cx.arm(pat.span, vec![pat], body_expr)
|
|
|
|
};
|
|
|
|
|
|
|
|
// `_ => break`
|
|
|
|
let break_arm = {
|
|
|
|
let pat_under = fld.cx.pat_wild(span);
|
|
|
|
let break_expr = fld.cx.expr_break(span);
|
|
|
|
fld.cx.arm(span, vec![pat_under], break_expr)
|
|
|
|
};
|
|
|
|
|
|
|
|
// `match <expr> { ... }`
|
|
|
|
let arms = vec![pat_arm, break_arm];
|
|
|
|
let match_expr = fld.cx.expr(span,
|
2014-12-19 22:58:02 +00:00
|
|
|
ast::ExprMatch(expr, arms, ast::MatchSource::WhileLetDesugar));
|
2014-10-03 02:45:46 +00:00
|
|
|
|
|
|
|
// `[opt_ident]: loop { ... }`
|
|
|
|
let loop_block = fld.cx.block_expr(match_expr);
|
|
|
|
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
|
|
|
|
fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
|
|
|
|
}
|
|
|
|
|
2014-08-25 02:08:48 +00:00
|
|
|
// Desugar ExprIfLet
|
|
|
|
// From: `if let <pat> = <expr> <body> [<elseopt>]`
|
|
|
|
ast::ExprIfLet(pat, expr, body, mut elseopt) => {
|
|
|
|
// to:
|
|
|
|
//
|
|
|
|
// match <expr> {
|
|
|
|
// <pat> => <body>,
|
|
|
|
// [_ if <elseopt_if_cond> => <elseopt_if_body>,]
|
|
|
|
// _ => [<elseopt> | ()]
|
|
|
|
// }
|
|
|
|
|
|
|
|
// `<pat> => <body>`
|
|
|
|
let pat_arm = {
|
|
|
|
let body_expr = fld.cx.expr_block(body);
|
|
|
|
fld.cx.arm(pat.span, vec![pat], body_expr)
|
|
|
|
};
|
|
|
|
|
|
|
|
// `[_ if <elseopt_if_cond> => <elseopt_if_body>,]`
|
|
|
|
let else_if_arms = {
|
|
|
|
let mut arms = vec![];
|
|
|
|
loop {
|
2014-09-29 20:45:26 +00:00
|
|
|
let elseopt_continue = elseopt
|
|
|
|
.and_then(|els| els.and_then(|els| match els.node {
|
|
|
|
// else if
|
|
|
|
ast::ExprIf(cond, then, elseopt) => {
|
|
|
|
let pat_under = fld.cx.pat_wild(span);
|
|
|
|
arms.push(ast::Arm {
|
|
|
|
attrs: vec![],
|
|
|
|
pats: vec![pat_under],
|
|
|
|
guard: Some(cond),
|
|
|
|
body: fld.cx.expr_block(then)
|
|
|
|
});
|
|
|
|
elseopt.map(|elseopt| (elseopt, true))
|
|
|
|
}
|
|
|
|
_ => Some((P(els), false))
|
|
|
|
}));
|
|
|
|
match elseopt_continue {
|
|
|
|
Some((e, true)) => {
|
|
|
|
elseopt = Some(e);
|
|
|
|
}
|
|
|
|
Some((e, false)) => {
|
|
|
|
elseopt = Some(e);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
None => {
|
|
|
|
elseopt = None;
|
|
|
|
break;
|
|
|
|
}
|
2014-08-25 02:08:48 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
arms
|
|
|
|
};
|
|
|
|
|
2014-12-19 22:58:02 +00:00
|
|
|
let contains_else_clause = elseopt.is_some();
|
|
|
|
|
2014-08-25 02:08:48 +00:00
|
|
|
// `_ => [<elseopt> | ()]`
|
|
|
|
let else_arm = {
|
|
|
|
let pat_under = fld.cx.pat_wild(span);
|
2014-11-09 15:14:15 +00:00
|
|
|
let else_expr = elseopt.unwrap_or_else(|| fld.cx.expr_tuple(span, vec![]));
|
2014-08-25 02:08:48 +00:00
|
|
|
fld.cx.arm(span, vec![pat_under], else_expr)
|
|
|
|
};
|
|
|
|
|
|
|
|
let mut arms = Vec::with_capacity(else_if_arms.len() + 2);
|
|
|
|
arms.push(pat_arm);
|
2014-10-15 06:05:01 +00:00
|
|
|
arms.extend(else_if_arms.into_iter());
|
2014-08-25 02:08:48 +00:00
|
|
|
arms.push(else_arm);
|
|
|
|
|
2014-12-19 22:58:02 +00:00
|
|
|
let match_expr = fld.cx.expr(span,
|
|
|
|
ast::ExprMatch(expr, arms,
|
|
|
|
ast::MatchSource::IfLetDesugar {
|
|
|
|
contains_else_clause: contains_else_clause,
|
|
|
|
}));
|
2014-08-25 02:08:48 +00:00
|
|
|
fld.fold_expr(match_expr)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Desugar support for ExprIfLet in the ExprIf else position
|
2014-08-28 04:34:03 +00:00
|
|
|
ast::ExprIf(cond, blk, elseopt) => {
|
2014-09-29 20:45:26 +00:00
|
|
|
let elseopt = elseopt.map(|els| els.and_then(|els| match els.node {
|
2014-08-28 04:34:03 +00:00
|
|
|
ast::ExprIfLet(..) => {
|
|
|
|
// wrap the if-let expr in a block
|
2014-09-29 20:45:26 +00:00
|
|
|
let span = els.span;
|
2014-08-28 04:34:03 +00:00
|
|
|
let blk = P(ast::Block {
|
|
|
|
stmts: vec![],
|
2014-09-29 20:45:26 +00:00
|
|
|
expr: Some(P(els)),
|
2014-08-28 04:34:03 +00:00
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
rules: ast::DefaultBlock,
|
2014-09-29 20:45:26 +00:00
|
|
|
span: span
|
2014-08-28 04:34:03 +00:00
|
|
|
});
|
|
|
|
fld.cx.expr_block(blk)
|
|
|
|
}
|
2014-09-29 20:45:26 +00:00
|
|
|
_ => P(els)
|
|
|
|
}));
|
|
|
|
let if_expr = fld.cx.expr(span, ast::ExprIf(cond, blk, elseopt));
|
|
|
|
if_expr.map(|e| noop_fold_expr(e, fld))
|
2014-08-25 02:08:48 +00:00
|
|
|
}
|
|
|
|
|
2014-02-15 08:54:32 +00:00
|
|
|
ast::ExprLoop(loop_block, opt_ident) => {
|
2014-02-24 20:47:19 +00:00
|
|
|
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
|
2014-09-13 16:06:01 +00:00
|
|
|
fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident))
|
2014-02-15 08:54:32 +00:00
|
|
|
}
|
|
|
|
|
2015-01-08 23:04:26 +00:00
|
|
|
// Desugar ExprForLoop
|
|
|
|
// From: `[opt_ident]: for <pat> in <head> <body>`
|
2014-07-22 03:54:28 +00:00
|
|
|
ast::ExprForLoop(pat, head, body, opt_ident) => {
|
2015-01-08 23:04:26 +00:00
|
|
|
// to:
|
|
|
|
//
|
make `for PAT in ITER_EXPR { ... }` a terminating-scope for ITER_EXPR.
In effect, temporary anonymous values created during the evaluation of
ITER_EXPR no longer not live for the entirety of the block surrounding
the for-loop; instead they only live for the extent of the for-loop
itself, and no longer.
----
There is one case I know of that this breaks, demonstrated to me by
niko (but it is also a corner-case that is useless in practice). Here
is that case:
```
fn main() {
let mut foo: Vec<&i8> = Vec::new();
for i in &[1, 2, 3] { foo.push(i) }
}
```
Note that if you add any code following the for-loop above, or even a
semicolon to the end of it, then the code will stop compiling (i.e.,
it gathers a vector of references but the gathered vector cannot
actually be used.)
(The above code, despite being useless, did occur in one run-pass test
by accident; that test is updated here to accommodate the new
striction.)
----
So, technically this is a:
[breaking-change]
2015-02-04 12:24:44 +00:00
|
|
|
// {
|
|
|
|
// let result = match ::std::iter::IntoIterator::into_iter(<head>) {
|
|
|
|
// mut iter => {
|
|
|
|
// [opt_ident]: loop {
|
|
|
|
// match ::std::iter::Iterator::next(&mut iter) {
|
|
|
|
// ::std::option::Option::Some(<pat>) => <body>,
|
|
|
|
// ::std::option::Option::None => break
|
|
|
|
// }
|
2015-01-08 23:04:26 +00:00
|
|
|
// }
|
|
|
|
// }
|
make `for PAT in ITER_EXPR { ... }` a terminating-scope for ITER_EXPR.
In effect, temporary anonymous values created during the evaluation of
ITER_EXPR no longer not live for the entirety of the block surrounding
the for-loop; instead they only live for the extent of the for-loop
itself, and no longer.
----
There is one case I know of that this breaks, demonstrated to me by
niko (but it is also a corner-case that is useless in practice). Here
is that case:
```
fn main() {
let mut foo: Vec<&i8> = Vec::new();
for i in &[1, 2, 3] { foo.push(i) }
}
```
Note that if you add any code following the for-loop above, or even a
semicolon to the end of it, then the code will stop compiling (i.e.,
it gathers a vector of references but the gathered vector cannot
actually be used.)
(The above code, despite being useless, did occur in one run-pass test
by accident; that test is updated here to accommodate the new
striction.)
----
So, technically this is a:
[breaking-change]
2015-02-04 12:24:44 +00:00
|
|
|
// };
|
|
|
|
// result
|
2015-01-08 23:04:26 +00:00
|
|
|
// }
|
|
|
|
|
|
|
|
// expand <head>
|
2014-07-22 03:54:28 +00:00
|
|
|
let head = fld.fold_expr(head);
|
2015-01-08 23:04:26 +00:00
|
|
|
|
|
|
|
// create an hygienic ident
|
|
|
|
let iter = {
|
|
|
|
let ident = fld.cx.ident_of("iter");
|
|
|
|
let new_ident = fresh_name(&ident);
|
|
|
|
let rename = (ident, new_ident);
|
|
|
|
let mut rename_list = vec![rename];
|
|
|
|
let mut rename_fld = IdentRenamer{ renames: &mut rename_list };
|
|
|
|
|
|
|
|
rename_fld.fold_ident(ident)
|
|
|
|
};
|
|
|
|
|
|
|
|
let pat_span = pat.span;
|
|
|
|
// `:;std::option::Option::Some(<pat>) => <body>`
|
|
|
|
let pat_arm = {
|
|
|
|
let body_expr = fld.cx.expr_block(body);
|
|
|
|
let some_pat = fld.cx.pat_some(pat_span, pat);
|
|
|
|
|
|
|
|
fld.cx.arm(pat_span, vec![some_pat], body_expr)
|
|
|
|
};
|
|
|
|
|
|
|
|
// `::std::option::Option::None => break`
|
|
|
|
let break_arm = {
|
|
|
|
let break_expr = fld.cx.expr_break(span);
|
|
|
|
|
|
|
|
fld.cx.arm(span, vec![fld.cx.pat_none(span)], break_expr)
|
|
|
|
};
|
|
|
|
|
|
|
|
// `match ::std::iter::Iterator::next(&mut iter) { ... }`
|
|
|
|
let match_expr = {
|
|
|
|
let next_path = {
|
|
|
|
let strs = vec![
|
2014-09-07 21:57:26 +00:00
|
|
|
fld.cx.ident_of_std("core"),
|
2015-01-08 23:04:26 +00:00
|
|
|
fld.cx.ident_of("iter"),
|
|
|
|
fld.cx.ident_of("Iterator"),
|
|
|
|
fld.cx.ident_of("next"),
|
|
|
|
];
|
|
|
|
|
|
|
|
fld.cx.path_global(span, strs)
|
|
|
|
};
|
|
|
|
let ref_mut_iter = fld.cx.expr_mut_addr_of(span, fld.cx.expr_ident(span, iter));
|
|
|
|
let next_expr =
|
|
|
|
fld.cx.expr_call(span, fld.cx.expr_path(next_path), vec![ref_mut_iter]);
|
|
|
|
let arms = vec![pat_arm, break_arm];
|
|
|
|
|
2015-01-22 22:46:40 +00:00
|
|
|
fld.cx.expr(pat_span,
|
|
|
|
ast::ExprMatch(next_expr, arms, ast::MatchSource::ForLoopDesugar))
|
2015-01-08 23:04:26 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
// `[opt_ident]: loop { ... }`
|
|
|
|
let loop_block = fld.cx.block_expr(match_expr);
|
|
|
|
let (loop_block, opt_ident) = expand_loop_block(loop_block, opt_ident, fld);
|
|
|
|
let loop_expr = fld.cx.expr(span, ast::ExprLoop(loop_block, opt_ident));
|
|
|
|
|
|
|
|
// `mut iter => { ... }`
|
|
|
|
let iter_arm = {
|
|
|
|
let iter_pat =
|
|
|
|
fld.cx.pat_ident_binding_mode(span, iter, ast::BindByValue(ast::MutMutable));
|
|
|
|
fld.cx.arm(span, vec![iter_pat], loop_expr)
|
|
|
|
};
|
|
|
|
|
|
|
|
// `match ::std::iter::IntoIterator::into_iter(<head>) { ... }`
|
|
|
|
let into_iter_expr = {
|
|
|
|
let into_iter_path = {
|
|
|
|
let strs = vec![
|
2014-09-07 21:57:26 +00:00
|
|
|
fld.cx.ident_of_std("core"),
|
2015-01-08 23:04:26 +00:00
|
|
|
fld.cx.ident_of("iter"),
|
|
|
|
fld.cx.ident_of("IntoIterator"),
|
|
|
|
fld.cx.ident_of("into_iter"),
|
|
|
|
];
|
|
|
|
|
|
|
|
fld.cx.path_global(span, strs)
|
|
|
|
};
|
|
|
|
|
|
|
|
fld.cx.expr_call(span, fld.cx.expr_path(into_iter_path), vec![head])
|
|
|
|
};
|
make `for PAT in ITER_EXPR { ... }` a terminating-scope for ITER_EXPR.
In effect, temporary anonymous values created during the evaluation of
ITER_EXPR no longer not live for the entirety of the block surrounding
the for-loop; instead they only live for the extent of the for-loop
itself, and no longer.
----
There is one case I know of that this breaks, demonstrated to me by
niko (but it is also a corner-case that is useless in practice). Here
is that case:
```
fn main() {
let mut foo: Vec<&i8> = Vec::new();
for i in &[1, 2, 3] { foo.push(i) }
}
```
Note that if you add any code following the for-loop above, or even a
semicolon to the end of it, then the code will stop compiling (i.e.,
it gathers a vector of references but the gathered vector cannot
actually be used.)
(The above code, despite being useless, did occur in one run-pass test
by accident; that test is updated here to accommodate the new
striction.)
----
So, technically this is a:
[breaking-change]
2015-02-04 12:24:44 +00:00
|
|
|
|
|
|
|
let match_expr = fld.cx.expr_match(span, into_iter_expr, vec![iter_arm]);
|
|
|
|
|
|
|
|
// `{ let result = ...; result }`
|
|
|
|
let result_ident = token::gensym_ident("result");
|
|
|
|
fld.cx.expr_block(
|
|
|
|
fld.cx.block_all(
|
|
|
|
span,
|
|
|
|
vec![fld.cx.stmt_let(span, false, result_ident, match_expr)],
|
|
|
|
Some(fld.cx.expr_ident(span, result_ident))))
|
2014-07-22 03:54:28 +00:00
|
|
|
}
|
|
|
|
|
2015-02-03 16:34:05 +00:00
|
|
|
ast::ExprClosure(capture_clause, fn_decl, block) => {
|
2014-07-04 00:20:25 +00:00
|
|
|
let (rewritten_fn_decl, rewritten_block)
|
2014-09-13 16:06:01 +00:00
|
|
|
= expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
|
2014-11-19 16:18:17 +00:00
|
|
|
let new_node = ast::ExprClosure(capture_clause,
|
2014-07-23 19:43:29 +00:00
|
|
|
rewritten_fn_decl,
|
|
|
|
rewritten_block);
|
2014-09-13 16:06:01 +00:00
|
|
|
P(ast::Expr{id:id, node: new_node, span: fld.new_span(span)})
|
2014-07-04 00:20:25 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
_ => {
|
|
|
|
P(noop_fold_expr(ast::Expr {
|
|
|
|
id: id,
|
|
|
|
node: node,
|
|
|
|
span: span
|
|
|
|
}, fld))
|
|
|
|
}
|
|
|
|
})
|
2011-07-06 22:22:23 +00:00
|
|
|
}
|
|
|
|
|
2014-07-12 22:00:23 +00:00
|
|
|
/// Expand a (not-ident-style) macro invocation. Returns the result
|
|
|
|
/// of expansion and the mark which must be applied to the result.
|
|
|
|
/// Our current interface doesn't allow us to apply the mark to the
|
|
|
|
/// result until after calling make_expr, make_items, etc.
|
2014-12-08 18:28:32 +00:00
|
|
|
fn expand_mac_invoc<T, F, G>(mac: ast::Mac, span: codemap::Span,
|
|
|
|
parse_thunk: F,
|
|
|
|
mark_thunk: G,
|
|
|
|
fld: &mut MacroExpander)
|
|
|
|
-> Option<T> where
|
2015-02-12 15:41:47 +00:00
|
|
|
F: for<'a> FnOnce(Box<MacResult+'a>) -> Option<T>,
|
2014-12-08 18:28:32 +00:00
|
|
|
G: FnOnce(T, Mrk) -> T,
|
2014-08-28 01:46:52 +00:00
|
|
|
{
|
2014-09-13 16:06:01 +00:00
|
|
|
match mac.node {
|
2014-07-12 22:00:23 +00:00
|
|
|
// it would almost certainly be cleaner to pass the whole
|
|
|
|
// macro invocation in, rather than pulling it apart and
|
|
|
|
// marking the tts and the ctxt separately. This also goes
|
|
|
|
// for the other three macro invocation chunks of code
|
|
|
|
// in this file.
|
|
|
|
// Token-tree macros:
|
2014-09-13 16:06:01 +00:00
|
|
|
MacInvocTT(pth, tts, _) => {
|
2015-01-28 01:01:48 +00:00
|
|
|
if pth.segments.len() > 1 {
|
2014-07-12 22:00:23 +00:00
|
|
|
fld.cx.span_err(pth.span,
|
|
|
|
"expected macro name without module \
|
|
|
|
separators");
|
|
|
|
// let compilation continue
|
|
|
|
return None;
|
|
|
|
}
|
2014-10-15 06:05:01 +00:00
|
|
|
let extname = pth.segments[0].identifier;
|
2014-07-12 22:00:23 +00:00
|
|
|
let extnamestr = token::get_ident(extname);
|
2014-07-19 19:34:24 +00:00
|
|
|
match fld.cx.syntax_env.find(&extname.name) {
|
2014-07-12 22:00:23 +00:00
|
|
|
None => {
|
|
|
|
fld.cx.span_err(
|
|
|
|
pth.span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("macro undefined: '{}!'",
|
2015-02-20 19:08:14 +00:00
|
|
|
&extnamestr));
|
2014-07-12 22:00:23 +00:00
|
|
|
|
|
|
|
// let compilation continue
|
|
|
|
None
|
|
|
|
}
|
2014-07-19 19:34:24 +00:00
|
|
|
Some(rc) => match *rc {
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
NormalTT(ref expandfun, exp_span, allow_internal_unstable) => {
|
2014-07-19 19:34:24 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
2014-09-13 16:06:01 +00:00
|
|
|
call_site: span,
|
2014-07-19 19:34:24 +00:00
|
|
|
callee: NameAndSpan {
|
2015-02-03 22:31:06 +00:00
|
|
|
name: extnamestr.to_string(),
|
2014-07-19 19:34:24 +00:00
|
|
|
format: MacroBang,
|
|
|
|
span: exp_span,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
2014-07-19 19:34:24 +00:00
|
|
|
},
|
|
|
|
});
|
|
|
|
let fm = fresh_mark();
|
2015-02-18 19:48:57 +00:00
|
|
|
let marked_before = mark_tts(&tts[..], fm);
|
2014-07-19 19:34:24 +00:00
|
|
|
|
|
|
|
// The span that we pass to the expanders we want to
|
|
|
|
// be the root of the call stack. That's the most
|
|
|
|
// relevant span and it's the actual invocation of
|
|
|
|
// the macro.
|
2014-09-17 16:01:33 +00:00
|
|
|
let mac_span = fld.cx.original_span();
|
2014-07-19 19:34:24 +00:00
|
|
|
|
2014-08-28 01:46:52 +00:00
|
|
|
let opt_parsed = {
|
|
|
|
let expanded = expandfun.expand(fld.cx,
|
2014-09-17 16:01:33 +00:00
|
|
|
mac_span,
|
2015-02-18 19:48:57 +00:00
|
|
|
&marked_before[..]);
|
2014-08-28 01:46:52 +00:00
|
|
|
parse_thunk(expanded)
|
|
|
|
};
|
|
|
|
let parsed = match opt_parsed {
|
2014-07-19 19:34:24 +00:00
|
|
|
Some(e) => e,
|
|
|
|
None => {
|
|
|
|
fld.cx.span_err(
|
|
|
|
pth.span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("non-expression macro in expression position: {}",
|
2015-02-18 19:48:57 +00:00
|
|
|
&extnamestr[..]
|
2015-02-20 19:08:14 +00:00
|
|
|
));
|
2014-07-19 19:34:24 +00:00
|
|
|
return None;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
Some(mark_thunk(parsed,fm))
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
fld.cx.span_err(
|
|
|
|
pth.span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("'{}' is not a tt-style macro",
|
2015-02-20 19:08:14 +00:00
|
|
|
&extnamestr));
|
2014-07-19 19:34:24 +00:00
|
|
|
None
|
|
|
|
}
|
2014-07-12 22:00:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-09 20:12:30 +00:00
|
|
|
/// Rename loop label and expand its loop body
|
|
|
|
///
|
|
|
|
/// The renaming procedure for loop is different in the sense that the loop
|
|
|
|
/// body is in a block enclosed by loop head so the renaming of loop label
|
|
|
|
/// must be propagated to the enclosed context.
|
2014-02-24 20:47:19 +00:00
|
|
|
fn expand_loop_block(loop_block: P<Block>,
|
|
|
|
opt_ident: Option<Ident>,
|
|
|
|
fld: &mut MacroExpander) -> (P<Block>, Option<Ident>) {
|
2014-02-15 08:54:32 +00:00
|
|
|
match opt_ident {
|
|
|
|
Some(label) => {
|
|
|
|
let new_label = fresh_name(&label);
|
|
|
|
let rename = (label, new_label);
|
2014-02-24 20:47:19 +00:00
|
|
|
|
|
|
|
// The rename *must not* be added to the pending list of current
|
|
|
|
// syntax context otherwise an unrelated `break` or `continue` in
|
|
|
|
// the same context will pick that up in the deferred renaming pass
|
|
|
|
// and be renamed incorrectly.
|
|
|
|
let mut rename_list = vec!(rename);
|
2014-06-26 20:18:52 +00:00
|
|
|
let mut rename_fld = IdentRenamer{renames: &mut rename_list};
|
2014-02-24 20:47:19 +00:00
|
|
|
let renamed_ident = rename_fld.fold_ident(label);
|
|
|
|
|
|
|
|
// The rename *must* be added to the enclosed syntax context for
|
|
|
|
// `break` or `continue` to pick up because by definition they are
|
|
|
|
// in a block enclosed by loop head.
|
2014-07-19 19:34:24 +00:00
|
|
|
fld.cx.syntax_env.push_frame();
|
|
|
|
fld.cx.syntax_env.info().pending_renames.push(rename);
|
2014-09-13 16:06:01 +00:00
|
|
|
let expanded_block = expand_block_elts(loop_block, fld);
|
2014-07-19 19:34:24 +00:00
|
|
|
fld.cx.syntax_env.pop_frame();
|
2014-02-24 20:47:19 +00:00
|
|
|
|
|
|
|
(expanded_block, Some(renamed_ident))
|
2014-02-15 08:54:32 +00:00
|
|
|
}
|
2014-02-24 20:47:19 +00:00
|
|
|
None => (fld.fold_block(loop_block), opt_ident)
|
2014-02-15 08:54:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-04 19:05:43 +00:00
|
|
|
// eval $e with a new exts frame.
|
|
|
|
// must be a macro so that $e isn't evaluated too early.
|
2014-11-14 17:18:10 +00:00
|
|
|
macro_rules! with_exts_frame {
|
2013-05-08 22:27:29 +00:00
|
|
|
($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
|
2013-12-31 01:45:39 +00:00
|
|
|
({$extsboxexpr.push_frame();
|
|
|
|
$extsboxexpr.info().macros_escape = $macros_escape;
|
2013-02-26 18:15:29 +00:00
|
|
|
let result = $e;
|
2013-12-31 01:45:39 +00:00
|
|
|
$extsboxexpr.pop_frame();
|
2013-02-26 18:15:29 +00:00
|
|
|
result
|
|
|
|
})
|
2014-11-14 17:18:10 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
|
2012-07-27 18:19:21 +00:00
|
|
|
// When we enter a module, record it, for the sake of `module!`
|
2014-09-13 16:06:01 +00:00
|
|
|
pub fn expand_item(it: P<ast::Item>, fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<P<ast::Item>> {
|
2014-02-28 07:49:25 +00:00
|
|
|
let it = expand_item_modifiers(it, fld);
|
|
|
|
|
2014-12-02 18:07:41 +00:00
|
|
|
expand_annotatable(Annotatable::Item(it), fld)
|
|
|
|
.into_iter().map(|i| i.expect_item()).collect()
|
2012-05-18 17:00:49 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
|
|
|
|
-> P<ast::Item> {
|
2014-07-04 19:18:37 +00:00
|
|
|
// partition the attributes into ItemModifiers and others
|
2014-12-02 18:07:41 +00:00
|
|
|
let (modifiers, other_attrs) = modifiers(&it.attrs, fld);
|
|
|
|
|
2014-07-04 19:18:37 +00:00
|
|
|
// update the attrs, leave everything else alone. Is this mutation really a good idea?
|
2014-09-13 16:06:01 +00:00
|
|
|
it = P(ast::Item {
|
2014-07-04 19:18:37 +00:00
|
|
|
attrs: other_attrs,
|
2014-02-28 07:49:25 +00:00
|
|
|
..(*it).clone()
|
2014-09-13 16:06:01 +00:00
|
|
|
});
|
2014-02-28 07:49:25 +00:00
|
|
|
|
|
|
|
if modifiers.is_empty() {
|
2014-12-02 18:07:41 +00:00
|
|
|
let it = expand_item_multi_modifier(Annotatable::Item(it), fld);
|
|
|
|
return it.expect_item();
|
2014-02-28 07:49:25 +00:00
|
|
|
}
|
|
|
|
|
2015-01-31 17:20:46 +00:00
|
|
|
for attr in &modifiers {
|
2014-02-28 07:49:25 +00:00
|
|
|
let mname = attr.name();
|
|
|
|
|
2015-02-04 20:48:12 +00:00
|
|
|
match fld.cx.syntax_env.find(&intern(&mname)) {
|
2014-07-19 19:34:24 +00:00
|
|
|
Some(rc) => match *rc {
|
2014-09-11 05:07:49 +00:00
|
|
|
Modifier(ref mac) => {
|
2014-07-19 19:34:24 +00:00
|
|
|
attr::mark_used(attr);
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: attr.span,
|
|
|
|
callee: NameAndSpan {
|
2015-02-03 22:31:06 +00:00
|
|
|
name: mname.to_string(),
|
2014-07-19 19:34:24 +00:00
|
|
|
format: MacroAttribute,
|
|
|
|
span: None,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
// attributes can do whatever they like,
|
|
|
|
// for now
|
|
|
|
allow_internal_unstable: true,
|
2014-07-19 19:34:24 +00:00
|
|
|
}
|
|
|
|
});
|
2014-09-13 16:06:01 +00:00
|
|
|
it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
|
2014-07-19 19:34:24 +00:00
|
|
|
fld.cx.bt_pop();
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
},
|
2014-02-28 07:49:25 +00:00
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-02 18:07:41 +00:00
|
|
|
// Expansion may have added new ItemModifiers.
|
|
|
|
// It is possible, that an item modifier could expand to a multi-modifier or
|
|
|
|
// vice versa. In this case we will expand all modifiers before multi-modifiers,
|
|
|
|
// which might give an odd ordering. However, I think it is unlikely that the
|
|
|
|
// two kinds will be mixed, and I old-style multi-modifiers should be deprecated
|
|
|
|
// anyway.
|
2014-02-28 07:49:25 +00:00
|
|
|
expand_item_modifiers(it, fld)
|
|
|
|
}
|
|
|
|
|
2014-07-04 19:19:15 +00:00
|
|
|
/// Expand item_underscore
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_item_underscore(item: ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
|
|
|
|
match item {
|
|
|
|
ast::ItemFn(decl, fn_style, abi, generics, body) => {
|
2014-07-04 19:19:15 +00:00
|
|
|
let (rewritten_fn_decl, rewritten_body)
|
2014-09-13 16:06:01 +00:00
|
|
|
= expand_and_rename_fn_decl_and_block(decl, body, fld);
|
2014-07-25 19:00:33 +00:00
|
|
|
let expanded_generics = fold::noop_fold_generics(generics,fld);
|
2014-07-04 19:19:15 +00:00
|
|
|
ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body)
|
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
_ => noop_fold_item_underscore(item, fld)
|
2014-07-04 19:19:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-19 04:48:26 +00:00
|
|
|
// does this attribute list contain "macro_use" ?
|
|
|
|
fn contains_macro_use(fld: &mut MacroExpander, attrs: &[ast::Attribute]) -> bool {
|
2015-01-31 17:20:46 +00:00
|
|
|
for attr in attrs {
|
2014-12-19 04:48:26 +00:00
|
|
|
let mut is_use = attr.check_name("macro_use");
|
|
|
|
if attr.check_name("macro_escape") {
|
|
|
|
fld.cx.span_warn(attr.span, "macro_escape is a deprecated synonym for macro_use");
|
|
|
|
is_use = true;
|
|
|
|
if let ast::AttrInner = attr.node.style {
|
2015-02-24 14:07:54 +00:00
|
|
|
fld.cx.fileline_help(attr.span, "consider an outer attribute, \
|
2014-12-19 04:48:26 +00:00
|
|
|
#[macro_use] mod ...");
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
if is_use {
|
|
|
|
match attr.node.value.node {
|
|
|
|
ast::MetaWord(..) => (),
|
|
|
|
_ => fld.cx.span_err(attr.span, "arguments to macro_use are not allowed here"),
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
false
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2012-07-27 18:19:21 +00:00
|
|
|
// Support for item-position macro invocations, exactly the same
|
|
|
|
// logic as for expression-position macro invocations.
|
2014-09-16 01:27:28 +00:00
|
|
|
pub fn expand_item_mac(it: P<ast::Item>,
|
|
|
|
fld: &mut MacroExpander) -> SmallVector<P<ast::Item>> {
|
2014-09-13 16:06:01 +00:00
|
|
|
let (extname, path_span, tts) = match it.node {
|
2014-01-09 13:05:33 +00:00
|
|
|
ItemMac(codemap::Spanned {
|
2014-01-25 21:34:26 +00:00
|
|
|
node: MacInvocTT(ref pth, ref tts, _),
|
2013-11-28 20:22:53 +00:00
|
|
|
..
|
2013-08-29 19:10:02 +00:00
|
|
|
}) => {
|
2014-10-15 06:05:01 +00:00
|
|
|
(pth.segments[0].identifier, pth.span, (*tts).clone())
|
2012-12-27 19:36:00 +00:00
|
|
|
}
|
2013-12-28 00:43:01 +00:00
|
|
|
_ => fld.cx.span_bug(it.span, "invalid item macro invocation")
|
2012-12-12 20:25:40 +00:00
|
|
|
};
|
2012-11-17 12:41:36 +00:00
|
|
|
|
2014-02-14 05:07:09 +00:00
|
|
|
let extnamestr = token::get_ident(extname);
|
2013-06-29 12:59:08 +00:00
|
|
|
let fm = fresh_mark();
|
2014-09-15 23:09:09 +00:00
|
|
|
let items = {
|
|
|
|
let expanded = match fld.cx.syntax_env.find(&extname.name) {
|
2014-08-28 01:46:52 +00:00
|
|
|
None => {
|
2014-09-13 16:06:01 +00:00
|
|
|
fld.cx.span_err(path_span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("macro undefined: '{}!'",
|
2015-02-20 19:08:14 +00:00
|
|
|
extnamestr));
|
2014-08-28 01:46:52 +00:00
|
|
|
// let compilation continue
|
|
|
|
return SmallVector::zero();
|
|
|
|
}
|
2012-11-17 12:41:36 +00:00
|
|
|
|
2014-08-28 01:46:52 +00:00
|
|
|
Some(rc) => match *rc {
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
NormalTT(ref expander, span, allow_internal_unstable) => {
|
2014-08-28 01:46:52 +00:00
|
|
|
if it.ident.name != parse::token::special_idents::invalid.name {
|
|
|
|
fld.cx
|
2014-09-13 16:06:01 +00:00
|
|
|
.span_err(path_span,
|
2015-02-20 19:08:14 +00:00
|
|
|
&format!("macro {}! expects no ident argument, given '{}'",
|
|
|
|
extnamestr,
|
|
|
|
token::get_ident(it.ident)));
|
2014-08-28 01:46:52 +00:00
|
|
|
return SmallVector::zero();
|
|
|
|
}
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: it.span,
|
|
|
|
callee: NameAndSpan {
|
2015-02-03 22:31:06 +00:00
|
|
|
name: extnamestr.to_string(),
|
2014-08-28 01:46:52 +00:00
|
|
|
format: MacroBang,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
span: span,
|
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
2014-08-28 01:46:52 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
// mark before expansion:
|
2015-02-18 19:48:57 +00:00
|
|
|
let marked_before = mark_tts(&tts[..], fm);
|
|
|
|
expander.expand(fld.cx, it.span, &marked_before[..])
|
2014-07-19 19:34:24 +00:00
|
|
|
}
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
IdentTT(ref expander, span, allow_internal_unstable) => {
|
2014-08-28 01:46:52 +00:00
|
|
|
if it.ident.name == parse::token::special_idents::invalid.name {
|
2014-09-13 16:06:01 +00:00
|
|
|
fld.cx.span_err(path_span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("macro {}! expects an ident argument",
|
2015-02-20 19:08:14 +00:00
|
|
|
&extnamestr));
|
2014-08-28 01:46:52 +00:00
|
|
|
return SmallVector::zero();
|
2014-07-19 19:34:24 +00:00
|
|
|
}
|
2014-08-28 01:46:52 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: it.span,
|
|
|
|
callee: NameAndSpan {
|
2015-02-03 22:31:06 +00:00
|
|
|
name: extnamestr.to_string(),
|
2014-08-28 01:46:52 +00:00
|
|
|
format: MacroBang,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
span: span,
|
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
2014-08-28 01:46:52 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
// mark before expansion:
|
2015-02-18 19:48:57 +00:00
|
|
|
let marked_tts = mark_tts(&tts[..], fm);
|
2014-08-28 01:46:52 +00:00
|
|
|
expander.expand(fld.cx, it.span, it.ident, marked_tts)
|
2013-02-21 08:16:31 +00:00
|
|
|
}
|
2014-09-15 23:09:09 +00:00
|
|
|
MacroRulesTT => {
|
2014-08-28 01:46:52 +00:00
|
|
|
if it.ident.name == parse::token::special_idents::invalid.name {
|
2014-09-13 16:06:01 +00:00
|
|
|
fld.cx.span_err(path_span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("macro_rules! expects an ident argument")
|
2015-02-20 19:08:14 +00:00
|
|
|
);
|
2014-08-28 01:46:52 +00:00
|
|
|
return SmallVector::zero();
|
2014-07-19 19:34:24 +00:00
|
|
|
}
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
|
2014-08-28 01:46:52 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: it.span,
|
|
|
|
callee: NameAndSpan {
|
2015-02-03 22:31:06 +00:00
|
|
|
name: extnamestr.to_string(),
|
2014-08-28 01:46:52 +00:00
|
|
|
format: MacroBang,
|
2014-09-15 23:09:09 +00:00
|
|
|
span: None,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
// `macro_rules!` doesn't directly allow
|
|
|
|
// unstable (this is orthogonal to whether
|
|
|
|
// the macro it creates allows it)
|
|
|
|
allow_internal_unstable: false,
|
2014-08-28 01:46:52 +00:00
|
|
|
}
|
|
|
|
});
|
2014-09-15 23:09:09 +00:00
|
|
|
// DON'T mark before expansion.
|
2014-12-31 03:10:46 +00:00
|
|
|
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
let allow_internal_unstable = attr::contains_name(&it.attrs,
|
|
|
|
"allow_internal_unstable");
|
|
|
|
|
|
|
|
// ensure any #[allow_internal_unstable]s are
|
|
|
|
// detected (including nested macro definitions
|
|
|
|
// etc.)
|
|
|
|
if allow_internal_unstable && !fld.cx.ecfg.enable_allow_internal_unstable() {
|
|
|
|
feature_gate::emit_feature_err(
|
|
|
|
&fld.cx.parse_sess.span_diagnostic,
|
|
|
|
"allow_internal_unstable",
|
|
|
|
it.span,
|
|
|
|
feature_gate::EXPLAIN_ALLOW_INTERNAL_UNSTABLE)
|
|
|
|
}
|
|
|
|
|
2014-12-31 03:10:46 +00:00
|
|
|
let def = ast::MacroDef {
|
|
|
|
ident: it.ident,
|
|
|
|
attrs: it.attrs.clone(),
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
span: it.span,
|
|
|
|
imported_from: None,
|
2015-02-02 02:53:25 +00:00
|
|
|
export: attr::contains_name(&it.attrs, "macro_export"),
|
2015-01-02 20:50:45 +00:00
|
|
|
use_locally: true,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
2014-12-31 03:10:46 +00:00
|
|
|
body: tts,
|
|
|
|
};
|
2015-01-02 00:37:47 +00:00
|
|
|
fld.cx.insert_macro(def);
|
2014-09-15 23:09:09 +00:00
|
|
|
|
|
|
|
// macro_rules! has a side effect but expands to nothing.
|
|
|
|
fld.cx.bt_pop();
|
|
|
|
return SmallVector::zero();
|
2014-08-28 01:46:52 +00:00
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
fld.cx.span_err(it.span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("{}! is not legal in item position",
|
2015-02-20 19:08:14 +00:00
|
|
|
&extnamestr));
|
2014-07-19 19:34:24 +00:00
|
|
|
return SmallVector::zero();
|
2013-02-21 08:16:31 +00:00
|
|
|
}
|
2014-07-07 16:54:08 +00:00
|
|
|
}
|
2014-08-28 01:46:52 +00:00
|
|
|
};
|
|
|
|
|
2014-09-15 23:09:09 +00:00
|
|
|
expanded.make_items()
|
2012-11-17 12:41:36 +00:00
|
|
|
};
|
2012-11-09 04:12:45 +00:00
|
|
|
|
2014-09-15 23:09:09 +00:00
|
|
|
let items = match items {
|
|
|
|
Some(items) => {
|
2014-09-15 03:27:36 +00:00
|
|
|
items.into_iter()
|
2014-08-28 01:46:52 +00:00
|
|
|
.map(|i| mark_item(i, fm))
|
2014-09-15 03:27:36 +00:00
|
|
|
.flat_map(|i| fld.fold_item(i).into_iter())
|
2014-08-28 01:46:52 +00:00
|
|
|
.collect()
|
|
|
|
}
|
2014-09-15 23:09:09 +00:00
|
|
|
None => {
|
2014-09-13 16:06:01 +00:00
|
|
|
fld.cx.span_err(path_span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("non-item macro in item position: {}",
|
2015-02-20 19:08:14 +00:00
|
|
|
&extnamestr));
|
2014-08-28 01:46:52 +00:00
|
|
|
return SmallVector::zero();
|
2014-04-15 12:00:14 +00:00
|
|
|
}
|
2012-11-17 12:41:36 +00:00
|
|
|
};
|
2014-08-28 01:46:52 +00:00
|
|
|
|
2013-12-28 00:43:01 +00:00
|
|
|
fld.cx.bt_pop();
|
2014-09-13 16:06:01 +00:00
|
|
|
items
|
2012-07-05 19:10:33 +00:00
|
|
|
}
|
|
|
|
|
2014-07-12 22:00:23 +00:00
|
|
|
/// Expand a stmt
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_stmt(s: Stmt, fld: &mut MacroExpander) -> SmallVector<P<Stmt>> {
|
2014-11-14 17:18:10 +00:00
|
|
|
let (mac, style) = match s.node {
|
|
|
|
StmtMac(mac, style) => (mac, style),
|
2013-12-28 00:43:01 +00:00
|
|
|
_ => return expand_non_macro_stmt(s, fld)
|
2012-12-12 20:25:40 +00:00
|
|
|
};
|
2014-12-05 09:10:22 +00:00
|
|
|
let expanded_stmt = match expand_mac_invoc(mac.and_then(|m| m), s.span,
|
2014-09-13 16:06:01 +00:00
|
|
|
|r| r.make_stmt(),
|
|
|
|
mark_stmt, fld) {
|
2014-07-12 22:00:23 +00:00
|
|
|
Some(stmt) => stmt,
|
2013-08-29 19:10:02 +00:00
|
|
|
None => {
|
2014-01-17 14:53:10 +00:00
|
|
|
return SmallVector::zero();
|
2012-11-17 12:41:36 +00:00
|
|
|
}
|
2012-11-21 23:49:42 +00:00
|
|
|
};
|
|
|
|
|
2014-01-06 02:02:57 +00:00
|
|
|
// Keep going, outside-in.
|
2014-09-13 16:06:01 +00:00
|
|
|
let fully_expanded = fld.fold_stmt(expanded_stmt);
|
2014-01-06 02:02:57 +00:00
|
|
|
fld.cx.bt_pop();
|
2014-09-13 16:06:01 +00:00
|
|
|
|
2014-11-14 17:18:10 +00:00
|
|
|
if style == MacStmtWithSemicolon {
|
2014-09-15 03:27:36 +00:00
|
|
|
fully_expanded.into_iter().map(|s| s.map(|Spanned {node, span}| {
|
2014-09-13 16:06:01 +00:00
|
|
|
Spanned {
|
|
|
|
node: match node {
|
|
|
|
StmtExpr(e, stmt_id) => StmtSemi(e, stmt_id),
|
|
|
|
_ => node /* might already have a semi */
|
|
|
|
},
|
|
|
|
span: span
|
2013-11-25 07:08:53 +00:00
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
})).collect()
|
|
|
|
} else {
|
|
|
|
fully_expanded
|
|
|
|
}
|
2012-11-20 01:31:22 +00:00
|
|
|
}
|
|
|
|
|
2013-06-25 18:40:51 +00:00
|
|
|
// expand a non-macro stmt. this is essentially the fallthrough for
|
|
|
|
// expand_stmt, above.
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_non_macro_stmt(Spanned {node, span: stmt_span}: Stmt, fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<P<Stmt>> {
|
2013-06-25 18:40:51 +00:00
|
|
|
// is it a let?
|
2014-09-13 16:06:01 +00:00
|
|
|
match node {
|
|
|
|
StmtDecl(decl, node_id) => decl.and_then(|Spanned {node: decl, span}| match decl {
|
|
|
|
DeclLocal(local) => {
|
|
|
|
// take it apart:
|
|
|
|
let rewritten_local = local.map(|Local {id, pat, ty, init, source, span}| {
|
2014-07-19 07:06:43 +00:00
|
|
|
// expand the ty since TyFixedLengthVec contains an Expr
|
|
|
|
// and thus may have a macro use
|
2015-01-02 11:55:31 +00:00
|
|
|
let expanded_ty = ty.map(|t| fld.fold_ty(t));
|
2014-06-26 23:49:13 +00:00
|
|
|
// expand the pat (it might contain macro uses):
|
2014-01-03 23:08:48 +00:00
|
|
|
let expanded_pat = fld.fold_pat(pat);
|
2014-07-04 19:05:43 +00:00
|
|
|
// find the PatIdents in the pattern:
|
2014-01-03 23:08:48 +00:00
|
|
|
// oh dear heaven... this is going to include the enum
|
|
|
|
// names, as well... but that should be okay, as long as
|
|
|
|
// the new names are gensyms for the old ones.
|
|
|
|
// generate fresh names, push them to a new pending list
|
2014-07-07 23:35:15 +00:00
|
|
|
let idents = pattern_bindings(&*expanded_pat);
|
2014-06-26 21:39:54 +00:00
|
|
|
let mut new_pending_renames =
|
|
|
|
idents.iter().map(|ident| (*ident, fresh_name(ident))).collect();
|
2014-06-26 23:49:13 +00:00
|
|
|
// rewrite the pattern using the new names (the old
|
|
|
|
// ones have already been applied):
|
2014-01-03 23:08:48 +00:00
|
|
|
let rewritten_pat = {
|
2014-06-26 23:49:13 +00:00
|
|
|
// nested binding to allow borrow to expire:
|
|
|
|
let mut rename_fld = IdentRenamer{renames: &mut new_pending_renames};
|
2014-01-03 23:08:48 +00:00
|
|
|
rename_fld.fold_pat(expanded_pat)
|
|
|
|
};
|
|
|
|
// add them to the existing pending renames:
|
2014-10-15 06:05:01 +00:00
|
|
|
fld.cx.syntax_env.info().pending_renames
|
|
|
|
.extend(new_pending_renames.into_iter());
|
2014-09-13 16:06:01 +00:00
|
|
|
Local {
|
|
|
|
id: id,
|
|
|
|
ty: expanded_ty,
|
|
|
|
pat: rewritten_pat,
|
|
|
|
// also, don't forget to expand the init:
|
|
|
|
init: init.map(|e| fld.fold_expr(e)),
|
|
|
|
source: source,
|
2014-01-03 23:08:48 +00:00
|
|
|
span: span
|
2014-09-13 16:06:01 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
SmallVector::one(P(Spanned {
|
|
|
|
node: StmtDecl(P(Spanned {
|
|
|
|
node: DeclLocal(rewritten_local),
|
|
|
|
span: span
|
|
|
|
}),
|
|
|
|
node_id),
|
|
|
|
span: stmt_span
|
|
|
|
}))
|
2013-12-31 01:45:39 +00:00
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
_ => {
|
|
|
|
noop_fold_stmt(Spanned {
|
|
|
|
node: StmtDecl(P(Spanned {
|
|
|
|
node: decl,
|
|
|
|
span: span
|
|
|
|
}),
|
|
|
|
node_id),
|
|
|
|
span: stmt_span
|
|
|
|
}, fld)
|
|
|
|
}
|
|
|
|
}),
|
|
|
|
_ => {
|
|
|
|
noop_fold_stmt(Spanned {
|
|
|
|
node: node,
|
|
|
|
span: stmt_span
|
|
|
|
}, fld)
|
|
|
|
}
|
2013-06-25 18:40:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-04 19:19:15 +00:00
|
|
|
// expand the arm of a 'match', renaming for macro hygiene
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_arm(arm: ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
|
2014-06-26 23:49:13 +00:00
|
|
|
// expand pats... they might contain macro uses:
|
2014-09-13 16:06:01 +00:00
|
|
|
let expanded_pats = arm.pats.move_map(|pat| fld.fold_pat(pat));
|
2014-06-26 23:49:13 +00:00
|
|
|
if expanded_pats.len() == 0 {
|
2014-10-09 19:17:22 +00:00
|
|
|
panic!("encountered match arm with 0 patterns");
|
2014-06-25 22:20:01 +00:00
|
|
|
}
|
2014-06-26 20:19:34 +00:00
|
|
|
// all of the pats must have the same set of bindings, so use the
|
|
|
|
// first one to extract them and generate new names:
|
2014-10-15 06:05:01 +00:00
|
|
|
let idents = pattern_bindings(&*expanded_pats[0]);
|
2014-09-15 03:27:36 +00:00
|
|
|
let new_renames = idents.into_iter().map(|id| (id, fresh_name(&id))).collect();
|
2014-07-04 19:15:24 +00:00
|
|
|
// apply the renaming, but only to the PatIdents:
|
2014-07-04 19:19:15 +00:00
|
|
|
let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
|
2014-09-13 16:06:01 +00:00
|
|
|
let rewritten_pats = expanded_pats.move_map(|pat| rename_pats_fld.fold_pat(pat));
|
2014-06-26 23:49:13 +00:00
|
|
|
// apply renaming and then expansion to the guard and the body:
|
2014-07-04 19:19:15 +00:00
|
|
|
let mut rename_fld = IdentRenamer{renames:&new_renames};
|
2014-06-26 23:49:13 +00:00
|
|
|
let rewritten_guard =
|
|
|
|
arm.guard.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
|
|
|
|
let rewritten_body = fld.fold_expr(rename_fld.fold_expr(arm.body));
|
2014-06-26 20:19:34 +00:00
|
|
|
ast::Arm {
|
2015-02-09 17:01:15 +00:00
|
|
|
attrs: fold::fold_attrs(arm.attrs, fld),
|
2014-06-26 23:49:13 +00:00
|
|
|
pats: rewritten_pats,
|
|
|
|
guard: rewritten_guard,
|
|
|
|
body: rewritten_body,
|
2014-06-26 23:53:33 +00:00
|
|
|
}
|
2014-06-25 22:20:01 +00:00
|
|
|
}
|
|
|
|
|
2014-07-04 19:05:43 +00:00
|
|
|
/// A visitor that extracts the PatIdent (binding) paths
|
|
|
|
/// from a given thingy and puts them in a mutable
|
|
|
|
/// array
|
2015-01-04 03:54:18 +00:00
|
|
|
#[derive(Clone)]
|
2014-07-04 19:05:43 +00:00
|
|
|
struct PatIdentFinder {
|
2014-09-12 10:10:30 +00:00
|
|
|
ident_accumulator: Vec<ast::Ident>
|
2013-07-20 01:42:11 +00:00
|
|
|
}
|
|
|
|
|
2014-09-09 22:54:36 +00:00
|
|
|
impl<'v> Visitor<'v> for PatIdentFinder {
|
2014-09-12 10:10:30 +00:00
|
|
|
fn visit_pat(&mut self, pattern: &ast::Pat) {
|
2013-07-20 01:42:11 +00:00
|
|
|
match *pattern {
|
2014-07-04 19:05:43 +00:00
|
|
|
ast::Pat { id: _, node: ast::PatIdent(_, ref path1, ref inner), span: _ } => {
|
2014-07-01 01:02:14 +00:00
|
|
|
self.ident_accumulator.push(path1.node);
|
2014-07-04 19:05:43 +00:00
|
|
|
// visit optional subpattern of PatIdent:
|
2015-01-31 17:20:46 +00:00
|
|
|
if let Some(ref subpat) = *inner {
|
2014-09-12 10:10:30 +00:00
|
|
|
self.visit_pat(&**subpat)
|
2013-06-04 21:56:33 +00:00
|
|
|
}
|
|
|
|
}
|
2014-07-04 19:05:43 +00:00
|
|
|
// use the default traversal for non-PatIdents
|
2014-09-12 10:10:30 +00:00
|
|
|
_ => visit::walk_pat(self, pattern)
|
2013-07-20 01:42:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-07-04 19:05:43 +00:00
|
|
|
/// find the PatIdent paths in a pattern
|
2014-09-12 10:10:30 +00:00
|
|
|
fn pattern_bindings(pat: &ast::Pat) -> Vec<ast::Ident> {
|
2014-07-04 19:05:43 +00:00
|
|
|
let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
|
2014-09-12 10:10:30 +00:00
|
|
|
name_finder.visit_pat(pat);
|
2014-06-26 21:39:54 +00:00
|
|
|
name_finder.ident_accumulator
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
2014-07-04 19:19:15 +00:00
|
|
|
/// find the PatIdent paths in a
|
|
|
|
fn fn_decl_arg_bindings(fn_decl: &ast::FnDecl) -> Vec<ast::Ident> {
|
|
|
|
let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
|
2015-01-31 17:20:46 +00:00
|
|
|
for arg in &fn_decl.inputs {
|
2014-09-12 10:10:30 +00:00
|
|
|
pat_idents.visit_pat(&*arg.pat);
|
2014-07-04 19:19:15 +00:00
|
|
|
}
|
|
|
|
pat_idents.ident_accumulator
|
|
|
|
}
|
|
|
|
|
2013-07-05 20:57:53 +00:00
|
|
|
// expand a block. pushes a new exts_frame, then calls expand_block_elts
|
2014-09-13 16:06:01 +00:00
|
|
|
pub fn expand_block(blk: P<Block>, fld: &mut MacroExpander) -> P<Block> {
|
2013-06-25 18:40:51 +00:00
|
|
|
// see note below about treatment of exts table
|
2014-07-19 19:34:24 +00:00
|
|
|
with_exts_frame!(fld.cx.syntax_env,false,
|
2013-12-28 00:43:01 +00:00
|
|
|
expand_block_elts(blk, fld))
|
2013-06-25 18:40:51 +00:00
|
|
|
}
|
|
|
|
|
2013-07-05 20:57:53 +00:00
|
|
|
// expand the elements of a block.
|
2014-09-13 16:06:01 +00:00
|
|
|
pub fn expand_block_elts(b: P<Block>, fld: &mut MacroExpander) -> P<Block> {
|
2015-01-13 15:30:17 +00:00
|
|
|
b.map(|Block {id, stmts, expr, rules, span}| {
|
2014-09-15 03:27:36 +00:00
|
|
|
let new_stmts = stmts.into_iter().flat_map(|x| {
|
2014-06-26 20:18:52 +00:00
|
|
|
// perform all pending renames
|
2014-02-07 22:28:07 +00:00
|
|
|
let renamed_stmt = {
|
2014-07-19 19:34:24 +00:00
|
|
|
let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
|
2014-06-26 20:18:52 +00:00
|
|
|
let mut rename_fld = IdentRenamer{renames:pending_renames};
|
2014-09-13 16:06:01 +00:00
|
|
|
rename_fld.fold_stmt(x).expect_one("rename_fold didn't return one value")
|
2014-02-07 22:28:07 +00:00
|
|
|
};
|
2014-06-26 20:18:52 +00:00
|
|
|
// expand macros in the statement
|
2014-09-15 03:27:36 +00:00
|
|
|
fld.fold_stmt(renamed_stmt).into_iter()
|
2014-02-07 22:28:07 +00:00
|
|
|
}).collect();
|
2014-09-13 16:06:01 +00:00
|
|
|
let new_expr = expr.map(|x| {
|
|
|
|
let expr = {
|
|
|
|
let pending_renames = &mut fld.cx.syntax_env.info().pending_renames;
|
|
|
|
let mut rename_fld = IdentRenamer{renames:pending_renames};
|
|
|
|
rename_fld.fold_expr(x)
|
|
|
|
};
|
|
|
|
fld.fold_expr(expr)
|
|
|
|
});
|
|
|
|
Block {
|
|
|
|
id: fld.new_id(id),
|
|
|
|
stmts: new_stmts,
|
|
|
|
expr: new_expr,
|
|
|
|
rules: rules,
|
|
|
|
span: span
|
|
|
|
}
|
2013-11-30 22:00:39 +00:00
|
|
|
})
|
2013-05-17 16:41:04 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_pat(p: P<ast::Pat>, fld: &mut MacroExpander) -> P<ast::Pat> {
|
|
|
|
match p.node {
|
|
|
|
PatMac(_) => {}
|
|
|
|
_ => return noop_fold_pat(p, fld)
|
2014-05-19 20:59:35 +00:00
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
p.map(|ast::Pat {node, span, ..}| {
|
|
|
|
let (pth, tts) = match node {
|
|
|
|
PatMac(mac) => match mac.node {
|
|
|
|
MacInvocTT(pth, tts, _) => {
|
|
|
|
(pth, tts)
|
|
|
|
}
|
|
|
|
},
|
|
|
|
_ => unreachable!()
|
|
|
|
};
|
2015-01-28 01:01:48 +00:00
|
|
|
if pth.segments.len() > 1 {
|
2014-09-13 16:06:01 +00:00
|
|
|
fld.cx.span_err(pth.span, "expected macro name without module separators");
|
|
|
|
return DummyResult::raw_pat(span);
|
2014-05-19 20:59:35 +00:00
|
|
|
}
|
2014-10-15 06:05:01 +00:00
|
|
|
let extname = pth.segments[0].identifier;
|
2014-09-13 16:06:01 +00:00
|
|
|
let extnamestr = token::get_ident(extname);
|
|
|
|
let marked_after = match fld.cx.syntax_env.find(&extname.name) {
|
|
|
|
None => {
|
|
|
|
fld.cx.span_err(pth.span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("macro undefined: '{}!'",
|
2015-02-20 19:08:14 +00:00
|
|
|
extnamestr));
|
2014-09-13 16:06:01 +00:00
|
|
|
// let compilation continue
|
|
|
|
return DummyResult::raw_pat(span);
|
|
|
|
}
|
2014-05-19 20:59:35 +00:00
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
Some(rc) => match *rc {
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
NormalTT(ref expander, tt_span, allow_internal_unstable) => {
|
2014-09-13 16:06:01 +00:00
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: span,
|
|
|
|
callee: NameAndSpan {
|
2015-02-03 22:31:06 +00:00
|
|
|
name: extnamestr.to_string(),
|
2014-09-13 16:06:01 +00:00
|
|
|
format: MacroBang,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
span: tt_span,
|
|
|
|
allow_internal_unstable: allow_internal_unstable,
|
2014-09-13 16:06:01 +00:00
|
|
|
}
|
|
|
|
});
|
2014-05-19 20:59:35 +00:00
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
let fm = fresh_mark();
|
2015-02-18 19:48:57 +00:00
|
|
|
let marked_before = mark_tts(&tts[..], fm);
|
2014-09-17 16:01:33 +00:00
|
|
|
let mac_span = fld.cx.original_span();
|
2014-09-13 16:06:01 +00:00
|
|
|
let expanded = match expander.expand(fld.cx,
|
2014-09-17 16:01:33 +00:00
|
|
|
mac_span,
|
2015-02-18 19:48:57 +00:00
|
|
|
&marked_before[..]).make_pat() {
|
2014-09-13 16:06:01 +00:00
|
|
|
Some(e) => e,
|
|
|
|
None => {
|
|
|
|
fld.cx.span_err(
|
|
|
|
pth.span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!(
|
2014-09-13 16:06:01 +00:00
|
|
|
"non-pattern macro in pattern position: {}",
|
2015-02-04 20:48:12 +00:00
|
|
|
&extnamestr
|
2015-02-20 19:08:14 +00:00
|
|
|
)
|
2014-09-13 16:06:01 +00:00
|
|
|
);
|
|
|
|
return DummyResult::raw_pat(span);
|
|
|
|
}
|
|
|
|
};
|
2014-07-19 19:34:24 +00:00
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
// mark after:
|
|
|
|
mark_pat(expanded,fm)
|
|
|
|
}
|
|
|
|
_ => {
|
|
|
|
fld.cx.span_err(span,
|
2015-01-07 16:58:31 +00:00
|
|
|
&format!("{}! is not legal in pattern position",
|
2015-02-20 19:08:14 +00:00
|
|
|
&extnamestr));
|
2014-09-13 16:06:01 +00:00
|
|
|
return DummyResult::raw_pat(span);
|
|
|
|
}
|
2014-07-19 19:34:24 +00:00
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
};
|
2014-05-19 20:59:35 +00:00
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
let fully_expanded =
|
|
|
|
fld.fold_pat(marked_after).node.clone();
|
|
|
|
fld.cx.bt_pop();
|
2014-05-19 20:59:35 +00:00
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
ast::Pat {
|
|
|
|
id: ast::DUMMY_NODE_ID,
|
|
|
|
node: fully_expanded,
|
|
|
|
span: span
|
|
|
|
}
|
|
|
|
})
|
2014-05-19 20:59:35 +00:00
|
|
|
}
|
|
|
|
|
2014-07-03 06:17:10 +00:00
|
|
|
/// A tree-folder that applies every rename in its (mutable) list
|
|
|
|
/// to every identifier, including both bindings and varrefs
|
|
|
|
/// (and lots of things that will turn out to be neither)
|
2014-03-02 23:26:39 +00:00
|
|
|
pub struct IdentRenamer<'a> {
|
2014-07-03 05:38:30 +00:00
|
|
|
renames: &'a mtwt::RenameList,
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-01-09 13:05:33 +00:00
|
|
|
impl<'a> Folder for IdentRenamer<'a> {
|
2014-02-24 20:47:19 +00:00
|
|
|
fn fold_ident(&mut self, id: Ident) -> Ident {
|
|
|
|
Ident {
|
2013-08-29 19:10:02 +00:00
|
|
|
name: id.name,
|
2014-07-03 21:38:59 +00:00
|
|
|
ctxt: mtwt::apply_renames(self.renames, id.ctxt),
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
}
|
2015-01-02 21:39:05 +00:00
|
|
|
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
|
|
|
fold::noop_fold_mac(mac, self)
|
2014-07-09 21:48:12 +00:00
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-07-03 06:16:01 +00:00
|
|
|
/// A tree-folder that applies every rename in its list to
|
|
|
|
/// the idents that are in PatIdent patterns. This is more narrowly
|
|
|
|
/// focused than IdentRenamer, and is needed for FnDecl,
|
|
|
|
/// where we want to rename the args but not the fn name or the generics etc.
|
|
|
|
pub struct PatIdentRenamer<'a> {
|
|
|
|
renames: &'a mtwt::RenameList,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a> Folder for PatIdentRenamer<'a> {
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
|
2014-07-03 06:16:01 +00:00
|
|
|
match pat.node {
|
2014-09-13 16:06:01 +00:00
|
|
|
ast::PatIdent(..) => {},
|
|
|
|
_ => return noop_fold_pat(pat, self)
|
|
|
|
}
|
|
|
|
|
|
|
|
pat.map(|ast::Pat {id, node, span}| match node {
|
|
|
|
ast::PatIdent(binding_mode, Spanned{span: sp, node: ident}, sub) => {
|
|
|
|
let new_ident = Ident{name: ident.name,
|
|
|
|
ctxt: mtwt::apply_renames(self.renames, ident.ctxt)};
|
2014-07-03 06:16:01 +00:00
|
|
|
let new_node =
|
|
|
|
ast::PatIdent(binding_mode,
|
2014-09-13 16:06:01 +00:00
|
|
|
Spanned{span: self.new_span(sp), node: new_ident},
|
2014-07-03 06:16:01 +00:00
|
|
|
sub.map(|p| self.fold_pat(p)));
|
2014-09-13 16:06:01 +00:00
|
|
|
ast::Pat {
|
|
|
|
id: id,
|
2014-07-03 06:16:01 +00:00
|
|
|
node: new_node,
|
2014-09-13 16:06:01 +00:00
|
|
|
span: self.new_span(span)
|
2014-07-03 06:16:01 +00:00
|
|
|
}
|
|
|
|
},
|
2014-09-13 16:06:01 +00:00
|
|
|
_ => unreachable!()
|
|
|
|
})
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
2015-01-02 21:39:05 +00:00
|
|
|
fn fold_mac(&mut self, mac: ast::Mac) -> ast::Mac {
|
|
|
|
fold::noop_fold_mac(mac, self)
|
2014-07-09 21:48:12 +00:00
|
|
|
}
|
2012-02-05 01:37:24 +00:00
|
|
|
}
|
|
|
|
|
2014-12-02 18:07:41 +00:00
|
|
|
fn expand_annotatable(a: Annotatable,
|
|
|
|
fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<Annotatable> {
|
|
|
|
let a = expand_item_multi_modifier(a, fld);
|
|
|
|
|
|
|
|
let mut decorator_items = SmallVector::zero();
|
|
|
|
let mut new_attrs = Vec::new();
|
2015-01-31 17:20:46 +00:00
|
|
|
for attr in a.attrs() {
|
2014-12-02 18:07:41 +00:00
|
|
|
let mname = attr.name();
|
|
|
|
|
2015-02-04 20:48:12 +00:00
|
|
|
match fld.cx.syntax_env.find(&intern(&mname)) {
|
2014-12-02 18:07:41 +00:00
|
|
|
Some(rc) => match *rc {
|
|
|
|
Decorator(ref dec) => {
|
|
|
|
let it = match a {
|
|
|
|
Annotatable::Item(ref it) => it,
|
|
|
|
// ItemDecorators are only implemented for Items.
|
|
|
|
_ => break,
|
|
|
|
};
|
|
|
|
|
|
|
|
attr::mark_used(attr);
|
|
|
|
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: attr.span,
|
|
|
|
callee: NameAndSpan {
|
2015-02-03 22:31:06 +00:00
|
|
|
name: mname.to_string(),
|
2014-12-02 18:07:41 +00:00
|
|
|
format: MacroAttribute,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
span: None,
|
|
|
|
// attributes can do whatever they like,
|
|
|
|
// for now.
|
|
|
|
allow_internal_unstable: true,
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
// we'd ideally decorator_items.push_all(expand_item(item, fld)),
|
|
|
|
// but that double-mut-borrows fld
|
|
|
|
let mut items: SmallVector<P<ast::Item>> = SmallVector::zero();
|
|
|
|
dec.expand(fld.cx, attr.span, &*attr.node.value, &**it,
|
2015-02-12 15:41:47 +00:00
|
|
|
&mut |item| items.push(item));
|
|
|
|
decorator_items.extend(
|
|
|
|
items.into_iter()
|
|
|
|
.flat_map(|item| expand_item(item, fld).into_iter()));
|
2014-12-02 18:07:41 +00:00
|
|
|
|
|
|
|
fld.cx.bt_pop();
|
|
|
|
}
|
|
|
|
_ => new_attrs.push((*attr).clone()),
|
|
|
|
},
|
|
|
|
_ => new_attrs.push((*attr).clone()),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
let mut new_items: SmallVector<Annotatable> = match a {
|
|
|
|
Annotatable::Item(it) => match it.node {
|
|
|
|
ast::ItemMac(..) => {
|
|
|
|
expand_item_mac(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
|
|
|
|
}
|
|
|
|
ast::ItemMod(_) | ast::ItemForeignMod(_) => {
|
|
|
|
let valid_ident =
|
|
|
|
it.ident.name != parse::token::special_idents::invalid.name;
|
|
|
|
|
|
|
|
if valid_ident {
|
|
|
|
fld.cx.mod_push(it.ident);
|
|
|
|
}
|
2015-02-18 19:48:57 +00:00
|
|
|
let macro_use = contains_macro_use(fld, &new_attrs[..]);
|
2014-12-02 18:07:41 +00:00
|
|
|
let result = with_exts_frame!(fld.cx.syntax_env,
|
|
|
|
macro_use,
|
|
|
|
noop_fold_item(it, fld));
|
|
|
|
if valid_ident {
|
|
|
|
fld.cx.mod_pop();
|
|
|
|
}
|
|
|
|
result.into_iter().map(|i| Annotatable::Item(i)).collect()
|
|
|
|
},
|
|
|
|
_ => {
|
|
|
|
let it = P(ast::Item {
|
|
|
|
attrs: new_attrs,
|
|
|
|
..(*it).clone()
|
|
|
|
});
|
|
|
|
noop_fold_item(it, fld).into_iter().map(|i| Annotatable::Item(i)).collect()
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Annotatable::TraitItem(it) => match it {
|
|
|
|
ast::TraitItem::ProvidedMethod(m) => {
|
|
|
|
expand_method(m, fld).into_iter().map(|m|
|
|
|
|
Annotatable::TraitItem(ast::TraitItem::ProvidedMethod(m))).collect()
|
|
|
|
}
|
|
|
|
ast::TraitItem::RequiredMethod(m) => {
|
|
|
|
SmallVector::one(Annotatable::TraitItem(
|
|
|
|
ast::TraitItem::RequiredMethod(fld.fold_type_method(m))))
|
|
|
|
}
|
|
|
|
ast::TraitItem::TypeTraitItem(t) => {
|
|
|
|
SmallVector::one(Annotatable::TraitItem(
|
|
|
|
ast::TraitItem::TypeTraitItem(P(fld.fold_associated_type((*t).clone())))))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
Annotatable::ImplItem(it) => match it {
|
|
|
|
ast::ImplItem::MethodImplItem(m) => {
|
|
|
|
expand_method(m, fld).into_iter().map(|m|
|
|
|
|
Annotatable::ImplItem(ast::ImplItem::MethodImplItem(m))).collect()
|
|
|
|
}
|
|
|
|
ast::ImplItem::TypeImplItem(t) => {
|
|
|
|
SmallVector::one(Annotatable::ImplItem(
|
|
|
|
ast::ImplItem::TypeImplItem(P(fld.fold_typedef((*t).clone())))))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
new_items.push_all(decorator_items.into_iter().map(|i| Annotatable::Item(i)).collect());
|
|
|
|
new_items
|
|
|
|
}
|
|
|
|
|
|
|
|
fn expand_trait_item(i: ast::TraitItem,
|
|
|
|
fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<ast::TraitItem> {
|
|
|
|
expand_annotatable(Annotatable::TraitItem(i), fld)
|
|
|
|
.into_iter().map(|i| i.expect_trait_item()).collect()
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
fn expand_impl_item(i: ast::ImplItem,
|
|
|
|
fld: &mut MacroExpander)
|
|
|
|
-> SmallVector<ast::ImplItem> {
|
|
|
|
expand_annotatable(Annotatable::ImplItem(i), fld)
|
|
|
|
.into_iter().map(|i| i.expect_impl_item()).collect()
|
|
|
|
}
|
|
|
|
|
|
|
|
// partition the attributes into ItemModifiers and others
|
|
|
|
fn modifiers(attrs: &Vec<ast::Attribute>,
|
|
|
|
fld: &MacroExpander)
|
|
|
|
-> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
|
|
|
|
attrs.iter().cloned().partition(|attr| {
|
2015-02-04 20:48:12 +00:00
|
|
|
match fld.cx.syntax_env.find(&intern(&attr.name())) {
|
2014-12-02 18:07:41 +00:00
|
|
|
Some(rc) => match *rc {
|
|
|
|
Modifier(_) => true,
|
|
|
|
_ => false
|
|
|
|
},
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// partition the attributes into MultiModifiers and others
|
|
|
|
fn multi_modifiers(attrs: &[ast::Attribute],
|
|
|
|
fld: &MacroExpander)
|
|
|
|
-> (Vec<ast::Attribute>, Vec<ast::Attribute>) {
|
|
|
|
attrs.iter().cloned().partition(|attr| {
|
2015-02-04 20:48:12 +00:00
|
|
|
match fld.cx.syntax_env.find(&intern(&attr.name())) {
|
2014-12-02 18:07:41 +00:00
|
|
|
Some(rc) => match *rc {
|
|
|
|
MultiModifier(_) => true,
|
|
|
|
_ => false
|
|
|
|
},
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
fn expand_item_multi_modifier(mut it: Annotatable,
|
|
|
|
fld: &mut MacroExpander)
|
|
|
|
-> Annotatable {
|
|
|
|
let (modifiers, other_attrs) = multi_modifiers(it.attrs(), fld);
|
|
|
|
|
|
|
|
// Update the attrs, leave everything else alone. Is this mutation really a good idea?
|
|
|
|
it = it.fold_attrs(other_attrs);
|
|
|
|
|
|
|
|
if modifiers.is_empty() {
|
|
|
|
return it
|
|
|
|
}
|
|
|
|
|
2015-01-31 17:20:46 +00:00
|
|
|
for attr in &modifiers {
|
2014-12-02 18:07:41 +00:00
|
|
|
let mname = attr.name();
|
|
|
|
|
2015-02-04 20:48:12 +00:00
|
|
|
match fld.cx.syntax_env.find(&intern(&mname)) {
|
2014-12-02 18:07:41 +00:00
|
|
|
Some(rc) => match *rc {
|
|
|
|
MultiModifier(ref mac) => {
|
|
|
|
attr::mark_used(attr);
|
|
|
|
fld.cx.bt_push(ExpnInfo {
|
|
|
|
call_site: attr.span,
|
|
|
|
callee: NameAndSpan {
|
2015-02-03 22:31:06 +00:00
|
|
|
name: mname.to_string(),
|
2014-12-02 18:07:41 +00:00
|
|
|
format: MacroAttribute,
|
|
|
|
span: None,
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
// attributes can do whatever they like,
|
|
|
|
// for now
|
|
|
|
allow_internal_unstable: true,
|
2014-12-02 18:07:41 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
it = mac.expand(fld.cx, attr.span, &*attr.node.value, it);
|
|
|
|
fld.cx.bt_pop();
|
|
|
|
}
|
|
|
|
_ => unreachable!()
|
|
|
|
},
|
|
|
|
_ => unreachable!()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Expansion may have added new ItemModifiers.
|
|
|
|
expand_item_multi_modifier(it, fld)
|
|
|
|
}
|
|
|
|
|
2014-07-04 18:24:28 +00:00
|
|
|
// expand a method
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_method(m: P<ast::Method>, fld: &mut MacroExpander) -> SmallVector<P<ast::Method>> {
|
|
|
|
m.and_then(|m| match m.node {
|
2014-05-29 05:26:56 +00:00
|
|
|
ast::MethDecl(ident,
|
2014-09-13 16:06:01 +00:00
|
|
|
generics,
|
2014-05-29 05:26:56 +00:00
|
|
|
abi,
|
2014-09-13 16:06:01 +00:00
|
|
|
explicit_self,
|
2014-05-29 05:26:56 +00:00
|
|
|
fn_style,
|
|
|
|
decl,
|
|
|
|
body,
|
|
|
|
vis) => {
|
2014-09-13 16:06:01 +00:00
|
|
|
let id = fld.new_id(m.id);
|
2014-07-13 16:35:48 +00:00
|
|
|
let (rewritten_fn_decl, rewritten_body)
|
2014-12-02 18:07:41 +00:00
|
|
|
= expand_and_rename_fn_decl_and_block(decl, body, fld);
|
2014-09-13 16:06:01 +00:00
|
|
|
SmallVector::one(P(ast::Method {
|
2015-02-09 17:01:15 +00:00
|
|
|
attrs: fold::fold_attrs(m.attrs, fld),
|
2014-07-13 16:35:48 +00:00
|
|
|
id: id,
|
|
|
|
span: fld.new_span(m.span),
|
|
|
|
node: ast::MethDecl(fld.fold_ident(ident),
|
2014-07-25 19:00:33 +00:00
|
|
|
noop_fold_generics(generics, fld),
|
2014-05-29 05:26:56 +00:00
|
|
|
abi,
|
2014-07-13 16:35:48 +00:00
|
|
|
fld.fold_explicit_self(explicit_self),
|
|
|
|
fn_style,
|
|
|
|
rewritten_fn_decl,
|
|
|
|
rewritten_body,
|
|
|
|
vis)
|
2014-09-13 16:06:01 +00:00
|
|
|
}))
|
2014-07-13 16:35:48 +00:00
|
|
|
},
|
2014-09-13 16:06:01 +00:00
|
|
|
ast::MethMac(mac) => {
|
2014-07-13 16:35:48 +00:00
|
|
|
let maybe_new_methods =
|
2014-09-13 16:06:01 +00:00
|
|
|
expand_mac_invoc(mac, m.span,
|
|
|
|
|r| r.make_methods(),
|
|
|
|
|meths, mark| meths.move_map(|m| mark_method(m, mark)),
|
2014-07-13 16:35:48 +00:00
|
|
|
fld);
|
|
|
|
|
2014-12-12 16:34:28 +00:00
|
|
|
match maybe_new_methods {
|
|
|
|
Some(methods) => {
|
|
|
|
// expand again if necessary
|
|
|
|
let new_methods = methods.into_iter()
|
|
|
|
.flat_map(|m| fld.fold_method(m).into_iter())
|
|
|
|
.collect();
|
|
|
|
fld.cx.bt_pop();
|
|
|
|
new_methods
|
|
|
|
}
|
2014-07-13 16:35:48 +00:00
|
|
|
None => SmallVector::zero()
|
2014-12-12 16:34:28 +00:00
|
|
|
}
|
2014-07-12 04:22:11 +00:00
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
})
|
2014-07-04 18:24:28 +00:00
|
|
|
}
|
|
|
|
|
2014-07-04 19:19:15 +00:00
|
|
|
/// Given a fn_decl and a block and a MacroExpander, expand the fn_decl, then use the
|
|
|
|
/// PatIdents in its arguments to perform renaming in the FnDecl and
|
|
|
|
/// the block, returning both the new FnDecl and the new Block.
|
2014-09-13 16:06:01 +00:00
|
|
|
fn expand_and_rename_fn_decl_and_block(fn_decl: P<ast::FnDecl>, block: P<ast::Block>,
|
2014-07-04 19:19:15 +00:00
|
|
|
fld: &mut MacroExpander)
|
2014-09-13 16:06:01 +00:00
|
|
|
-> (P<ast::FnDecl>, P<ast::Block>) {
|
2014-07-04 19:19:15 +00:00
|
|
|
let expanded_decl = fld.fold_fn_decl(fn_decl);
|
2014-07-07 23:35:15 +00:00
|
|
|
let idents = fn_decl_arg_bindings(&*expanded_decl);
|
2014-07-04 19:19:15 +00:00
|
|
|
let renames =
|
|
|
|
idents.iter().map(|id : &ast::Ident| (*id,fresh_name(id))).collect();
|
|
|
|
// first, a renamer for the PatIdents, for the fn_decl:
|
|
|
|
let mut rename_pat_fld = PatIdentRenamer{renames: &renames};
|
2014-09-13 16:06:01 +00:00
|
|
|
let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
|
2014-07-04 19:19:15 +00:00
|
|
|
// now, a renamer for *all* idents, for the body:
|
|
|
|
let mut rename_fld = IdentRenamer{renames: &renames};
|
|
|
|
let rewritten_body = fld.fold_block(rename_fld.fold_block(block));
|
|
|
|
(rewritten_fn_decl,rewritten_body)
|
|
|
|
}
|
|
|
|
|
2014-07-03 06:16:01 +00:00
|
|
|
/// A tree-folder that performs macro expansion
|
2014-08-28 01:46:52 +00:00
|
|
|
pub struct MacroExpander<'a, 'b:'a> {
|
2014-03-27 22:39:48 +00:00
|
|
|
pub cx: &'a mut ExtCtxt<'b>,
|
2014-12-14 02:42:41 +00:00
|
|
|
// The type of the impl currently being expanded.
|
|
|
|
current_impl_type: Option<P<ast::Ty>>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'b> MacroExpander<'a, 'b> {
|
|
|
|
pub fn new(cx: &'a mut ExtCtxt<'b>) -> MacroExpander<'a, 'b> {
|
|
|
|
MacroExpander { cx: cx, current_impl_type: None }
|
|
|
|
}
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-03-09 14:54:34 +00:00
|
|
|
impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_expr(&mut self, expr: P<ast::Expr>) -> P<ast::Expr> {
|
2013-12-28 00:43:01 +00:00
|
|
|
expand_expr(expr, self)
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_pat(&mut self, pat: P<ast::Pat>) -> P<ast::Pat> {
|
2014-05-19 20:59:35 +00:00
|
|
|
expand_pat(pat, self)
|
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_item(&mut self, item: P<ast::Item>) -> SmallVector<P<ast::Item>> {
|
2014-12-14 02:42:41 +00:00
|
|
|
let prev_type = self.current_impl_type.clone();
|
2014-12-28 22:33:18 +00:00
|
|
|
if let ast::Item_::ItemImpl(_, _, _, _, ref ty, _) = item.node {
|
2014-12-14 02:42:41 +00:00
|
|
|
self.current_impl_type = Some(ty.clone());
|
|
|
|
}
|
|
|
|
|
|
|
|
let result = expand_item(item, self);
|
|
|
|
self.current_impl_type = prev_type;
|
|
|
|
result
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_item_underscore(&mut self, item: ast::Item_) -> ast::Item_ {
|
2014-07-04 19:19:15 +00:00
|
|
|
expand_item_underscore(item, self)
|
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_stmt(&mut self, stmt: P<ast::Stmt>) -> SmallVector<P<ast::Stmt>> {
|
|
|
|
stmt.and_then(|stmt| expand_stmt(stmt, self))
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2013-12-28 03:34:51 +00:00
|
|
|
fn fold_block(&mut self, block: P<Block>) -> P<Block> {
|
2014-09-13 16:06:01 +00:00
|
|
|
expand_block(block, self)
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_arm(&mut self, arm: ast::Arm) -> ast::Arm {
|
2014-06-25 22:20:01 +00:00
|
|
|
expand_arm(arm, self)
|
|
|
|
}
|
|
|
|
|
2014-12-02 18:07:41 +00:00
|
|
|
fn fold_trait_item(&mut self, i: ast::TraitItem) -> SmallVector<ast::TraitItem> {
|
|
|
|
expand_trait_item(i, self)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn fold_impl_item(&mut self, i: ast::ImplItem) -> SmallVector<ast::ImplItem> {
|
|
|
|
expand_impl_item(i, self)
|
|
|
|
}
|
|
|
|
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_method(&mut self, method: P<ast::Method>) -> SmallVector<P<ast::Method>> {
|
|
|
|
expand_method(method, self)
|
2014-07-04 18:24:28 +00:00
|
|
|
}
|
|
|
|
|
2014-12-14 02:42:41 +00:00
|
|
|
fn fold_ty(&mut self, t: P<ast::Ty>) -> P<ast::Ty> {
|
|
|
|
let impl_type = self.current_impl_type.clone();
|
|
|
|
expand_type(t, self, impl_type)
|
|
|
|
}
|
|
|
|
|
2013-12-28 03:34:51 +00:00
|
|
|
fn new_span(&mut self, span: Span) -> Span {
|
2013-08-29 19:10:02 +00:00
|
|
|
new_span(self.cx, span)
|
|
|
|
}
|
2013-07-16 05:05:50 +00:00
|
|
|
}
|
|
|
|
|
2014-07-03 06:16:01 +00:00
|
|
|
fn new_span(cx: &ExtCtxt, sp: Span) -> Span {
|
|
|
|
/* this discards information in the case of macro-defining macros */
|
|
|
|
Span {
|
|
|
|
lo: sp.lo,
|
|
|
|
hi: sp.hi,
|
2014-09-17 16:01:33 +00:00
|
|
|
expn_id: cx.backtrace(),
|
2014-07-03 06:16:01 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
pub struct ExpansionConfig<'feat> {
|
2014-06-06 20:21:18 +00:00
|
|
|
pub crate_name: String,
|
2015-02-15 20:30:45 +00:00
|
|
|
pub features: Option<&'feat Features>,
|
2015-01-17 23:33:05 +00:00
|
|
|
pub recursion_limit: usize,
|
2014-09-27 00:14:23 +00:00
|
|
|
}
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
impl<'feat> ExpansionConfig<'feat> {
|
|
|
|
pub fn default(crate_name: String) -> ExpansionConfig<'static> {
|
2014-09-27 00:14:23 +00:00
|
|
|
ExpansionConfig {
|
|
|
|
crate_name: crate_name,
|
2015-02-15 20:30:45 +00:00
|
|
|
features: None,
|
2014-10-01 09:38:54 +00:00
|
|
|
recursion_limit: 64,
|
2014-09-27 00:14:23 +00:00
|
|
|
}
|
|
|
|
}
|
2015-02-15 20:30:45 +00:00
|
|
|
|
|
|
|
pub fn enable_quotes(&self) -> bool {
|
|
|
|
match self.features {
|
2015-02-15 21:14:03 +00:00
|
|
|
Some(&Features { allow_quote: true, .. }) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enable_asm(&self) -> bool {
|
|
|
|
match self.features {
|
|
|
|
Some(&Features { allow_asm: true, .. }) => true,
|
2015-02-15 20:30:45 +00:00
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
2015-02-15 22:49:55 +00:00
|
|
|
|
|
|
|
pub fn enable_log_syntax(&self) -> bool {
|
|
|
|
match self.features {
|
|
|
|
Some(&Features { allow_log_syntax: true, .. }) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enable_concat_idents(&self) -> bool {
|
|
|
|
match self.features {
|
|
|
|
Some(&Features { allow_concat_idents: true, .. }) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn enable_trace_macros(&self) -> bool {
|
|
|
|
match self.features {
|
|
|
|
Some(&Features { allow_trace_macros: true, .. }) => true,
|
|
|
|
_ => false,
|
|
|
|
}
|
|
|
|
}
|
Add #[allow_internal_unstable] to track stability for macros better.
Unstable items used in a macro expansion will now always trigger
stability warnings, *unless* the unstable items are directly inside a
macro marked with `#[allow_internal_unstable]`. IOW, the compiler warns
unless the span of the unstable item is a subspan of the definition of a
macro marked with that attribute.
E.g.
#[allow_internal_unstable]
macro_rules! foo {
($e: expr) => {{
$e;
unstable(); // no warning
only_called_by_foo!();
}}
}
macro_rules! only_called_by_foo {
() => { unstable() } // warning
}
foo!(unstable()) // warning
The unstable inside `foo` is fine, due to the attribute. But the
`unstable` inside `only_called_by_foo` is not, since that macro doesn't
have the attribute, and the `unstable` passed into `foo` is also not
fine since it isn't contained in the macro itself (that is, even though
it is only used directly in the macro).
In the process this makes the stability tracking much more precise,
e.g. previously `println!("{}", unstable())` got no warning, but now it
does. As such, this is a bug fix that may cause [breaking-change]s.
The attribute is definitely feature gated, since it explicitly allows
side-stepping the feature gating system.
2015-03-01 03:09:28 +00:00
|
|
|
|
|
|
|
pub fn enable_allow_internal_unstable(&self) -> bool {
|
|
|
|
match self.features {
|
|
|
|
Some(&Features { allow_internal_unstable: true, .. }) => true,
|
|
|
|
_ => false
|
|
|
|
}
|
|
|
|
}
|
2014-03-01 07:17:38 +00:00
|
|
|
}
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
pub fn expand_crate<'feat>(parse_sess: &parse::ParseSess,
|
|
|
|
cfg: ExpansionConfig<'feat>,
|
|
|
|
// these are the macros being imported to this crate:
|
|
|
|
imported_macros: Vec<ast::MacroDef>,
|
|
|
|
user_exts: Vec<NamedSyntaxExtension>,
|
|
|
|
c: Crate) -> Crate {
|
2014-03-01 07:17:38 +00:00
|
|
|
let mut cx = ExtCtxt::new(parse_sess, c.config.clone(), cfg);
|
2014-09-07 21:57:26 +00:00
|
|
|
cx.use_std = std_inject::use_std(&c);
|
|
|
|
|
2014-12-14 02:42:41 +00:00
|
|
|
let mut expander = MacroExpander::new(&mut cx);
|
2013-08-29 19:10:02 +00:00
|
|
|
|
2015-02-01 01:03:04 +00:00
|
|
|
for def in imported_macros {
|
2015-01-02 00:37:47 +00:00
|
|
|
expander.cx.insert_macro(def);
|
2014-05-24 23:16:10 +00:00
|
|
|
}
|
|
|
|
|
2015-02-01 01:03:04 +00:00
|
|
|
for (name, extension) in user_exts {
|
2014-07-19 19:34:24 +00:00
|
|
|
expander.cx.syntax_env.insert(name, extension);
|
2014-05-24 23:16:10 +00:00
|
|
|
}
|
|
|
|
|
2014-07-10 22:41:11 +00:00
|
|
|
let mut ret = expander.fold_crate(c);
|
|
|
|
ret.exported_macros = expander.cx.exported_macros.clone();
|
2013-07-29 08:12:41 +00:00
|
|
|
parse_sess.span_diagnostic.handler().abort_if_errors();
|
|
|
|
return ret;
|
2011-07-06 22:22:23 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
|
2013-07-13 17:18:37 +00:00
|
|
|
// HYGIENIC CONTEXT EXTENSION:
|
|
|
|
// all of these functions are for walking over
|
|
|
|
// ASTs and making some change to the context of every
|
|
|
|
// element that has one. a CtxtFn is a trait-ified
|
|
|
|
// version of a closure in (SyntaxContext -> SyntaxContext).
|
|
|
|
// the ones defined here include:
|
|
|
|
// Marker - add a mark to a context
|
|
|
|
|
2014-02-06 22:38:33 +00:00
|
|
|
// A Marker adds the given mark to the syntax context
|
|
|
|
struct Marker { mark: Mrk }
|
2013-07-09 22:56:21 +00:00
|
|
|
|
2014-02-06 22:38:33 +00:00
|
|
|
impl Folder for Marker {
|
2014-02-24 20:47:19 +00:00
|
|
|
fn fold_ident(&mut self, id: Ident) -> Ident {
|
2013-08-29 19:10:02 +00:00
|
|
|
ast::Ident {
|
2014-02-06 22:38:33 +00:00
|
|
|
name: id.name,
|
2014-07-03 21:38:59 +00:00
|
|
|
ctxt: mtwt::apply_mark(self.mark, id.ctxt)
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
|
|
|
}
|
2014-09-13 16:06:01 +00:00
|
|
|
fn fold_mac(&mut self, Spanned {node, span}: ast::Mac) -> ast::Mac {
|
2013-08-29 19:10:02 +00:00
|
|
|
Spanned {
|
2014-09-13 16:06:01 +00:00
|
|
|
node: match node {
|
|
|
|
MacInvocTT(path, tts, ctxt) => {
|
|
|
|
MacInvocTT(self.fold_path(path),
|
2015-02-18 19:48:57 +00:00
|
|
|
self.fold_tts(&tts[..]),
|
2014-09-13 16:06:01 +00:00
|
|
|
mtwt::apply_mark(self.mark, ctxt))
|
|
|
|
}
|
|
|
|
},
|
|
|
|
span: span,
|
2013-08-29 19:10:02 +00:00
|
|
|
}
|
2013-07-09 22:56:21 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// apply a given mark to the given token trees. Used prior to expansion of a macro.
|
2014-02-28 21:09:09 +00:00
|
|
|
fn mark_tts(tts: &[TokenTree], m: Mrk) -> Vec<TokenTree> {
|
2014-07-25 19:00:33 +00:00
|
|
|
noop_fold_tts(tts, &mut Marker{mark:m})
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// apply a given mark to the given expr. Used following the expansion of a macro.
|
2014-09-13 16:06:01 +00:00
|
|
|
fn mark_expr(expr: P<ast::Expr>, m: Mrk) -> P<ast::Expr> {
|
2014-06-26 22:37:18 +00:00
|
|
|
Marker{mark:m}.fold_expr(expr)
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
2014-05-19 20:59:35 +00:00
|
|
|
// apply a given mark to the given pattern. Used following the expansion of a macro.
|
2014-09-13 16:06:01 +00:00
|
|
|
fn mark_pat(pat: P<ast::Pat>, m: Mrk) -> P<ast::Pat> {
|
2014-06-26 22:37:18 +00:00
|
|
|
Marker{mark:m}.fold_pat(pat)
|
2014-05-19 20:59:35 +00:00
|
|
|
}
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// apply a given mark to the given stmt. Used following the expansion of a macro.
|
2014-09-13 16:06:01 +00:00
|
|
|
fn mark_stmt(expr: P<ast::Stmt>, m: Mrk) -> P<ast::Stmt> {
|
2014-06-26 22:37:18 +00:00
|
|
|
Marker{mark:m}.fold_stmt(expr)
|
2014-07-13 16:35:48 +00:00
|
|
|
.expect_one("marking a stmt didn't return exactly one stmt")
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// apply a given mark to the given item. Used following the expansion of a macro.
|
2014-09-13 16:06:01 +00:00
|
|
|
fn mark_item(expr: P<ast::Item>, m: Mrk) -> P<ast::Item> {
|
2014-06-26 22:37:18 +00:00
|
|
|
Marker{mark:m}.fold_item(expr)
|
2014-07-13 16:35:48 +00:00
|
|
|
.expect_one("marking an item didn't return exactly one item")
|
|
|
|
}
|
|
|
|
|
|
|
|
// apply a given mark to the given item. Used following the expansion of a macro.
|
2014-09-13 16:06:01 +00:00
|
|
|
fn mark_method(expr: P<ast::Method>, m: Mrk) -> P<ast::Method> {
|
2014-07-13 16:35:48 +00:00
|
|
|
Marker{mark:m}.fold_method(expr)
|
|
|
|
.expect_one("marking an item didn't return exactly one method")
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
|
2014-07-09 23:41:13 +00:00
|
|
|
/// Check that there are no macro invocations left in the AST:
|
|
|
|
pub fn check_for_macros(sess: &parse::ParseSess, krate: &ast::Crate) {
|
2014-09-12 10:10:30 +00:00
|
|
|
visit::walk_crate(&mut MacroExterminator{sess:sess}, krate);
|
2014-07-09 23:41:13 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/// A visitor that ensures that no macro invocations remain in an AST.
|
|
|
|
struct MacroExterminator<'a>{
|
|
|
|
sess: &'a parse::ParseSess
|
|
|
|
}
|
|
|
|
|
2014-09-09 22:54:36 +00:00
|
|
|
impl<'a, 'v> Visitor<'v> for MacroExterminator<'a> {
|
2015-01-02 21:39:05 +00:00
|
|
|
fn visit_mac(&mut self, mac: &ast::Mac) {
|
|
|
|
self.sess.span_diagnostic.span_bug(mac.span,
|
2014-07-09 23:41:13 +00:00
|
|
|
"macro exterminator: expected AST \
|
|
|
|
with no macro invocations");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
#[cfg(test)]
|
|
|
|
mod test {
|
2015-01-08 02:53:58 +00:00
|
|
|
use super::{pattern_bindings, expand_crate};
|
2014-09-27 00:14:23 +00:00
|
|
|
use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
|
2013-02-25 19:11:21 +00:00
|
|
|
use ast;
|
2015-01-27 03:56:50 +00:00
|
|
|
use ast::Name;
|
2013-02-25 19:11:21 +00:00
|
|
|
use codemap;
|
2014-02-24 20:47:19 +00:00
|
|
|
use ext::mtwt;
|
2014-07-04 19:18:37 +00:00
|
|
|
use fold::Folder;
|
2013-02-25 19:11:21 +00:00
|
|
|
use parse;
|
2013-07-14 19:25:04 +00:00
|
|
|
use parse::token;
|
2014-03-09 14:54:34 +00:00
|
|
|
use util::parser_testing::{string_to_parser};
|
2013-11-12 01:35:25 +00:00
|
|
|
use util::parser_testing::{string_to_pat, string_to_crate, strs_to_idents};
|
2013-06-29 12:59:08 +00:00
|
|
|
use visit;
|
2013-10-28 08:20:26 +00:00
|
|
|
use visit::Visitor;
|
|
|
|
|
|
|
|
// a visitor that extracts the paths
|
|
|
|
// from a given thingy and puts them in a mutable
|
|
|
|
// array (passed in to the traversal)
|
2015-01-04 03:54:18 +00:00
|
|
|
#[derive(Clone)]
|
2014-06-25 00:03:49 +00:00
|
|
|
struct PathExprFinderContext {
|
2014-02-28 21:09:09 +00:00
|
|
|
path_accumulator: Vec<ast::Path> ,
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
|
|
|
|
2014-09-09 22:54:36 +00:00
|
|
|
impl<'v> Visitor<'v> for PathExprFinderContext {
|
2014-09-12 10:10:30 +00:00
|
|
|
fn visit_expr(&mut self, expr: &ast::Expr) {
|
2015-02-17 17:29:13 +00:00
|
|
|
if let ast::ExprPath(None, ref p) = expr.node {
|
|
|
|
self.path_accumulator.push(p.clone());
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
2015-02-17 17:29:13 +00:00
|
|
|
visit::walk_expr(self, expr);
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-06-26 21:39:54 +00:00
|
|
|
// find the variable references in a crate
|
|
|
|
fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
|
|
|
|
let mut path_finder = PathExprFinderContext{path_accumulator:Vec::new()};
|
2014-09-12 10:10:30 +00:00
|
|
|
visit::walk_crate(&mut path_finder, the_crate);
|
2014-06-26 21:39:54 +00:00
|
|
|
path_finder.path_accumulator
|
2013-10-28 08:20:26 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
|
2014-07-04 19:18:37 +00:00
|
|
|
/// A Visitor that extracts the identifiers from a thingy.
|
|
|
|
// as a side note, I'm starting to want to abstract over these....
|
2014-09-12 10:10:30 +00:00
|
|
|
struct IdentFinder {
|
2014-07-04 19:18:37 +00:00
|
|
|
ident_accumulator: Vec<ast::Ident>
|
|
|
|
}
|
2014-06-26 21:39:54 +00:00
|
|
|
|
2014-09-09 22:54:36 +00:00
|
|
|
impl<'v> Visitor<'v> for IdentFinder {
|
2014-09-12 10:10:30 +00:00
|
|
|
fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident){
|
2014-07-04 19:18:37 +00:00
|
|
|
self.ident_accumulator.push(id);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Find the idents in a crate
|
|
|
|
fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
|
|
|
|
let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
|
2014-09-12 10:10:30 +00:00
|
|
|
visit::walk_crate(&mut ident_finder, the_crate);
|
2014-07-04 19:18:37 +00:00
|
|
|
ident_finder.ident_accumulator
|
|
|
|
}
|
2014-06-26 21:39:54 +00:00
|
|
|
|
2013-02-26 18:15:29 +00:00
|
|
|
// these following tests are quite fragile, in that they don't test what
|
|
|
|
// *kind* of failure occurs.
|
|
|
|
|
2015-02-15 20:30:45 +00:00
|
|
|
fn test_ecfg() -> ExpansionConfig<'static> {
|
2014-09-27 00:14:23 +00:00
|
|
|
ExpansionConfig::default("test".to_string())
|
|
|
|
}
|
|
|
|
|
2014-06-24 01:45:51 +00:00
|
|
|
// make sure that macros can't escape fns
|
2013-02-26 18:15:29 +00:00
|
|
|
#[should_fail]
|
|
|
|
#[test] fn macros_cant_escape_fns_test () {
|
2014-11-14 17:18:10 +00:00
|
|
|
let src = "fn bogus() {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2014-02-06 22:38:33 +00:00
|
|
|
let sess = parse::new_parse_sess();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2014-03-17 07:55:41 +00:00
|
|
|
Vec::new(), &sess);
|
2013-02-26 18:15:29 +00:00
|
|
|
// should fail:
|
2014-09-27 00:14:23 +00:00
|
|
|
expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-06-24 01:45:51 +00:00
|
|
|
// make sure that macros can't escape modules
|
2013-02-26 18:15:29 +00:00
|
|
|
#[should_fail]
|
|
|
|
#[test] fn macros_cant_escape_mods_test () {
|
2014-11-14 17:18:10 +00:00
|
|
|
let src = "mod foo {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2014-02-06 22:38:33 +00:00
|
|
|
let sess = parse::new_parse_sess();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2014-03-17 07:55:41 +00:00
|
|
|
Vec::new(), &sess);
|
2014-09-27 00:14:23 +00:00
|
|
|
expand_crate(&sess,test_ecfg(),vec!(),vec!(),crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-12-19 04:48:26 +00:00
|
|
|
// macro_use modules should allow macros to escape
|
2013-02-26 18:15:29 +00:00
|
|
|
#[test] fn macros_can_escape_flattened_mods_test () {
|
2014-12-19 04:48:26 +00:00
|
|
|
let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
|
2015-01-17 23:55:21 +00:00
|
|
|
fn inty() -> i32 { z!() }".to_string();
|
2014-02-06 22:38:33 +00:00
|
|
|
let sess = parse::new_parse_sess();
|
2013-02-26 18:15:29 +00:00
|
|
|
let crate_ast = parse::parse_crate_from_source_str(
|
2014-05-25 10:17:19 +00:00
|
|
|
"<test>".to_string(),
|
2013-06-12 17:02:55 +00:00
|
|
|
src,
|
2014-03-17 07:55:41 +00:00
|
|
|
Vec::new(), &sess);
|
2014-09-27 00:14:23 +00:00
|
|
|
expand_crate(&sess, test_ecfg(), vec!(), vec!(), crate_ast);
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|
|
|
|
|
2014-05-22 23:57:53 +00:00
|
|
|
fn expand_crate_str(crate_str: String) -> ast::Crate {
|
2014-03-09 14:54:34 +00:00
|
|
|
let ps = parse::new_parse_sess();
|
2014-03-17 07:55:41 +00:00
|
|
|
let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
|
2013-06-25 18:40:51 +00:00
|
|
|
// the cfg argument actually does matter, here...
|
2014-09-27 00:14:23 +00:00
|
|
|
expand_crate(&ps,test_ecfg(),vec!(),vec!(),crate_ast)
|
2013-07-09 23:00:41 +00:00
|
|
|
}
|
|
|
|
|
2014-06-26 21:39:54 +00:00
|
|
|
// find the pat_ident paths in a crate
|
|
|
|
fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
|
2014-07-04 19:05:43 +00:00
|
|
|
let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
|
2014-09-12 10:10:30 +00:00
|
|
|
visit::walk_crate(&mut name_finder, the_crate);
|
2014-06-26 21:39:54 +00:00
|
|
|
name_finder.ident_accumulator
|
|
|
|
}
|
|
|
|
|
2013-07-09 22:10:16 +00:00
|
|
|
#[test] fn macro_tokens_should_match(){
|
2014-05-07 23:33:43 +00:00
|
|
|
expand_crate_str(
|
2014-11-14 17:18:10 +00:00
|
|
|
"macro_rules! m((a)=>(13)) ;fn main(){m!(a);}".to_string());
|
2013-07-09 22:10:16 +00:00
|
|
|
}
|
|
|
|
|
2014-07-08 00:43:09 +00:00
|
|
|
// should be able to use a bound identifier as a literal in a macro definition:
|
|
|
|
#[test] fn self_macro_parsing(){
|
|
|
|
expand_crate_str(
|
2015-01-17 23:55:21 +00:00
|
|
|
"macro_rules! foo ((zz) => (287;));
|
|
|
|
fn f(zz: i32) {foo!(zz);}".to_string()
|
2014-07-08 00:43:09 +00:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// renaming tests expand a crate and then check that the bindings match
|
|
|
|
// the right varrefs. The specification of the test case includes the
|
|
|
|
// text of the crate, and also an array of arrays. Each element in the
|
|
|
|
// outer array corresponds to a binding in the traversal of the AST
|
|
|
|
// induced by visit. Each of these arrays contains a list of indexes,
|
|
|
|
// interpreted as the varrefs in the varref traversal that this binding
|
|
|
|
// should match. So, for instance, in a program with two bindings and
|
|
|
|
// three varrefs, the array ~[~[1,2],~[0]] would indicate that the first
|
|
|
|
// binding should match the second two varrefs, and the second binding
|
|
|
|
// should match the first varref.
|
|
|
|
//
|
2014-06-24 01:45:51 +00:00
|
|
|
// Put differently; this is a sparse representation of a boolean matrix
|
|
|
|
// indicating which bindings capture which identifiers.
|
|
|
|
//
|
|
|
|
// Note also that this matrix is dependent on the implicit ordering of
|
|
|
|
// the bindings and the varrefs discovered by the name-finder and the path-finder.
|
|
|
|
//
|
2013-06-29 12:59:08 +00:00
|
|
|
// The comparisons are done post-mtwt-resolve, so we're comparing renamed
|
|
|
|
// names; differences in marks don't matter any more.
|
2013-07-13 01:35:47 +00:00
|
|
|
//
|
2013-11-12 01:35:25 +00:00
|
|
|
// oog... I also want tests that check "bound-identifier-=?". That is,
|
2013-07-13 01:35:47 +00:00
|
|
|
// not just "do these have the same name", but "do they have the same
|
|
|
|
// name *and* the same marks"? Understanding this is really pretty painful.
|
|
|
|
// in principle, you might want to control this boolean on a per-varref basis,
|
|
|
|
// but that would make things even harder to understand, and might not be
|
|
|
|
// necessary for thorough testing.
|
2015-01-17 23:33:05 +00:00
|
|
|
type RenamingTest = (&'static str, Vec<Vec<usize>>, bool);
|
2013-06-29 12:59:08 +00:00
|
|
|
|
2013-06-25 18:40:51 +00:00
|
|
|
#[test]
|
|
|
|
fn automatic_renaming () {
|
2014-02-28 21:09:09 +00:00
|
|
|
let tests: Vec<RenamingTest> =
|
|
|
|
vec!(// b & c should get new names throughout, in the expr too:
|
2015-01-17 23:55:21 +00:00
|
|
|
("fn a() -> i32 { let b = 13; let c = b; b+c }",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(0,1),vec!(2)), false),
|
2013-06-07 22:01:28 +00:00
|
|
|
// both x's should be renamed (how is this causing a bug?)
|
2015-01-17 23:55:21 +00:00
|
|
|
("fn main () {let x: i32 = 13;x;}",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(0)), false),
|
2013-06-29 12:59:08 +00:00
|
|
|
// the use of b after the + should be renamed, the other one not:
|
2015-01-17 23:55:21 +00:00
|
|
|
("macro_rules! f (($x:ident) => (b + $x)); fn a() -> i32 { let b = 13; f!(b)}",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(1)), false),
|
2013-06-06 18:21:02 +00:00
|
|
|
// the b before the plus should not be renamed (requires marks)
|
2015-01-17 23:55:21 +00:00
|
|
|
("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> i32 { f!(b)}",
|
2014-02-28 21:09:09 +00:00
|
|
|
vec!(vec!(1)), false),
|
2013-06-29 12:59:08 +00:00
|
|
|
// the marks going in and out of letty should cancel, allowing that $x to
|
|
|
|
// capture the one following the semicolon.
|
|
|
|
// this was an awesome test case, and caught a *lot* of bugs.
|
2014-11-14 17:18:10 +00:00
|
|
|
("macro_rules! letty(($x:ident) => (let $x = 15;));
|
|
|
|
macro_rules! user(($x:ident) => ({letty!($x); $x}));
|
2015-01-17 23:55:21 +00:00
|
|
|
fn main() -> i32 {user!(z)}",
|
2013-11-12 01:35:25 +00:00
|
|
|
vec!(vec!(0)), false)
|
|
|
|
);
|
2013-09-05 20:28:57 +00:00
|
|
|
for (idx,s) in tests.iter().enumerate() {
|
|
|
|
run_renaming_test(s,idx);
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-11-12 01:35:25 +00:00
|
|
|
// no longer a fixme #8062: this test exposes a *potential* bug; our system does
|
|
|
|
// not behave exactly like MTWT, but a conversation with Matthew Flatt
|
|
|
|
// suggests that this can only occur in the presence of local-expand, which
|
|
|
|
// we have no plans to support. ... unless it's needed for item hygiene....
|
|
|
|
#[ignore]
|
|
|
|
#[test] fn issue_8062(){
|
|
|
|
run_renaming_test(
|
|
|
|
&("fn main() {let hrcoo = 19; macro_rules! getx(()=>(hrcoo)); getx!();}",
|
|
|
|
vec!(vec!(0)), true), 0)
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME #6994:
|
|
|
|
// the z flows into and out of two macros (g & f) along one path, and one
|
|
|
|
// (just g) along the other, so the result of the whole thing should
|
|
|
|
// be "let z_123 = 3; z_123"
|
|
|
|
#[ignore]
|
|
|
|
#[test] fn issue_6994(){
|
|
|
|
run_renaming_test(
|
|
|
|
&("macro_rules! g (($x:ident) =>
|
2014-11-14 17:18:10 +00:00
|
|
|
({macro_rules! f(($y:ident)=>({let $y=3;$x}));f!($x)}));
|
2013-11-12 01:35:25 +00:00
|
|
|
fn a(){g!(z)}",
|
|
|
|
vec!(vec!(0)),false),
|
|
|
|
0)
|
2014-06-24 23:34:03 +00:00
|
|
|
}
|
|
|
|
|
2014-06-27 23:10:23 +00:00
|
|
|
// match variable hygiene. Should expand into
|
2014-06-26 22:37:45 +00:00
|
|
|
// fn z() {match 8 {x_1 => {match 9 {x_2 | x_2 if x_2 == x_1 => x_2 + x_1}}}}
|
2014-06-24 23:34:03 +00:00
|
|
|
#[test] fn issue_9384(){
|
|
|
|
run_renaming_test(
|
2014-11-14 17:18:10 +00:00
|
|
|
&("macro_rules! bad_macro (($ex:expr) => ({match 9 {x | x if x == $ex => x + $ex}}));
|
2014-06-26 22:37:45 +00:00
|
|
|
fn z() {match 8 {x => bad_macro!(x)}}",
|
2014-06-24 23:34:03 +00:00
|
|
|
// NB: the third "binding" is the repeat of the second one.
|
2014-06-26 22:37:45 +00:00
|
|
|
vec!(vec!(1,3),vec!(0,2),vec!(0,2)),
|
2014-06-24 23:34:03 +00:00
|
|
|
true),
|
|
|
|
0)
|
2013-11-12 01:35:25 +00:00
|
|
|
}
|
|
|
|
|
2014-06-27 23:10:23 +00:00
|
|
|
// interpolated nodes weren't getting labeled.
|
2014-06-27 20:10:47 +00:00
|
|
|
// should expand into
|
|
|
|
// fn main(){let g1_1 = 13; g1_1}}
|
|
|
|
#[test] fn pat_expand_issue_15221(){
|
|
|
|
run_renaming_test(
|
2014-11-14 17:18:10 +00:00
|
|
|
&("macro_rules! inner ( ($e:pat ) => ($e));
|
|
|
|
macro_rules! outer ( ($e:pat ) => (inner!($e)));
|
2014-06-27 20:10:47 +00:00
|
|
|
fn main() { let outer!(g) = 13; g;}",
|
|
|
|
vec!(vec!(0)),
|
|
|
|
true),
|
|
|
|
0)
|
|
|
|
}
|
|
|
|
|
2013-11-12 01:35:25 +00:00
|
|
|
// create a really evil test case where a $x appears inside a binding of $x
|
2014-07-03 01:27:07 +00:00
|
|
|
// but *shouldn't* bind because it was inserted by a different macro....
|
2013-11-12 01:35:25 +00:00
|
|
|
// can't write this test case until we have macro-generating macros.
|
|
|
|
|
2014-07-04 17:57:17 +00:00
|
|
|
// method arg hygiene
|
2015-01-17 23:55:21 +00:00
|
|
|
// method expands to fn get_x(&self_0, x_1: i32) {self_0 + self_2 + x_3 + x_1}
|
2014-07-04 17:57:17 +00:00
|
|
|
#[test] fn method_arg_hygiene(){
|
|
|
|
run_renaming_test(
|
2014-11-14 17:18:10 +00:00
|
|
|
&("macro_rules! inject_x (()=>(x));
|
|
|
|
macro_rules! inject_self (()=>(self));
|
2014-07-04 17:57:17 +00:00
|
|
|
struct A;
|
2015-01-17 23:55:21 +00:00
|
|
|
impl A{fn get_x(&self, x: i32) {self + inject_self!() + inject_x!() + x;} }",
|
2014-07-04 17:57:17 +00:00
|
|
|
vec!(vec!(0),vec!(3)),
|
|
|
|
true),
|
|
|
|
0)
|
|
|
|
}
|
|
|
|
|
2014-07-07 16:53:41 +00:00
|
|
|
// ooh, got another bite?
|
|
|
|
// expands to struct A; impl A {fn thingy(&self_1) {self_1;}}
|
|
|
|
#[test] fn method_arg_hygiene_2(){
|
|
|
|
run_renaming_test(
|
|
|
|
&("struct A;
|
|
|
|
macro_rules! add_method (($T:ty) =>
|
2014-11-14 17:18:10 +00:00
|
|
|
(impl $T { fn thingy(&self) {self;} }));
|
|
|
|
add_method!(A);",
|
2014-07-07 16:53:41 +00:00
|
|
|
vec!(vec!(0)),
|
|
|
|
true),
|
|
|
|
0)
|
|
|
|
}
|
|
|
|
|
2014-07-04 17:57:28 +00:00
|
|
|
// item fn hygiene
|
2015-01-17 23:55:21 +00:00
|
|
|
// expands to fn q(x_1: i32){fn g(x_2: i32){x_2 + x_1};}
|
2014-07-04 17:57:28 +00:00
|
|
|
#[test] fn issue_9383(){
|
2013-11-12 01:35:25 +00:00
|
|
|
run_renaming_test(
|
2015-01-17 23:55:21 +00:00
|
|
|
&("macro_rules! bad_macro (($ex:expr) => (fn g(x: i32){ x + $ex }));
|
|
|
|
fn q(x: i32) { bad_macro!(x); }",
|
2014-06-30 19:42:33 +00:00
|
|
|
vec!(vec!(1),vec!(0)),true),
|
2013-11-12 01:35:25 +00:00
|
|
|
0)
|
2014-06-30 19:42:33 +00:00
|
|
|
}
|
2013-11-12 01:35:25 +00:00
|
|
|
|
2014-11-19 16:18:17 +00:00
|
|
|
// closure arg hygiene (ExprClosure)
|
2015-01-17 23:55:21 +00:00
|
|
|
// expands to fn f(){(|x_1 : i32| {(x_2 + x_1)})(3);}
|
2014-07-04 18:02:17 +00:00
|
|
|
#[test] fn closure_arg_hygiene(){
|
|
|
|
run_renaming_test(
|
2014-11-14 17:18:10 +00:00
|
|
|
&("macro_rules! inject_x (()=>(x));
|
2015-01-17 23:55:21 +00:00
|
|
|
fn f(){(|x : i32| {(inject_x!() + x)})(3);}",
|
2014-07-04 18:02:17 +00:00
|
|
|
vec!(vec!(1)),
|
|
|
|
true),
|
|
|
|
0)
|
|
|
|
}
|
|
|
|
|
2014-07-06 22:10:57 +00:00
|
|
|
// macro_rules in method position. Sadly, unimplemented.
|
2014-07-12 04:17:17 +00:00
|
|
|
#[test] fn macro_in_method_posn(){
|
2014-07-07 22:12:31 +00:00
|
|
|
expand_crate_str(
|
2015-01-17 23:55:21 +00:00
|
|
|
"macro_rules! my_method (() => (fn thirteen(&self) -> i32 {13}));
|
2014-07-07 22:12:31 +00:00
|
|
|
struct A;
|
2014-11-14 17:18:10 +00:00
|
|
|
impl A{ my_method!(); }
|
2014-07-07 22:12:31 +00:00
|
|
|
fn f(){A.thirteen;}".to_string());
|
|
|
|
}
|
|
|
|
|
2014-07-06 22:10:57 +00:00
|
|
|
// another nested macro
|
|
|
|
// expands to impl Entries {fn size_hint(&self_1) {self_1;}
|
|
|
|
#[test] fn item_macro_workaround(){
|
|
|
|
run_renaming_test(
|
|
|
|
&("macro_rules! item { ($i:item) => {$i}}
|
|
|
|
struct Entries;
|
|
|
|
macro_rules! iterator_impl {
|
2014-11-14 17:18:10 +00:00
|
|
|
() => { item!( impl Entries { fn size_hint(&self) { self;}});}}
|
2014-07-06 22:10:57 +00:00
|
|
|
iterator_impl! { }",
|
|
|
|
vec!(vec!(0)), true),
|
|
|
|
0)
|
|
|
|
}
|
|
|
|
|
2013-07-13 01:35:47 +00:00
|
|
|
// run one of the renaming tests
|
2015-01-17 23:33:05 +00:00
|
|
|
fn run_renaming_test(t: &RenamingTest, test_idx: usize) {
|
2013-09-05 16:29:31 +00:00
|
|
|
let invalid_name = token::special_idents::invalid.name;
|
2013-07-13 01:35:47 +00:00
|
|
|
let (teststr, bound_connections, bound_ident_check) = match *t {
|
2014-07-16 20:37:28 +00:00
|
|
|
(ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic)
|
2013-06-29 12:59:08 +00:00
|
|
|
};
|
2014-05-25 10:17:19 +00:00
|
|
|
let cr = expand_crate_str(teststr.to_string());
|
2014-06-26 21:39:54 +00:00
|
|
|
let bindings = crate_bindings(&cr);
|
|
|
|
let varrefs = crate_varrefs(&cr);
|
2013-10-28 08:20:26 +00:00
|
|
|
|
2013-06-29 12:59:08 +00:00
|
|
|
// must be one check clause for each binding:
|
|
|
|
assert_eq!(bindings.len(),bound_connections.len());
|
|
|
|
for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
|
2014-10-15 06:05:01 +00:00
|
|
|
let binding_name = mtwt::resolve(bindings[binding_idx]);
|
|
|
|
let binding_marks = mtwt::marksof(bindings[binding_idx].ctxt, invalid_name);
|
2013-06-29 12:59:08 +00:00
|
|
|
// shouldmatch can't name varrefs that don't exist:
|
|
|
|
assert!((shouldmatch.len() == 0) ||
|
|
|
|
(varrefs.len() > *shouldmatch.iter().max().unwrap()));
|
|
|
|
for (idx,varref) in varrefs.iter().enumerate() {
|
2015-02-01 17:44:15 +00:00
|
|
|
let print_hygiene_debug_info = || {
|
2014-07-06 22:06:32 +00:00
|
|
|
// good lord, you can't make a path with 0 segments, can you?
|
|
|
|
let final_varref_ident = match varref.segments.last() {
|
|
|
|
Some(pathsegment) => pathsegment.identifier,
|
2014-10-09 19:17:22 +00:00
|
|
|
None => panic!("varref with 0 path segments?")
|
2014-07-06 22:06:32 +00:00
|
|
|
};
|
|
|
|
let varref_name = mtwt::resolve(final_varref_ident);
|
|
|
|
let varref_idents : Vec<ast::Ident>
|
|
|
|
= varref.segments.iter().map(|s| s.identifier)
|
|
|
|
.collect();
|
2015-01-08 02:53:58 +00:00
|
|
|
println!("varref #{}: {:?}, resolves to {}",idx, varref_idents, varref_name);
|
2014-07-06 22:06:32 +00:00
|
|
|
let string = token::get_ident(final_varref_ident);
|
2015-02-18 19:48:57 +00:00
|
|
|
println!("varref's first segment's string: \"{}\"", &string[..]);
|
2014-07-06 22:06:32 +00:00
|
|
|
println!("binding #{}: {}, resolves to {}",
|
2014-10-15 06:05:01 +00:00
|
|
|
binding_idx, bindings[binding_idx], binding_name);
|
2014-07-06 22:06:32 +00:00
|
|
|
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
|
|
|
};
|
2013-06-29 12:59:08 +00:00
|
|
|
if shouldmatch.contains(&idx) {
|
|
|
|
// it should be a path of length 1, and it should
|
2013-07-13 01:35:47 +00:00
|
|
|
// be free-identifier=? or bound-identifier=? to the given binding
|
2013-06-29 12:59:08 +00:00
|
|
|
assert_eq!(varref.segments.len(),1);
|
2014-10-15 06:05:01 +00:00
|
|
|
let varref_name = mtwt::resolve(varref.segments[0].identifier);
|
|
|
|
let varref_marks = mtwt::marksof(varref.segments[0]
|
2014-02-24 20:47:19 +00:00
|
|
|
.identifier
|
|
|
|
.ctxt,
|
|
|
|
invalid_name);
|
2014-01-30 22:28:44 +00:00
|
|
|
if !(varref_name==binding_name) {
|
2014-01-09 10:06:55 +00:00
|
|
|
println!("uh oh, should match but doesn't:");
|
2014-07-06 22:06:32 +00:00
|
|
|
print_hygiene_debug_info();
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
assert_eq!(varref_name,binding_name);
|
2014-01-30 22:28:44 +00:00
|
|
|
if bound_ident_check {
|
2013-09-05 16:29:31 +00:00
|
|
|
// we're checking bound-identifier=?, and the marks
|
|
|
|
// should be the same, too:
|
2013-07-13 01:35:47 +00:00
|
|
|
assert_eq!(varref_marks,binding_marks.clone());
|
|
|
|
}
|
2013-06-29 12:59:08 +00:00
|
|
|
} else {
|
2014-10-15 06:05:01 +00:00
|
|
|
let varref_name = mtwt::resolve(varref.segments[0].identifier);
|
2013-06-29 12:59:08 +00:00
|
|
|
let fail = (varref.segments.len() == 1)
|
2014-07-04 19:18:37 +00:00
|
|
|
&& (varref_name == binding_name);
|
2013-06-29 12:59:08 +00:00
|
|
|
// temp debugging:
|
2014-01-30 22:28:44 +00:00
|
|
|
if fail {
|
2013-09-05 20:28:57 +00:00
|
|
|
println!("failure on test {}",test_idx);
|
|
|
|
println!("text of test case: \"{}\"", teststr);
|
|
|
|
println!("");
|
|
|
|
println!("uh oh, matches but shouldn't:");
|
2014-07-06 22:06:32 +00:00
|
|
|
print_hygiene_debug_info();
|
2013-06-29 12:59:08 +00:00
|
|
|
}
|
|
|
|
assert!(!fail);
|
|
|
|
}
|
|
|
|
}
|
2013-06-25 18:40:51 +00:00
|
|
|
}
|
|
|
|
}
|
2013-05-21 23:57:21 +00:00
|
|
|
|
2013-07-13 01:35:47 +00:00
|
|
|
#[test] fn fmt_in_macro_used_inside_module_macro() {
|
2014-11-14 17:18:10 +00:00
|
|
|
let crate_str = "macro_rules! fmt_wrap(($b:expr)=>($b.to_string()));
|
|
|
|
macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}));
|
|
|
|
foo_module!();
|
2014-05-25 10:17:19 +00:00
|
|
|
".to_string();
|
2013-07-13 01:35:47 +00:00
|
|
|
let cr = expand_crate_str(crate_str);
|
|
|
|
// find the xx binding
|
2014-06-26 21:39:54 +00:00
|
|
|
let bindings = crate_bindings(&cr);
|
2014-02-28 21:09:09 +00:00
|
|
|
let cxbinds: Vec<&ast::Ident> =
|
2014-02-13 17:46:46 +00:00
|
|
|
bindings.iter().filter(|b| {
|
|
|
|
let ident = token::get_ident(**b);
|
2015-02-18 19:48:57 +00:00
|
|
|
let string = &ident[..];
|
2014-02-13 17:46:46 +00:00
|
|
|
"xx" == string
|
|
|
|
}).collect();
|
2015-02-18 19:48:57 +00:00
|
|
|
let cxbinds: &[&ast::Ident] = &cxbinds[..];
|
2013-07-13 01:35:47 +00:00
|
|
|
let cxbind = match cxbinds {
|
|
|
|
[b] => b,
|
2014-10-09 19:17:22 +00:00
|
|
|
_ => panic!("expected just one binding for ext_cx")
|
2013-07-13 01:35:47 +00:00
|
|
|
};
|
2014-02-24 20:47:19 +00:00
|
|
|
let resolved_binding = mtwt::resolve(*cxbind);
|
2014-06-26 21:39:54 +00:00
|
|
|
let varrefs = crate_varrefs(&cr);
|
2013-10-28 08:20:26 +00:00
|
|
|
|
2013-07-13 01:35:47 +00:00
|
|
|
// the xx binding should bind all of the xx varrefs:
|
2014-02-14 05:07:09 +00:00
|
|
|
for (idx,v) in varrefs.iter().filter(|p| {
|
2014-01-31 23:26:59 +00:00
|
|
|
p.segments.len() == 1
|
2015-02-21 10:56:57 +00:00
|
|
|
&& "xx" == &*token::get_ident(p.segments[0].identifier)
|
2014-01-31 23:26:59 +00:00
|
|
|
}).enumerate() {
|
2014-10-15 06:05:01 +00:00
|
|
|
if mtwt::resolve(v.segments[0].identifier) != resolved_binding {
|
2014-01-09 10:06:55 +00:00
|
|
|
println!("uh oh, xx binding didn't match xx varref:");
|
2014-10-15 01:59:41 +00:00
|
|
|
println!("this is xx varref \\# {}", idx);
|
|
|
|
println!("binding: {}", cxbind);
|
|
|
|
println!("resolves to: {}", resolved_binding);
|
2014-10-15 06:05:01 +00:00
|
|
|
println!("varref: {}", v.segments[0].identifier);
|
2014-10-15 01:59:41 +00:00
|
|
|
println!("resolves to: {}",
|
2014-10-15 06:05:01 +00:00
|
|
|
mtwt::resolve(v.segments[0].identifier));
|
2014-02-24 20:47:19 +00:00
|
|
|
mtwt::with_sctable(|x| mtwt::display_sctable(x));
|
2013-07-13 01:35:47 +00:00
|
|
|
}
|
2014-10-15 06:05:01 +00:00
|
|
|
assert_eq!(mtwt::resolve(v.segments[0].identifier),
|
2014-02-28 20:54:01 +00:00
|
|
|
resolved_binding);
|
2013-07-13 01:35:47 +00:00
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2013-06-04 21:56:33 +00:00
|
|
|
#[test]
|
|
|
|
fn pat_idents(){
|
2014-05-07 23:33:43 +00:00
|
|
|
let pat = string_to_pat(
|
2014-05-25 10:17:19 +00:00
|
|
|
"(a,Foo{x:c @ (b,9),y:Bar(4,d)})".to_string());
|
2014-07-07 23:35:15 +00:00
|
|
|
let idents = pattern_bindings(&*pat);
|
2014-06-26 21:39:54 +00:00
|
|
|
assert_eq!(idents, strs_to_idents(vec!("a","c","b","d")));
|
2013-06-04 21:56:33 +00:00
|
|
|
}
|
2013-06-07 19:28:03 +00:00
|
|
|
|
2014-06-24 23:33:44 +00:00
|
|
|
// test the list of identifier patterns gathered by the visitor. Note that
|
|
|
|
// 'None' is listed as an identifier pattern because we don't yet know that
|
|
|
|
// it's the name of a 0-ary variant, and that 'i' appears twice in succession.
|
2013-11-12 01:35:25 +00:00
|
|
|
#[test]
|
2014-07-04 19:18:37 +00:00
|
|
|
fn crate_bindings_test(){
|
2015-01-17 23:55:21 +00:00
|
|
|
let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| {
|
2014-06-24 23:33:44 +00:00
|
|
|
match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
|
2014-06-26 21:39:54 +00:00
|
|
|
let idents = crate_bindings(&the_crate);
|
|
|
|
assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
|
2013-11-12 01:35:25 +00:00
|
|
|
}
|
|
|
|
|
2014-07-04 19:18:37 +00:00
|
|
|
// test the IdentRenamer directly
|
|
|
|
#[test]
|
|
|
|
fn ident_renamer_test () {
|
2015-01-17 23:55:21 +00:00
|
|
|
let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
|
2014-07-04 19:18:37 +00:00
|
|
|
let f_ident = token::str_to_ident("f");
|
|
|
|
let x_ident = token::str_to_ident("x");
|
2015-01-17 23:55:21 +00:00
|
|
|
let int_ident = token::str_to_ident("i32");
|
2014-07-09 05:28:52 +00:00
|
|
|
let renames = vec!((x_ident,Name(16)));
|
2014-07-04 19:18:37 +00:00
|
|
|
let mut renamer = IdentRenamer{renames: &renames};
|
|
|
|
let renamed_crate = renamer.fold_crate(the_crate);
|
|
|
|
let idents = crate_idents(&renamed_crate);
|
|
|
|
let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
|
2015-02-24 18:15:45 +00:00
|
|
|
assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),Name(16),Name(16)]);
|
2014-07-04 19:18:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// test the PatIdentRenamer; only PatIdents get renamed
|
|
|
|
#[test]
|
|
|
|
fn pat_ident_renamer_test () {
|
2015-01-17 23:55:21 +00:00
|
|
|
let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
|
2014-07-04 19:18:37 +00:00
|
|
|
let f_ident = token::str_to_ident("f");
|
|
|
|
let x_ident = token::str_to_ident("x");
|
2015-01-17 23:55:21 +00:00
|
|
|
let int_ident = token::str_to_ident("i32");
|
2014-07-09 05:28:52 +00:00
|
|
|
let renames = vec!((x_ident,Name(16)));
|
2014-07-04 19:18:37 +00:00
|
|
|
let mut renamer = PatIdentRenamer{renames: &renames};
|
|
|
|
let renamed_crate = renamer.fold_crate(the_crate);
|
|
|
|
let idents = crate_idents(&renamed_crate);
|
|
|
|
let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
|
|
|
|
let x_name = x_ident.name;
|
2015-02-24 18:15:45 +00:00
|
|
|
assert_eq!(resolved, [f_ident.name,Name(16),int_ident.name,Name(16),x_name,x_name]);
|
2014-07-04 19:18:37 +00:00
|
|
|
}
|
2013-02-26 18:15:29 +00:00
|
|
|
}
|