// in this file.
// Token-tree macros:
MacInvocTT(pth, tts, _) => {
- if pth.segments.len() > 1u {
+ if pth.segments.len() > 1us {
fld.cx.span_err(pth.span,
"expected macro name without module \
separators");
-> SmallVector<P<ast::Item>> {
let it = expand_item_modifiers(it, fld);
- let mut decorator_items = SmallVector::zero();
- let mut new_attrs = Vec::new();
- for attr in it.attrs.iter() {
- let mname = attr.name();
-
- match fld.cx.syntax_env.find(&intern(mname.get())) {
- Some(rc) => match *rc {
- Decorator(ref dec) => {
- attr::mark_used(attr);
-
- fld.cx.bt_push(ExpnInfo {
- call_site: attr.span,
- callee: NameAndSpan {
- name: mname.get().to_string(),
- format: MacroAttribute,
- span: None
- }
- });
-
- // 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,
- box |&mut : item| items.push(item));
- decorator_items.extend(items.into_iter()
- .flat_map(|item| expand_item(item, fld).into_iter()));
-
- fld.cx.bt_pop();
- }
- _ => new_attrs.push((*attr).clone()),
- },
- _ => new_attrs.push((*attr).clone()),
- }
- }
-
- let mut new_items = match it.node {
- ast::ItemMac(..) => expand_item_mac(it, fld),
- 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);
- }
- let macro_use = contains_macro_use(fld, &new_attrs[]);
- let result = with_exts_frame!(fld.cx.syntax_env,
- macro_use,
- noop_fold_item(it, fld));
- if valid_ident {
- fld.cx.mod_pop();
- }
- result
- },
- _ => {
- let it = P(ast::Item {
- attrs: new_attrs,
- ..(*it).clone()
- });
- noop_fold_item(it, fld)
- }
- };
-
- new_items.push_all(decorator_items);
- new_items
+ expand_annotatable(Annotatable::Item(it), fld)
+ .into_iter().map(|i| i.expect_item()).collect()
}
fn expand_item_modifiers(mut it: P<ast::Item>, fld: &mut MacroExpander)
-> P<ast::Item> {
// partition the attributes into ItemModifiers and others
- let (modifiers, other_attrs): (Vec<_>, _) = it.attrs.iter().cloned().partition(|attr| {
- match fld.cx.syntax_env.find(&intern(attr.name().get())) {
- Some(rc) => match *rc { Modifier(_) => true, _ => false },
- _ => false
- }
- });
+ let (modifiers, other_attrs) = modifiers(&it.attrs, fld);
+
// update the attrs, leave everything else alone. Is this mutation really a good idea?
it = P(ast::Item {
attrs: other_attrs,
});
if modifiers.is_empty() {
- return it;
+ let it = expand_item_multi_modifier(Annotatable::Item(it), fld);
+ return it.expect_item();
}
for attr in modifiers.iter() {
}
}
- // expansion may have added new ItemModifiers
+ // 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.
expand_item_modifiers(it, fld)
}
},
_ => unreachable!()
};
- if pth.segments.len() > 1u {
+ if pth.segments.len() > 1us {
fld.cx.span_err(pth.span, "expected macro name without module separators");
return DummyResult::raw_pat(span);
}
}
}
+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();
+ for attr in a.attrs().iter() {
+ let mname = attr.name();
+
+ match fld.cx.syntax_env.find(&intern(mname.get())) {
+ 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 {
+ name: mname.get().to_string(),
+ format: MacroAttribute,
+ span: None
+ }
+ });
+
+ // 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,
+ box |&mut: item| items.push(item));
+ decorator_items.extend(items.into_iter()
+ .flat_map(|item| expand_item(item, fld).into_iter()));
+
+ 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);
+ }
+ let macro_use = contains_macro_use(fld, &new_attrs[]);
+ 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| {
+ match fld.cx.syntax_env.find(&intern(attr.name().get())) {
+ 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| {
+ match fld.cx.syntax_env.find(&intern(attr.name().get())) {
+ 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
+ }
+
+ for attr in modifiers.iter() {
+ let mname = attr.name();
+
+ match fld.cx.syntax_env.find(&intern(mname.get())) {
+ Some(rc) => match *rc {
+ MultiModifier(ref mac) => {
+ attr::mark_used(attr);
+ fld.cx.bt_push(ExpnInfo {
+ call_site: attr.span,
+ callee: NameAndSpan {
+ name: mname.get().to_string(),
+ format: MacroAttribute,
+ span: None,
+ }
+ });
+ 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)
+}
+
// expand a method
fn expand_method(m: P<ast::Method>, fld: &mut MacroExpander) -> SmallVector<P<ast::Method>> {
m.and_then(|m| match m.node {
vis) => {
let id = fld.new_id(m.id);
let (rewritten_fn_decl, rewritten_body)
- = expand_and_rename_fn_decl_and_block(decl,body,fld);
+ = expand_and_rename_fn_decl_and_block(decl, body, fld);
SmallVector::one(P(ast::Method {
attrs: m.attrs.move_map(|a| fld.fold_attribute(a)),
id: id,
expand_arm(arm, self)
}
+ 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)
+ }
+
fn fold_method(&mut self, method: P<ast::Method>) -> SmallVector<P<ast::Method>> {
expand_method(method, self)
}
pub struct ExpansionConfig {
pub crate_name: String,
pub enable_quotes: bool,
- pub recursion_limit: uint,
+ pub recursion_limit: usize,
}
impl ExpansionConfig {
#[cfg(test)]
mod test {
- use super::{pattern_bindings, expand_crate, contains_macro_use};
+ use super::{pattern_bindings, expand_crate};
use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer, ExpansionConfig};
use ast;
use ast::{Attribute_, AttrOuter, MetaWord, Name};
#[should_fail]
#[test] fn macros_cant_escape_fns_test () {
let src = "fn bogus() {macro_rules! z (() => (3+4));}\
- fn inty() -> int { z!() }".to_string();
+ fn inty() -> i32 { z!() }".to_string();
let sess = parse::new_parse_sess();
let crate_ast = parse::parse_crate_from_source_str(
"<test>".to_string(),
#[should_fail]
#[test] fn macros_cant_escape_mods_test () {
let src = "mod foo {macro_rules! z (() => (3+4));}\
- fn inty() -> int { z!() }".to_string();
+ fn inty() -> i32 { z!() }".to_string();
let sess = parse::new_parse_sess();
let crate_ast = parse::parse_crate_from_source_str(
"<test>".to_string(),
// macro_use modules should allow macros to escape
#[test] fn macros_can_escape_flattened_mods_test () {
let src = "#[macro_use] mod foo {macro_rules! z (() => (3+4));}\
- fn inty() -> int { z!() }".to_string();
+ fn inty() -> i32 { z!() }".to_string();
let sess = parse::new_parse_sess();
let crate_ast = parse::parse_crate_from_source_str(
"<test>".to_string(),
expand_crate(&sess, test_ecfg(), vec!(), vec!(), crate_ast);
}
- // make a MetaWord outer attribute with the given name
- fn make_dummy_attr(s: &str) -> ast::Attribute {
- Spanned {
- span:codemap::DUMMY_SP,
- node: Attribute_ {
- id: attr::mk_attr_id(),
- style: AttrOuter,
- value: P(Spanned {
- node: MetaWord(token::intern_and_get_ident(s)),
- span: codemap::DUMMY_SP,
- }),
- is_sugared_doc: false,
- }
- }
- }
-
fn expand_crate_str(crate_str: String) -> ast::Crate {
let ps = parse::new_parse_sess();
let crate_ast = string_to_parser(&ps, crate_str).parse_crate_mod();
// should be able to use a bound identifier as a literal in a macro definition:
#[test] fn self_macro_parsing(){
expand_crate_str(
- "macro_rules! foo ((zz) => (287u;));
- fn f(zz : int) {foo!(zz);}".to_string()
+ "macro_rules! foo ((zz) => (287;));
+ fn f(zz: i32) {foo!(zz);}".to_string()
);
}
// 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.
- type RenamingTest = (&'static str, Vec<Vec<uint>>, bool);
+ type RenamingTest = (&'static str, Vec<Vec<usize>>, bool);
#[test]
fn automatic_renaming () {
let tests: Vec<RenamingTest> =
vec!(// b & c should get new names throughout, in the expr too:
- ("fn a() -> int { let b = 13; let c = b; b+c }",
+ ("fn a() -> i32 { let b = 13; let c = b; b+c }",
vec!(vec!(0,1),vec!(2)), false),
// both x's should be renamed (how is this causing a bug?)
- ("fn main () {let x: int = 13;x;}",
+ ("fn main () {let x: i32 = 13;x;}",
vec!(vec!(0)), false),
// the use of b after the + should be renamed, the other one not:
- ("macro_rules! f (($x:ident) => (b + $x)); fn a() -> int { let b = 13; f!(b)}",
+ ("macro_rules! f (($x:ident) => (b + $x)); fn a() -> i32 { let b = 13; f!(b)}",
vec!(vec!(1)), false),
// the b before the plus should not be renamed (requires marks)
- ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> int { f!(b)}",
+ ("macro_rules! f (($x:ident) => ({let b=9; ($x + b)})); fn a() -> i32 { f!(b)}",
vec!(vec!(1)), false),
// 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.
("macro_rules! letty(($x:ident) => (let $x = 15;));
macro_rules! user(($x:ident) => ({letty!($x); $x}));
- fn main() -> int {user!(z)}",
+ fn main() -> i32 {user!(z)}",
vec!(vec!(0)), false)
);
for (idx,s) in tests.iter().enumerate() {
// can't write this test case until we have macro-generating macros.
// method arg hygiene
- // method expands to fn get_x(&self_0, x_1:int) {self_0 + self_2 + x_3 + x_1}
+ // method expands to fn get_x(&self_0, x_1: i32) {self_0 + self_2 + x_3 + x_1}
#[test] fn method_arg_hygiene(){
run_renaming_test(
&("macro_rules! inject_x (()=>(x));
macro_rules! inject_self (()=>(self));
struct A;
- impl A{fn get_x(&self, x: int) {self + inject_self!() + inject_x!() + x;} }",
+ impl A{fn get_x(&self, x: i32) {self + inject_self!() + inject_x!() + x;} }",
vec!(vec!(0),vec!(3)),
true),
0)
}
// item fn hygiene
- // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};}
+ // expands to fn q(x_1: i32){fn g(x_2: i32){x_2 + x_1};}
#[test] fn issue_9383(){
run_renaming_test(
- &("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex }));
- fn q(x:int) { bad_macro!(x); }",
+ &("macro_rules! bad_macro (($ex:expr) => (fn g(x: i32){ x + $ex }));
+ fn q(x: i32) { bad_macro!(x); }",
vec!(vec!(1),vec!(0)),true),
0)
}
// closure arg hygiene (ExprClosure)
- // expands to fn f(){(|x_1 : int| {(x_2 + x_1)})(3);}
+ // expands to fn f(){(|x_1 : i32| {(x_2 + x_1)})(3);}
#[test] fn closure_arg_hygiene(){
run_renaming_test(
&("macro_rules! inject_x (()=>(x));
- fn f(){(|x : int| {(inject_x!() + x)})(3);}",
+ fn f(){(|x : i32| {(inject_x!() + x)})(3);}",
vec!(vec!(1)),
true),
0)
// macro_rules in method position. Sadly, unimplemented.
#[test] fn macro_in_method_posn(){
expand_crate_str(
- "macro_rules! my_method (() => (fn thirteen(&self) -> int {13}));
+ "macro_rules! my_method (() => (fn thirteen(&self) -> i32 {13}));
struct A;
impl A{ my_method!(); }
fn f(){A.thirteen;}".to_string());
}
// run one of the renaming tests
- fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
+ fn run_renaming_test(t: &RenamingTest, test_idx: usize) {
let invalid_name = token::special_idents::invalid.name;
let (teststr, bound_connections, bound_ident_check) = match *t {
(ref str,ref conns, bic) => (str.to_string(), conns.clone(), bic)
let varref_idents : Vec<ast::Ident>
= varref.segments.iter().map(|s| s.identifier)
.collect();
- println!("varref #{}: {}, resolves to {}",idx, varref_idents, varref_name);
+ println!("varref #{}: {:?}, resolves to {}",idx, varref_idents, varref_name);
let string = token::get_ident(final_varref_ident);
println!("varref's first segment's string: \"{}\"", string.get());
println!("binding #{}: {}, resolves to {}",
// it's the name of a 0-ary variant, and that 'i' appears twice in succession.
#[test]
fn crate_bindings_test(){
- let the_crate = string_to_crate("fn main (a : int) -> int {|b| {
+ let the_crate = string_to_crate("fn main (a: i32) -> i32 {|b| {
match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
let idents = crate_bindings(&the_crate);
assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
// test the IdentRenamer directly
#[test]
fn ident_renamer_test () {
- let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
+ let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
let f_ident = token::str_to_ident("f");
let x_ident = token::str_to_ident("x");
- let int_ident = token::str_to_ident("int");
+ let int_ident = token::str_to_ident("i32");
let renames = vec!((x_ident,Name(16)));
let mut renamer = IdentRenamer{renames: &renames};
let renamed_crate = renamer.fold_crate(the_crate);
// test the PatIdentRenamer; only PatIdents get renamed
#[test]
fn pat_ident_renamer_test () {
- let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
+ let the_crate = string_to_crate("fn f(x: i32){let x = x; x}".to_string());
let f_ident = token::str_to_ident("f");
let x_ident = token::str_to_ident("x");
- let int_ident = token::str_to_ident("int");
+ let int_ident = token::str_to_ident("i32");
let renames = vec!((x_ident,Name(16)));
let mut renamer = PatIdentRenamer{renames: &renames};
let renamed_crate = renamer.fold_crate(the_crate);