]> git.lizzy.rs Git - rust.git/commitdiff
auto merge of #15425 : jbclements/rust/hygiene-for-3-kinds-of-args, r=cmr
authorbors <bors@rust-lang.org>
Sat, 5 Jul 2014 03:16:50 +0000 (03:16 +0000)
committerbors <bors@rust-lang.org>
Sat, 5 Jul 2014 03:16:50 +0000 (03:16 +0000)
This pull request adds hygiene for 3 kinds of argument bindings:
- arguments to item fns,
- arguments to `ExprFnBlock`s, and
- arguments to `ExprProc`s

It also adds a bunch of unit tests, fixes a few macro uses to be non-capturing, and has a few cleanup items.

local `make check` succeeds.

src/librustc/middle/trans/_match.rs
src/libstd/io/extensions.rs
src/libsyntax/ast.rs
src/libsyntax/ext/base.rs
src/libsyntax/ext/expand.rs
src/libsyntax/ext/mtwt.rs
src/libsyntax/fold.rs
src/libsyntax/parse/token.rs

index 2f8918acb30bc30d8700ed3b954338b3ad36b037..d3905582a365fd1a076c1333166673ba191edcc6 100644 (file)
@@ -781,9 +781,9 @@ fn extract_vec_elems<'a>(
 // matches should fit that sort of pattern or NONE (however, some of the
 // matches may be wildcards like _ or identifiers).
 macro_rules! any_pat (
-    ($m:expr, $pattern:pat) => (
+    ($m:expr, $col:expr, $pattern:pat) => (
         ($m).iter().any(|br| {
-            match br.pats.get(col).node {
+            match br.pats.get($col).node {
                 $pattern => true,
                 _ => false
             }
@@ -792,11 +792,11 @@ macro_rules! any_pat (
 )
 
 fn any_uniq_pat(m: &[Match], col: uint) -> bool {
-    any_pat!(m, ast::PatBox(_))
+    any_pat!(m, col, ast::PatBox(_))
 }
 
 fn any_region_pat(m: &[Match], col: uint) -> bool {
-    any_pat!(m, ast::PatRegion(_))
+    any_pat!(m, col, ast::PatRegion(_))
 }
 
 fn any_irrefutable_adt_pat(bcx: &Block, m: &[Match], col: uint) -> bool {
index 277aca2332d475cb1b42a9852f0f59b793d2d505..ca3eee01575fd3aca54bbddd85bb875925a0a69b 100644 (file)
@@ -508,14 +508,15 @@ mod bench {
     use prelude::*;
     use self::test::Bencher;
 
+    // why is this a macro? wouldn't an inlined function work just as well?
     macro_rules! u64_from_be_bytes_bench_impl(
-        ($size:expr, $stride:expr, $start_index:expr) =>
+        ($b:expr, $size:expr, $stride:expr, $start_index:expr) =>
         ({
             use super::u64_from_be_bytes;
 
             let data = Vec::from_fn($stride*100+$start_index, |i| i as u8);
             let mut sum = 0u64;
-            b.iter(|| {
+            $b.iter(|| {
                 let mut i = $start_index;
                 while i < data.len() {
                     sum += u64_from_be_bytes(data.as_slice(), i, $size);
@@ -527,31 +528,31 @@ macro_rules! u64_from_be_bytes_bench_impl(
 
     #[bench]
     fn u64_from_be_bytes_4_aligned(b: &mut Bencher) {
-        u64_from_be_bytes_bench_impl!(4, 4, 0);
+        u64_from_be_bytes_bench_impl!(b, 4, 4, 0);
     }
 
     #[bench]
     fn u64_from_be_bytes_4_unaligned(b: &mut Bencher) {
-        u64_from_be_bytes_bench_impl!(4, 4, 1);
+        u64_from_be_bytes_bench_impl!(b, 4, 4, 1);
     }
 
     #[bench]
     fn u64_from_be_bytes_7_aligned(b: &mut Bencher) {
-        u64_from_be_bytes_bench_impl!(7, 8, 0);
+        u64_from_be_bytes_bench_impl!(b, 7, 8, 0);
     }
 
     #[bench]
     fn u64_from_be_bytes_7_unaligned(b: &mut Bencher) {
-        u64_from_be_bytes_bench_impl!(7, 8, 1);
+        u64_from_be_bytes_bench_impl!(b, 7, 8, 1);
     }
 
     #[bench]
     fn u64_from_be_bytes_8_aligned(b: &mut Bencher) {
-        u64_from_be_bytes_bench_impl!(8, 8, 0);
+        u64_from_be_bytes_bench_impl!(b, 8, 8, 0);
     }
 
     #[bench]
     fn u64_from_be_bytes_8_unaligned(b: &mut Bencher) {
-        u64_from_be_bytes_bench_impl!(8, 8, 1);
+        u64_from_be_bytes_bench_impl!(b, 8, 8, 1);
     }
 }
index 237d0660a41dcca5edb5f15875cd41bdc5fd2f5a..ce1302c8db8749b31e813d3f8a132fb118d492f7 100644 (file)
@@ -190,6 +190,8 @@ pub struct TyParam {
     pub span: Span
 }
 
+/// Represents lifetimes and type parameters attached to a declaration
+/// of a function, enum, trait, etc.
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
 pub struct Generics {
     pub lifetimes: Vec<Lifetime>,
@@ -288,7 +290,7 @@ pub enum Pat_ {
     PatWild,
     PatWildMulti,
     // A PatIdent may either be a new bound variable,
-    // or a nullary enum (in which case the second field
+    // or a nullary enum (in which case the third field
     // is None).
     // In the nullary enum case, the parser can't determine
     // which it is. The resolver determines this, and
@@ -453,10 +455,10 @@ pub enum Expr_ {
     ExprCast(Gc<Expr>, P<Ty>),
     ExprIf(Gc<Expr>, P<Block>, Option<Gc<Expr>>),
     ExprWhile(Gc<Expr>, P<Block>),
-    // FIXME #6993: change to Option<Name>
+    // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
     ExprForLoop(Gc<Pat>, Gc<Expr>, P<Block>, Option<Ident>),
     // Conditionless loop (can be exited with break, cont, or ret)
-    // FIXME #6993: change to Option<Name>
+    // FIXME #6993: change to Option<Name> ... or not, if these are hygienic.
     ExprLoop(P<Block>, Option<Ident>),
     ExprMatch(Gc<Expr>, Vec<Arm>),
     ExprFnBlock(P<FnDecl>, P<Block>),
@@ -468,9 +470,8 @@ pub enum Expr_ {
     ExprField(Gc<Expr>, SpannedIdent, Vec<P<Ty>>),
     ExprIndex(Gc<Expr>, Gc<Expr>),
 
-    /// Expression that looks like a "name". For example,
-    /// `std::slice::from_elem::<uint>` is an ExprPath that's the "name" part
-    /// of a function call.
+    /// Variable reference, possibly containing `::` and/or
+    /// type parameters, e.g. foo::bar::<baz>
     ExprPath(Path),
 
     ExprAddrOf(Mutability, Gc<Expr>),
@@ -643,6 +644,8 @@ pub struct TypeField {
     pub span: Span,
 }
 
+/// Represents a required method in a trait declaration,
+/// one without a default implementation
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
 pub struct TypeMethod {
     pub ident: Ident,
@@ -656,6 +659,8 @@ pub struct TypeMethod {
     pub vis: Visibility,
 }
 
+/// Represents a method declaration in a trait declaration, possibly
+/// including a default implementation
 // A trait method is either required (meaning it doesn't have an
 // implementation, just a signature) or provided (meaning it has a default
 // implementation).
@@ -741,6 +746,7 @@ fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
     }
 }
 
+/// Represents the type of a closure
 #[deriving(PartialEq, Eq, Encodable, Decodable, Hash)]
 pub struct ClosureTy {
     pub lifetimes: Vec<Lifetime>,
@@ -809,6 +815,7 @@ pub struct InlineAsm {
     pub dialect: AsmDialect
 }
 
+/// represents an argument in a function header
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
 pub struct Arg {
     pub ty: P<Ty>,
@@ -836,7 +843,7 @@ pub fn new_self(span: Span, mutability: Mutability) -> Arg {
     }
 }
 
-// represents the header (not the body) of a function declaration
+/// represents the header (not the body) of a function declaration
 #[deriving(Clone, PartialEq, Eq, Encodable, Decodable, Hash)]
 pub struct FnDecl {
     pub inputs: Vec<Arg>,
@@ -1107,6 +1114,7 @@ pub enum Item_ {
     ItemTy(P<Ty>, Generics),
     ItemEnum(EnumDef, Generics),
     ItemStruct(Gc<StructDef>, Generics),
+    /// Represents a Trait Declaration
     ItemTrait(Generics, Sized, Vec<TraitRef> , Vec<TraitMethod> ),
     ItemImpl(Generics,
              Option<TraitRef>, // (optional) trait this impl implements
index d2e69204d333cb5cb0374857bdf9c456a0d22492..cf69277594fd5380631d8f9239a9df34ca0fb414 100644 (file)
@@ -19,6 +19,7 @@
 use parse::token;
 use parse::token::{InternedString, intern, str_to_ident};
 use util::small_vector::SmallVector;
+use ext::mtwt;
 
 use std::collections::HashMap;
 use std::gc::{Gc, GC};
@@ -273,7 +274,7 @@ pub struct BlockInfo {
     // should macros escape from this scope?
     pub macros_escape: bool,
     // what are the pending renames?
-    pub pending_renames: RenameList,
+    pub pending_renames: mtwt::RenameList,
 }
 
 impl BlockInfo {
@@ -285,9 +286,6 @@ pub fn new() -> BlockInfo {
     }
 }
 
-// a list of ident->name renamings
-pub type RenameList = Vec<(ast::Ident, Name)>;
-
 // The base map of methods for expanding syntax extension
 // AST nodes into full ASTs
 pub fn syntax_expander_table() -> SyntaxEnv {
index c3413293e52ae4b1ac664de13b7246ccffdd5070..b30b62c8901d4059d183ff39e31bce03b2b37be6 100644 (file)
@@ -21,6 +21,7 @@
 use codemap::{Span, Spanned, ExpnInfo, NameAndSpan, MacroBang, MacroAttribute};
 use crateid::CrateId;
 use ext::base::*;
+use fold;
 use fold::*;
 use parse;
 use parse::token::{fresh_mark, fresh_name, intern};
@@ -228,6 +229,20 @@ pub fn expand_expr(e: Gc<ast::Expr>, fld: &mut MacroExpander) -> Gc<ast::Expr> {
             fld.cx.expr(e.span, ast::ExprLoop(loop_block, opt_ident))
         }
 
+        ast::ExprFnBlock(fn_decl, block) => {
+            let (rewritten_fn_decl, rewritten_block)
+                = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
+            let new_node = ast::ExprFnBlock(rewritten_fn_decl, rewritten_block);
+            box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
+        }
+
+        ast::ExprProc(fn_decl, block) => {
+            let (rewritten_fn_decl, rewritten_block)
+                = expand_and_rename_fn_decl_and_block(fn_decl, block, fld);
+            let new_node = ast::ExprProc(rewritten_fn_decl, rewritten_block);
+            box(GC) ast::Expr{id:e.id, node: new_node, span: fld.new_span(e.span)}
+        }
+
         _ => noop_fold_expr(e, fld)
     }
 }
@@ -267,7 +282,8 @@ fn expand_loop_block(loop_block: P<Block>,
     }
 }
 
-// eval $e with a new exts frame:
+// eval $e with a new exts frame.
+// must be a macro so that $e isn't evaluated too early.
 macro_rules! with_exts_frame (
     ($extsboxexpr:expr,$macros_escape:expr,$e:expr) =>
     ({$extsboxexpr.push_frame();
@@ -342,15 +358,16 @@ fn expand_item(it: Gc<ast::Item>, fld: &mut MacroExpander)
 
 fn expand_item_modifiers(mut it: Gc<ast::Item>, fld: &mut MacroExpander)
                          -> Gc<ast::Item> {
-    let (modifiers, attrs) = it.attrs.partitioned(|attr| {
+    // partition the attributes into ItemModifiers and others
+    let (modifiers, other_attrs) = it.attrs.partitioned(|attr| {
         match fld.extsbox.find(&intern(attr.name().get())) {
             Some(&ItemModifier(_)) => true,
             _ => false
         }
     });
-
+    // update the attrs, leave everything else alone. Is this mutation really a good idea?
     it = box(GC) ast::Item {
-        attrs: attrs,
+        attrs: other_attrs,
         ..(*it).clone()
     };
 
@@ -383,6 +400,19 @@ fn expand_item_modifiers(mut it: Gc<ast::Item>, fld: &mut MacroExpander)
     expand_item_modifiers(it, fld)
 }
 
+/// Expand item_underscore
+fn expand_item_underscore(item: &ast::Item_, fld: &mut MacroExpander) -> ast::Item_ {
+    match *item {
+        ast::ItemFn(decl, fn_style, abi, ref generics, body) => {
+            let (rewritten_fn_decl, rewritten_body)
+                = expand_and_rename_fn_decl_and_block(decl,body,fld);
+            let expanded_generics = fold::fold_generics(generics,fld);
+            ast::ItemFn(rewritten_fn_decl, fn_style, abi, expanded_generics, rewritten_body)
+        }
+        _ => noop_fold_item_underscore(&*item, fld)
+    }
+}
+
 // does this attribute list contain "macro_escape" ?
 fn contains_macro_escape(attrs: &[ast::Attribute]) -> bool {
     attr::contains_name(attrs, "macro_escape")
@@ -609,7 +639,7 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
                     } = **local;
                     // expand the pat (it might contain macro uses):
                     let expanded_pat = fld.fold_pat(pat);
-                    // find the pat_idents in the pattern:
+                    // find the PatIdents in the pattern:
                     // 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.
@@ -653,6 +683,7 @@ fn expand_non_macro_stmt(s: &Stmt, fld: &mut MacroExpander)
     }
 }
 
+// expand the arm of a 'match', renaming for macro hygiene
 fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
     // expand pats... they might contain macro uses:
     let expanded_pats : Vec<Gc<ast::Pat>> = arm.pats.iter().map(|pat| fld.fold_pat(*pat)).collect();
@@ -662,22 +693,15 @@ fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
     // all of the pats must have the same set of bindings, so use the
     // first one to extract them and generate new names:
     let first_pat = expanded_pats.get(0);
-    // code duplicated from 'let', above. Perhaps this can be lifted
-    // into a separate function:
     let idents = pattern_bindings(*first_pat);
-    let mut new_pending_renames =
+    let new_renames =
         idents.iter().map(|id| (*id,fresh_name(id))).collect();
-    // rewrite all of the patterns using the new names (the old
-    // ones have already been applied). Note that we depend here
-    // on the guarantee that after expansion, there can't be any
-    // Path expressions (a.k.a. varrefs) left in the pattern. If
-    // this were false, we'd need to apply this renaming only to
-    // the bindings, and not to the varrefs, using a more targeted
-    // fold-er.
-    let mut rename_fld = IdentRenamer{renames:&mut new_pending_renames};
+    // apply the renaming, but only to the PatIdents:
+    let mut rename_pats_fld = PatIdentRenamer{renames:&new_renames};
     let rewritten_pats =
-        expanded_pats.iter().map(|pat| rename_fld.fold_pat(*pat)).collect();
+        expanded_pats.iter().map(|pat| rename_pats_fld.fold_pat(*pat)).collect();
     // apply renaming and then expansion to the guard and the body:
+    let mut rename_fld = IdentRenamer{renames:&new_renames};
     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));
@@ -689,45 +713,47 @@ fn expand_arm(arm: &ast::Arm, fld: &mut MacroExpander) -> ast::Arm {
     }
 }
 
-
-
-// a visitor that extracts the pat_ident (binding) paths
-// from a given thingy and puts them in a mutable
-// array
+/// A visitor that extracts the PatIdent (binding) paths
+/// from a given thingy and puts them in a mutable
+/// array
 #[deriving(Clone)]
-struct NameFinderContext {
+struct PatIdentFinder {
     ident_accumulator: Vec<ast::Ident> ,
 }
 
-impl Visitor<()> for NameFinderContext {
+impl Visitor<()> for PatIdentFinder {
     fn visit_pat(&mut self, pattern: &ast::Pat, _: ()) {
         match *pattern {
-            // we found a pat_ident!
-            ast::Pat {
-                id: _,
-                node: ast::PatIdent(_, ref path1, ref inner),
-                span: _
-            } => {
+            ast::Pat { id: _, node: ast::PatIdent(_, ref path1, ref inner), span: _ } => {
                 self.ident_accumulator.push(path1.node);
-                // visit optional subpattern of pat_ident:
+                // visit optional subpattern of PatIdent:
                 for subpat in inner.iter() {
                     self.visit_pat(&**subpat, ())
                 }
             }
-            // use the default traversal for non-pat_idents
+            // use the default traversal for non-PatIdents
             _ => visit::walk_pat(self, pattern, ())
         }
     }
 
 }
 
-// find the pat_ident paths in a pattern
+/// find the PatIdent paths in a pattern
 fn pattern_bindings(pat : &ast::Pat) -> Vec<ast::Ident> {
-    let mut name_finder = NameFinderContext{ident_accumulator:Vec::new()};
+    let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
     name_finder.visit_pat(pat,());
     name_finder.ident_accumulator
 }
 
+/// 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()};
+    for arg in fn_decl.inputs.iter() {
+        pat_idents.visit_pat(arg.pat,());
+    }
+    pat_idents.ident_accumulator
+}
+
 // expand a block. pushes a new exts_frame, then calls expand_block_elts
 fn expand_block(blk: &Block, fld: &mut MacroExpander) -> P<Block> {
     // see note below about treatment of exts table
@@ -844,34 +870,71 @@ fn expand_pat(p: Gc<ast::Pat>, fld: &mut MacroExpander) -> Gc<ast::Pat> {
     }
 }
 
-// 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)
+/// 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)
 pub struct IdentRenamer<'a> {
-    renames: &'a mut RenameList,
+    renames: &'a mtwt::RenameList,
 }
 
 impl<'a> Folder for IdentRenamer<'a> {
     fn fold_ident(&mut self, id: Ident) -> Ident {
-        let new_ctxt = self.renames.iter().fold(id.ctxt, |ctxt, &(from, to)| {
-            mtwt::new_rename(from, to, ctxt)
-        });
         Ident {
             name: id.name,
-            ctxt: new_ctxt,
+            ctxt: mtwt::apply_renames(self.renames, id.ctxt),
         }
     }
 }
 
-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,
-        expn_info: cx.backtrace(),
+/// 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> {
+    fn fold_pat(&mut self, pat: Gc<ast::Pat>) -> Gc<ast::Pat> {
+        match pat.node {
+            ast::PatIdent(binding_mode, Spanned{span: ref sp, node: id}, ref sub) => {
+                let new_ident = Ident{name: id.name,
+                                      ctxt: mtwt::apply_renames(self.renames, id.ctxt)};
+                let new_node =
+                    ast::PatIdent(binding_mode,
+                                  Spanned{span: self.new_span(*sp), node: new_ident},
+                                  sub.map(|p| self.fold_pat(p)));
+                box(GC) ast::Pat {
+                    id: pat.id,
+                    span: self.new_span(pat.span),
+                    node: new_node,
+                }
+            },
+            _ => noop_fold_pat(pat, self)
+        }
     }
 }
 
+/// 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.
+fn expand_and_rename_fn_decl_and_block(fn_decl: &ast::FnDecl, block: Gc<ast::Block>,
+                                       fld: &mut MacroExpander)
+    -> (Gc<ast::FnDecl>, Gc<ast::Block>) {
+    let expanded_decl = fld.fold_fn_decl(fn_decl);
+    let idents = fn_decl_arg_bindings(expanded_decl);
+    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};
+    let rewritten_fn_decl = rename_pat_fld.fold_fn_decl(expanded_decl);
+    // 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)
+}
+
+/// A tree-folder that performs macro expansion
 pub struct MacroExpander<'a, 'b> {
     pub extsbox: SyntaxEnv,
     pub cx: &'a mut ExtCtxt<'b>,
@@ -890,6 +953,10 @@ fn fold_item(&mut self, item: Gc<ast::Item>) -> SmallVector<Gc<ast::Item>> {
         expand_item(item, self)
     }
 
+    fn fold_item_underscore(&mut self, item: &ast::Item_) -> ast::Item_ {
+        expand_item_underscore(item, self)
+    }
+
     fn fold_stmt(&mut self, stmt: &ast::Stmt) -> SmallVector<Gc<ast::Stmt>> {
         expand_stmt(stmt, self)
     }
@@ -907,6 +974,15 @@ fn new_span(&mut self, span: Span) -> Span {
     }
 }
 
+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,
+        expn_info: cx.backtrace(),
+    }
+}
+
 pub struct ExpansionConfig {
     pub deriving_hash_type_parameter: bool,
     pub crate_id: CrateId,
@@ -966,7 +1042,7 @@ impl Folder for Marker {
     fn fold_ident(&mut self, id: Ident) -> Ident {
         ast::Ident {
             name: id.name,
-            ctxt: mtwt::new_mark(self.mark, id.ctxt)
+            ctxt: mtwt::apply_mark(self.mark, id.ctxt)
         }
     }
     fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
@@ -974,7 +1050,7 @@ fn fold_mac(&mut self, m: &ast::Mac) -> ast::Mac {
             MacInvocTT(ref path, ref tts, ctxt) => {
                 MacInvocTT(self.fold_path(path),
                            fold_tts(tts.as_slice(), self),
-                           mtwt::new_mark(self.mark, ctxt))
+                           mtwt::apply_mark(self.mark, ctxt))
             }
         };
         Spanned {
@@ -1028,13 +1104,14 @@ fn original_span(cx: &ExtCtxt) -> Gc<codemap::ExpnInfo> {
 #[cfg(test)]
 mod test {
     use super::{pattern_bindings, expand_crate, contains_macro_escape};
-    use super::{NameFinderContext};
+    use super::{PatIdentFinder, IdentRenamer, PatIdentRenamer};
     use ast;
     use ast::{Attribute_, AttrOuter, MetaWord};
     use attr;
     use codemap;
     use codemap::Spanned;
     use ext::mtwt;
+    use fold::Folder;
     use parse;
     use parse::token;
     use util::parser_testing::{string_to_parser};
@@ -1072,7 +1149,24 @@ fn crate_varrefs(the_crate : &ast::Crate) -> Vec<ast::Path> {
         path_finder.path_accumulator
     }
 
+    /// A Visitor that extracts the identifiers from a thingy.
+    // as a side note, I'm starting to want to abstract over these....
+    struct IdentFinder{
+        ident_accumulator: Vec<ast::Ident>
+    }
+
+    impl Visitor<()> for IdentFinder {
+        fn visit_ident(&mut self, _: codemap::Span, id: ast::Ident, _: ()){
+            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()};
+        visit::walk_crate(&mut ident_finder, the_crate, ());
+        ident_finder.ident_accumulator
+    }
 
     // these following tests are quite fragile, in that they don't test what
     // *kind* of failure occurs.
@@ -1167,12 +1261,11 @@ fn expand_crate_str(crate_str: String) -> ast::Crate {
 
     // find the pat_ident paths in a crate
     fn crate_bindings(the_crate : &ast::Crate) -> Vec<ast::Ident> {
-        let mut name_finder = NameFinderContext{ident_accumulator:Vec::new()};
+        let mut name_finder = PatIdentFinder{ident_accumulator:Vec::new()};
         visit::walk_crate(&mut name_finder, the_crate, ());
         name_finder.ident_accumulator
     }
 
-
     //fn expand_and_resolve(crate_str: @str) -> ast::crate {
         //let expanded_ast = expand_crate_str(crate_str);
         // println!("expanded: {:?}\n",expanded_ast);
@@ -1298,18 +1391,37 @@ macro_rules! outer ( ($e:pat ) => (inner!($e)))
     // but *shouldn't* bind because it was inserted by a different macro....
     // can't write this test case until we have macro-generating macros.
 
-    // FIXME #9383 : lambda var hygiene
-    // interesting... can't even write this test, yet, because the name-finder
-    // only finds pattern vars. Time to upgrade test framework.
-    /*#[test]
-    fn issue_9383(){
+    // item fn hygiene
+    // expands to fn q(x_1:int){fn g(x_2:int){x_2 + x_1};}
+    #[test] fn issue_9383(){
         run_renaming_test(
-            &("macro_rules! bad_macro (($ex:expr) => ({(|_x| { $ex }) (9) }))
-              fn takes_x(_x : int) { assert_eq!(bad_macro!(_x),8); }
-              fn main() { takes_x(8); }",
-              vec!(vec!()),false),
+            &("macro_rules! bad_macro (($ex:expr) => (fn g(x:int){ x + $ex }))
+              fn q(x:int) { bad_macro!(x); }",
+              vec!(vec!(1),vec!(0)),true),
             0)
-    }*/
+    }
+
+    // closure arg hygiene (ExprFnBlock)
+    // expands to fn f(){(|x_1 : int| {(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);}",
+              vec!(vec!(1)),
+              true),
+            0)
+    }
+
+    // closure arg hygiene (ExprProc)
+    // expands to fn f(){(proc(x_1 : int) {(x_2 + x_1)})(3);}
+    #[test] fn closure_arg_hygiene_2(){
+        run_renaming_test(
+            &("macro_rules! inject_x (()=>(x))
+              fn f(){ (proc(x : int){(inject_x!() + x)})(3); }",
+              vec!(vec!(1)),
+              true),
+            0)
+    }
 
     // run one of the renaming tests
     fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
@@ -1359,9 +1471,9 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
                         assert_eq!(varref_marks,binding_marks.clone());
                     }
                 } else {
+                    let varref_name = mtwt::resolve(varref.segments.get(0).identifier);
                     let fail = (varref.segments.len() == 1)
-                        && (mtwt::resolve(varref.segments.get(0).identifier)
-                            == binding_name);
+                        && (varref_name == binding_name);
                     // temp debugging:
                     if fail {
                         let varref_idents : Vec<ast::Ident>
@@ -1372,7 +1484,8 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
                         println!("text of test case: \"{}\"", teststr);
                         println!("");
                         println!("uh oh, matches but shouldn't:");
-                        println!("varref: {}",varref_idents);
+                        println!("varref #{}: {}, resolves to {}",idx, varref_idents,
+                                 varref_name);
                         // good lord, you can't make a path with 0 segments, can you?
                         let string = token::get_ident(varref.segments
                                                             .get(0)
@@ -1380,7 +1493,9 @@ fn run_renaming_test(t: &RenamingTest, test_idx: uint) {
                         println!("varref's first segment's uint: {}, and string: \"{}\"",
                                  varref.segments.get(0).identifier.name,
                                  string.get());
-                        println!("binding: {}", *bindings.get(binding_idx));
+                        println!("binding #{}: {}, resolves to {}",
+                                 binding_idx, *bindings.get(binding_idx),
+                                 binding_name);
                         mtwt::with_sctable(|x| mtwt::display_sctable(x));
                     }
                     assert!(!fail);
@@ -1443,13 +1558,43 @@ fn pat_idents(){
     // '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.
     #[test]
-    fn crate_idents(){
+    fn crate_bindings_test(){
         let the_crate = string_to_crate("fn main (a : int) -> int {|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 f_ident = token::str_to_ident("f");
+        let x_ident = token::str_to_ident("x");
+        let int_ident = token::str_to_ident("int");
+        let renames = vec!((x_ident,16));
+        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();
+        assert_eq!(resolved,vec!(f_ident.name,16,int_ident.name,16,16,16));
+    }
+
+    // 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 f_ident = token::str_to_ident("f");
+        let x_ident = token::str_to_ident("x");
+        let int_ident = token::str_to_ident("int");
+        let renames = vec!((x_ident,16));
+        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;
+        assert_eq!(resolved,vec!(f_ident.name,16,int_ident.name,16,x_name,x_name));
+    }
+
 
 }
index 48895d34022c4c28b9459b2c1b23d48102730073..18466e381a58ba2d5d4baa0984037ae35da5ae60 100644 (file)
@@ -54,38 +54,51 @@ pub enum SyntaxContext_ {
     IllegalCtxt
 }
 
+/// A list of ident->name renamings
+pub type RenameList = Vec<(Ident, Name)>;
+
 /// Extend a syntax context with a given mark
-pub fn new_mark(m: Mrk, tail: SyntaxContext) -> SyntaxContext {
-    with_sctable(|table| new_mark_internal(m, tail, table))
+pub fn apply_mark(m: Mrk, ctxt: SyntaxContext) -> SyntaxContext {
+    with_sctable(|table| apply_mark_internal(m, ctxt, table))
 }
 
-// Extend a syntax context with a given mark and table
-fn new_mark_internal(m: Mrk, tail: SyntaxContext, table: &SCTable) -> SyntaxContext {
-    let key = (tail, m);
+// Extend a syntax context with a given mark and sctable (explicit memoization)
+fn apply_mark_internal(m: Mrk, ctxt: SyntaxContext, table: &SCTable) -> SyntaxContext {
+    let key = (ctxt, m);
     let new_ctxt = |_: &(SyntaxContext, Mrk)|
-                   idx_push(&mut *table.table.borrow_mut(), Mark(m, tail));
+                   idx_push(&mut *table.table.borrow_mut(), Mark(m, ctxt));
 
     *table.mark_memo.borrow_mut().find_or_insert_with(key, new_ctxt)
 }
 
 /// Extend a syntax context with a given rename
-pub fn new_rename(id: Ident, to:Name,
-                  tail: SyntaxContext) -> SyntaxContext {
-    with_sctable(|table| new_rename_internal(id, to, tail, table))
+pub fn apply_rename(id: Ident, to:Name,
+                  ctxt: SyntaxContext) -> SyntaxContext {
+    with_sctable(|table| apply_rename_internal(id, to, ctxt, table))
 }
 
-// Extend a syntax context with a given rename and sctable
-fn new_rename_internal(id: Ident,
+// Extend a syntax context with a given rename and sctable (explicit memoization)
+fn apply_rename_internal(id: Ident,
                        to: Name,
-                       tail: SyntaxContext,
+                       ctxt: SyntaxContext,
                        table: &SCTable) -> SyntaxContext {
-    let key = (tail,id,to);
+    let key = (ctxt,id,to);
     let new_ctxt = |_: &(SyntaxContext, Ident, Mrk)|
-                   idx_push(&mut *table.table.borrow_mut(), Rename(id, to, tail));
+                   idx_push(&mut *table.table.borrow_mut(), Rename(id, to, ctxt));
 
     *table.rename_memo.borrow_mut().find_or_insert_with(key, new_ctxt)
 }
 
+/// Apply a list of renamings to a context
+// if these rename lists get long, it would make sense
+// to consider memoizing this fold. This may come up
+// when we add hygiene to item names.
+pub fn apply_renames(renames: &RenameList, ctxt: SyntaxContext) -> SyntaxContext {
+    renames.iter().fold(ctxt, |ctxt, &(from, to)| {
+        apply_rename(from, to, ctxt)
+    })
+}
+
 /// Fetch the SCTable from TLS, create one if it doesn't yet exist.
 pub fn with_sctable<T>(op: |&SCTable| -> T) -> T {
     local_data_key!(sctable_key: Rc<SCTable>)
@@ -263,9 +276,9 @@ fn xor_push(marks: &mut Vec<Mrk>, mark: Mrk) {
 
 #[cfg(test)]
 mod tests {
-    use ast::*;
-    use super::{resolve, xor_push, new_mark_internal, new_sctable_internal};
-    use super::{new_rename_internal, marksof_internal, resolve_internal};
+    use ast::{EMPTY_CTXT, Ident, Mrk, Name, SyntaxContext};
+    use super::{resolve, xor_push, apply_mark_internal, new_sctable_internal};
+    use super::{apply_rename_internal, apply_renames, marksof_internal, resolve_internal};
     use super::{SCTable, EmptyCtxt, Mark, Rename, IllegalCtxt};
     use std::collections::HashMap;
 
@@ -306,8 +319,8 @@ fn unfold_test_sc(tscs : Vec<TestSC> , tail: SyntaxContext, table: &SCTable)
         -> SyntaxContext {
         tscs.iter().rev().fold(tail, |tail : SyntaxContext, tsc : &TestSC|
                   {match *tsc {
-                      M(mrk) => new_mark_internal(mrk,tail,table),
-                      R(ident,name) => new_rename_internal(ident,name,tail,table)}})
+                      M(mrk) => apply_mark_internal(mrk,tail,table),
+                      R(ident,name) => apply_rename_internal(ident,name,tail,table)}})
     }
 
     // gather a SyntaxContext back into a vector of TestSCs
@@ -352,7 +365,7 @@ fn test_unfold_refold(){
     fn unfold_marks(mrks: Vec<Mrk> , tail: SyntaxContext, table: &SCTable)
                     -> SyntaxContext {
         mrks.iter().rev().fold(tail, |tail:SyntaxContext, mrk:&Mrk|
-                   {new_mark_internal(*mrk,tail,table)})
+                   {apply_mark_internal(*mrk,tail,table)})
     }
 
     #[test] fn unfold_marks_test() {
@@ -384,13 +397,13 @@ fn test_marksof () {
         // rename where stop doesn't match:
         { let chain = vec!(M(9),
                         R(id(name1,
-                             new_mark_internal (4, EMPTY_CTXT,&mut t)),
+                             apply_mark_internal (4, EMPTY_CTXT,&mut t)),
                           100101102),
                         M(14));
          let ans = unfold_test_sc(chain,EMPTY_CTXT,&mut t);
          assert_eq! (marksof_internal (ans, stopname, &t), vec!(9,14));}
         // rename where stop does match
-        { let name1sc = new_mark_internal(4, EMPTY_CTXT, &mut t);
+        { let name1sc = apply_mark_internal(4, EMPTY_CTXT, &mut t);
          let chain = vec!(M(9),
                        R(id(name1, name1sc),
                          stopname),
@@ -414,7 +427,7 @@ fn resolve_tests () {
         { let sc = unfold_test_sc(vec!(R(id(50,EMPTY_CTXT),51),M(12)),EMPTY_CTXT,&mut t);
          assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),a);}
         // - rename where names do match, but marks don't
-        { let sc1 = new_mark_internal(1,EMPTY_CTXT,&mut t);
+        { let sc1 = apply_mark_internal(1,EMPTY_CTXT,&mut t);
          let sc = unfold_test_sc(vec!(R(id(a,sc1),50),
                                    M(1),
                                    M(2)),
@@ -437,11 +450,11 @@ fn resolve_tests () {
                                   EMPTY_CTXT,&mut t);
          assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt), 51); }
         // the simplest double-rename:
-        { let a_to_a50 = new_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t);
-         let a50_to_a51 = new_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
+        { let a_to_a50 = apply_rename_internal(id(a,EMPTY_CTXT),50,EMPTY_CTXT,&mut t);
+         let a50_to_a51 = apply_rename_internal(id(a,a_to_a50),51,a_to_a50,&mut t);
          assert_eq!(resolve_internal(id(a,a50_to_a51),&mut t, &mut rt),51);
          // mark on the outside doesn't stop rename:
-         let sc = new_mark_internal(9,a50_to_a51,&mut t);
+         let sc = apply_mark_internal(9,a50_to_a51,&mut t);
          assert_eq!(resolve_internal(id(a,sc),&mut t, &mut rt),51);
          // but mark on the inside does:
          let a50_to_a51_b = unfold_test_sc(vec!(R(id(a,a_to_a50),51),
@@ -461,10 +474,10 @@ fn mtwt_resolve_test(){
     #[test]
     fn hashing_tests () {
         let mut t = new_sctable_internal();
-        assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
-        assert_eq!(new_mark_internal(13,EMPTY_CTXT,&mut t),3);
+        assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),2);
+        assert_eq!(apply_mark_internal(13,EMPTY_CTXT,&mut t),3);
         // using the same one again should result in the same index:
-        assert_eq!(new_mark_internal(12,EMPTY_CTXT,&mut t),2);
+        assert_eq!(apply_mark_internal(12,EMPTY_CTXT,&mut t),2);
         // I'm assuming that the rename table will behave the same....
     }
 
@@ -480,4 +493,13 @@ fn resolve_table_hashing_tests() {
         resolve_internal(id(30,EMPTY_CTXT),&mut t, &mut rt);
         assert_eq!(rt.len(),2);
     }
+
+    #[test]
+    fn new_resolves_test() {
+        let renames = vec!((Ident{name:23,ctxt:EMPTY_CTXT},24),
+                           (Ident{name:29,ctxt:EMPTY_CTXT},29));
+        let new_ctxt1 = apply_renames(&renames,EMPTY_CTXT);
+        assert_eq!(resolve(Ident{name:23,ctxt:new_ctxt1}),24);
+        assert_eq!(resolve(Ident{name:29,ctxt:new_ctxt1}),29);
+    }
 }
index f9d7078da3dcbe4377132259c3c5699ae66fca36..04e6612daf1f008a7dcf3a7f4526fb23875dd2f9 100644 (file)
@@ -794,7 +794,7 @@ pub fn noop_fold_pat<T: Folder>(p: Gc<Pat>, folder: &mut T) -> Gc<Pat> {
         PatIdent(binding_mode, ref pth1, ref sub) => {
             PatIdent(binding_mode,
                      Spanned{span: folder.new_span(pth1.span),
-                                       node: folder.fold_ident(pth1.node)},
+                             node: folder.fold_ident(pth1.node)},
                      sub.map(|x| folder.fold_pat(x)))
         }
         PatLit(e) => PatLit(folder.fold_expr(e)),
index a93e8270d9866c0b12fe791ab2127411b3a43ffb..dcf37e37ff0a786f0bc40882052ec6efac5bf7d9 100644 (file)
@@ -765,7 +765,7 @@ mod test {
     use ext::mtwt;
 
     fn mark_ident(id : ast::Ident, m : ast::Mrk) -> ast::Ident {
-        ast::Ident{name:id.name,ctxt:mtwt::new_mark(m,id.ctxt)}
+        ast::Ident{name:id.name,ctxt:mtwt::apply_mark(m,id.ctxt)}
     }
 
     #[test] fn mtwt_token_eq_test() {