auto merge of #15425 : jbclements/rust/hygiene-for-3-kinds-of-args, r=cmr
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.
This commit is contained in:
commit
29d6a8ecc6
8 changed files with 297 additions and 123 deletions
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -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 @@ mod bench {
|
|||
|
||||
#[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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ impl fmt::Show for Onceness {
|
|||
}
|
||||
}
|
||||
|
||||
/// 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 @@ impl 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
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ use parse::parser;
|
|||
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 @@ impl 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 {
|
||||
|
|
|
|||
|
|
@ -21,6 +21,7 @@ use codemap;
|
|||
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 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
|
|||
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 @@ impl<'a, 'b> Folder for MacroExpander<'a, 'b> {
|
|||
}
|
||||
}
|
||||
|
||||
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 @@ impl Folder for Marker {
|
|||
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 @@ mod test {
|
|||
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 @@ mod test {
|
|||
|
||||
// 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 @@ mod test {
|
|||
// 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 @@ mod test {
|
|||
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 @@ mod test {
|
|||
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 @@ mod test {
|
|||
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 @@ foo_module!()
|
|||
// '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));
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 @@ mod tests {
|
|||
-> 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 @@ mod tests {
|
|||
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 @@ mod tests {
|
|||
// 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 @@ mod 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 @@ mod 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 @@ mod tests {
|
|||
#[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 @@ mod 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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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)),
|
||||
|
|
|
|||
|
|
@ -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() {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue