Skip to content

Commit 2f129b2

Browse files
committed
implement hygiene for arguments of item functions
Closes rust-lang#9384
1 parent 8cf4059 commit 2f129b2

File tree

1 file changed

+101
-21
lines changed

1 file changed

+101
-21
lines changed

src/libsyntax/ext/expand.rs

+101-21
Original file line numberDiff line numberDiff line change
@@ -344,15 +344,16 @@ fn expand_item(it: Gc<ast::Item>, fld: &mut MacroExpander)
344344

345345
fn expand_item_modifiers(mut it: Gc<ast::Item>, fld: &mut MacroExpander)
346346
-> Gc<ast::Item> {
347-
let (modifiers, attrs) = it.attrs.partitioned(|attr| {
347+
// partition the attributes into ItemModifiers and others
348+
let (modifiers, other_attrs) = it.attrs.partitioned(|attr| {
348349
match fld.extsbox.find(&intern(attr.name().get())) {
349350
Some(&ItemModifier(_)) => true,
350351
_ => false
351352
}
352353
});
353-
354+
// update the attrs, leave everything else alone. Is this mutation really a good idea?
354355
it = box(GC) ast::Item {
355-
attrs: attrs,
356+
attrs: other_attrs,
356357
..(*it).clone()
357358
};
358359

@@ -385,6 +386,34 @@ fn expand_item_modifiers(mut it: Gc<ast::Item>, fld: &mut MacroExpander)
385386
expand_item_modifiers(it, fld)
386387
}
387388

389+
/// Expand item_underscore
390+
fn expand_item_underscore(item: &ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
391+
match *item {
392+
ast::ItemFn(decl, fn_style, abi, ref generics, body) => {
393+
let expanded_decl = fld.fold_fn_decl(decl);
394+
// avoiding the pattern_bindings abstraction here
395+
// for efficiency's sake, to re-use one mutable vector
396+
// in the collection.
397+
let mut pat_idents = PatIdentFinder{ident_accumulator:Vec::new()};
398+
//pat_idents.visit_fn_decl(expanded_decl,());
399+
for arg in expanded_decl.inputs.iter() {
400+
pat_idents.visit_pat(arg.pat,());
401+
}
402+
let idents = pat_idents.ident_accumulator;
403+
let new_pending_renames =
404+
idents.iter().map(|id : &ast::Ident| (*id,fresh_name(id))).collect();
405+
let mut rename_pat_fld = PatIdentRenamer{renames:&new_pending_renames};
406+
let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
407+
// now, a renamer for *all* idents:
408+
let mut rename_fld = IdentRenamer{renames:&new_pending_renames};
409+
let rewritten_body = fld.fold_block(rename_fld.fold_block(body));
410+
let expanded_generics = fold::fold_generics(generics,fld);
411+
ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body)
412+
}
413+
_ => noop_fold_item_underscore(&*item, fld)
414+
}
415+
}
416+
388417
// does this attribute list contain "macro_escape" ?
389418
fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
390419
attr::contains_name(attrs, "macro_escape")
@@ -667,19 +696,14 @@ fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
667696
// code duplicated from 'let', above. Perhaps this can be lifted
668697
// into a separate function:
669698
let idents = pattern_bindings(*first_pat);
670-
let mut new_pending_renames =
699+
let new_pending_renames =
671700
idents.iter().map(|id| (*id,fresh_name(id))).collect();
672-
// rewrite all of the patterns using the new names (the old
673-
// ones have already been applied). Note that we depend here
674-
// on the guarantee that after expansion, there can't be any
675-
// Path expressions (a.k.a. varrefs) left in the pattern. If
676-
// this were false, we'd need to apply this renaming only to
677-
// the bindings, and not to the varrefs, using a more targeted
678-
// fold-er.
679-
let mut rename_fld = IdentRenamer{renames:&mut new_pending_renames};
701+
// apply the renaming, but only to the PatIdents:
702+
let mut rename_pats_fld = PatIdentRenamer{renames:&new_pending_renames};
680703
let rewritten_pats =
681-
expanded_pats.iter().map(|pat| rename_fld.fold_pat(*pat)).collect();
704+
expanded_pats.iter().map(|pat| rename_pats_fld.fold_pat(*pat)).collect();
682705
// apply renaming and then expansion to the guard and the body:
706+
let mut rename_fld = IdentRenamer{renames:&new_pending_renames};
683707
let rewritten_guard =
684708
arm.guard.map(|g| fld.fold_expr(rename_fld.fold_expr(g)));
685709
let rewritten_body = fld.fold_expr(rename_fld.fold_expr(arm.body));
@@ -905,6 +929,10 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
905929
expand_item(item, self)
906930
}
907931

932+
fn fold_item_underscore(&mut self, item: &ast::Item_) -> ast::Item_ {
933+
expand_item_underscore(item, self)
934+
}
935+
908936
fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<Gc<ast::Stmt>> {
909937
expand_stmt(stmt, self)
910938
}
@@ -1052,13 +1080,14 @@ fn original_span(cx: &ExtCtxt) -> Gc<codemap::ExpnInfo> {
10521080
#[cfg(test)]
10531081
mod test {
10541082
use super::{pattern_bindings, expand_crate, contains_macro_escape};
1055-
use super::{PatIdentFinder};
1083+
use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer};
10561084
use ast;
10571085
use ast::{Attribute_, AttrOuter, MetaWord};
10581086
use attr;
10591087
use codemap;
10601088
use codemap::Spanned;
10611089
use ext::mtwt;
1090+
use fold::Folder;
10621091
use parse;
10631092
use parse::token;
10641093
use util::parser_testing::{string_to_parser};
@@ -1096,7 +1125,24 @@ mod test {
10961125
path_finder.path_accumulator
10971126
}
10981127

1128+
/// A Visitor that extracts the identifiers from a thingy.
1129+
// as a side note, I'm starting to want to abstract over these....
1130+
struct IdentFinder{
1131+
ident_accumulator: Vec<ast::Ident>
1132+
}
10991133

1134+
impl Visitor<()> for IdentFinder {
1135+
fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident, _: ()){
1136+
self.ident_accumulator.push(id);
1137+
}
1138+
}
1139+
1140+
/// Find the idents in a crate
1141+
fn crate_idents(the_crate: &ast::Crate) -> Vec<ast::Ident> {
1142+
let mut ident_finder = IdentFinder{ident_accumulator: Vec::new()};
1143+
visit::walk_crate(&mut ident_finder, the_crate, ());
1144+
ident_finder.ident_accumulator
1145+
}
11001146

11011147
// these following tests are quite fragile, in that they don't test what
11021148
// *kind* of failure occurs.
@@ -1196,6 +1242,7 @@ mod test {
11961242
name_finder.ident_accumulator
11971243
}
11981244

1245+
11991246
//fn expand_and_resolve(crate_str: @str) -> ast::crate {
12001247
//let expanded_ast = expand_crate_str(crate_str);
12011248
// println!("expanded: {:?}\n",expanded_ast);
@@ -1321,7 +1368,7 @@ mod test {
13211368
// but *shouldnt* bind because it was inserted by a different macro....
13221369
// can't write this test case until we have macro-generating macros.
13231370

1324-
// FIXME #9383 : lambda var hygiene
1371+
// lambda var hygiene
13251372
// expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};}
13261373
#[test]
13271374
fn issue_9383(){
@@ -1380,9 +1427,9 @@ mod test {
13801427
assert_eq!(varref_marks,binding_marks.clone());
13811428
}
13821429
} else {
1430+
let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
13831431
let fail = (varref.segments.len() == 1)
1384-
&& (mtwt::resolve(varref.segments.get(0).identifier)
1385-
== binding_name);
1432+
&& (varref_name == binding_name);
13861433
// temp debugging:
13871434
if fail {
13881435
let varref_idents : Vec<ast::Ident>
@@ -1393,15 +1440,18 @@ mod test {
13931440
println!("text of test case: \"{}\"", teststr);
13941441
println!("");
13951442
println!("uh oh, matches but shouldn't:");
1396-
println!("varref: {}",varref_idents);
1443+
println!("varref #{}: {}, resolves to {}",idx, varref_idents,
1444+
varref_name);
13971445
// good lord, you can't make a path with 0 segments, can you?
13981446
let string = token::get_ident(varref.segments
13991447
.get(0)
14001448
.identifier);
14011449
println!("varref's first segment's uint: {}, and string: \"{}\"",
14021450
varref.segments.get(0).identifier.name,
14031451
string.get());
1404-
println!("binding: {}", *bindings.get(binding_idx));
1452+
println!("binding #{}: {}, resolves to {}",
1453+
binding_idx, *bindings.get(binding_idx),
1454+
binding_name);
14051455
mtwt::with_sctable(|x| mtwt::display_sctable(x));
14061456
}
14071457
assert!(!fail);
@@ -1464,13 +1514,43 @@ foo_module!()
14641514
// 'None' is listed as an identifier pattern because we don't yet know that
14651515
// it's the name of a 0-ary variant, and that 'i' appears twice in succession.
14661516
#[test]
1467-
fn crate_idents(){
1517+
fn crate_bindings_test(){
14681518
let the_crate = string_to_crate("fn main (a : int) -> int {|b| {
14691519
match 34 {None => 3, Some(i) | i => j, Foo{k:z,l:y} => \"banana\"}} }".to_string());
14701520
let idents = crate_bindings(&the_crate);
14711521
assert_eq!(idents, strs_to_idents(vec!("a","b","None","i","i","z","y")));
14721522
}
14731523

1474-
//
1524+
// test the IdentRenamer directly
1525+
#[test]
1526+
fn ident_renamer_test () {
1527+
let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
1528+
let f_ident = token::str_to_ident("f");
1529+
let x_ident = token::str_to_ident("x");
1530+
let int_ident = token::str_to_ident("int");
1531+
let renames = vec!((x_ident,16));
1532+
let mut renamer = IdentRenamer{renames: &renames};
1533+
let renamed_crate = renamer.fold_crate(the_crate);
1534+
let idents = crate_idents(&renamed_crate);
1535+
let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1536+
assert_eq!(resolved,vec!(f_ident.name,16,int_ident.name,16,16,16));
1537+
}
1538+
1539+
// test the PatIdentRenamer; only PatIdents get renamed
1540+
#[test]
1541+
fn pat_ident_renamer_test () {
1542+
let the_crate = string_to_crate("fn f(x : int){let x = x; x}".to_string());
1543+
let f_ident = token::str_to_ident("f");
1544+
let x_ident = token::str_to_ident("x");
1545+
let int_ident = token::str_to_ident("int");
1546+
let renames = vec!((x_ident,16));
1547+
let mut renamer = PatIdentRenamer{renames: &renames};
1548+
let renamed_crate = renamer.fold_crate(the_crate);
1549+
let idents = crate_idents(&renamed_crate);
1550+
let resolved : Vec<ast::Name> = idents.iter().map(|id| mtwt::resolve(*id)).collect();
1551+
let x_name = x_ident.name;
1552+
assert_eq!(resolved,vec!(f_ident.name,16,int_ident.name,16,x_name,x_name));
1553+
}
1554+
14751555

14761556
}

0 commit comments

Comments
 (0)