awesome new bug! added test case

This commit is contained in:
John Clements 2013-07-12 18:35:47 -07:00
parent 7b548e7180
commit dc7f3df27f

View file

@ -420,7 +420,6 @@ pub fn expand_item_mac(extsbox: @mut SyntaxEnv,
span: span
}
});
let fm = fresh_mark();
// mark before expansion:
let marked_tts = mark_tts(tts,fm);
let marked_ctxt = new_mark(fm,ctxt);
@ -1533,7 +1532,7 @@ mod test {
use super::*;
use ast;
use ast::{Attribute_, AttrOuter, MetaWord, EMPTY_CTXT};
use ast_util::{get_sctable, mtwt_resolve, new_rename};
use ast_util::{get_sctable, mtwt_marksof, mtwt_resolve, new_rename};
use codemap;
use codemap::Spanned;
use parse;
@ -1709,31 +1708,40 @@ mod test {
//
// The comparisons are done post-mtwt-resolve, so we're comparing renamed
// names; differences in marks don't matter any more.
type renaming_test = (&'static str, ~[~[uint]]);
//
// oog... I also want tests that check "binding-identifier-=?". That is,
// not just "do these have the same name", but "do they have the same
// name *and* the same marks"? Understanding this is really pretty painful.
// in principle, you might want to control this boolean on a per-varref basis,
// but that would make things even harder to understand, and might not be
// necessary for thorough testing.
type renaming_test = (&'static str, ~[~[uint]], bool);
#[test]
fn automatic_renaming () {
// need some other way to test these...
let tests : ~[renaming_test] =
~[// b & c should get new names throughout, in the expr too:
("fn a() -> int { let b = 13; let c = b; b+c }",
~[~[0,1],~[2]]),
~[~[0,1],~[2]], false),
// both x's should be renamed (how is this causing a bug?)
("fn main () {let x : int = 13;x;}",
~[~[0]]),
~[~[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)}",
~[~[1]]),
~[~[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)}",
~[~[1]]),
~[~[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)}",
~[~[0]])
~[~[0]], false),
// can't believe I missed this one : a macro def that refers to a local var:
("fn main() {let x = 19; macro_rules! getx(()=>(x)); getx!();}",
~[~[0]], true)
// FIXME #6994: the next string exposes the bug referred to in issue 6994, so I'm
// commenting it out.
// the z flows into and out of two macros (g & f) along one path, and one
@ -1750,10 +1758,10 @@ mod test {
}
}
// run one of the renaming tests
fn run_renaming_test(t : &renaming_test) {
let (teststr, bound_connections) = match *t {
(ref str,ref conns) => (str.to_managed(), conns.clone())
let (teststr, bound_connections, bound_ident_check) = match *t {
(ref str,ref conns, bic) => (str.to_managed(), conns.clone(), bic)
};
let cr = expand_crate_str(teststr.to_managed());
// find the bindings:
@ -1766,15 +1774,18 @@ mod test {
assert_eq!(bindings.len(),bound_connections.len());
for (binding_idx,shouldmatch) in bound_connections.iter().enumerate() {
let binding_name = mtwt_resolve(bindings[binding_idx]);
let binding_marks = mtwt_marksof(bindings[binding_idx].ctxt,binding_name);
// shouldmatch can't name varrefs that don't exist:
assert!((shouldmatch.len() == 0) ||
(varrefs.len() > *shouldmatch.iter().max().unwrap()));
for (idx,varref) in varrefs.iter().enumerate() {
if shouldmatch.contains(&idx) {
// it should be a path of length 1, and it should
// be free-identifier=? to the given binding
// be free-identifier=? or bound-identifier=? to the given binding
assert_eq!(varref.segments.len(),1);
let varref_name = mtwt_resolve(varref.segments[0].identifier);
let varref_marks = mtwt_marksof(varref.segments[0].identifier.ctxt,
binding_name);
if (!(varref_name==binding_name)){
std::io::println("uh oh, should match but doesn't:");
std::io::println(fmt!("varref: %?",varref));
@ -1786,6 +1797,10 @@ mod test {
}
}
assert_eq!(varref_name,binding_name);
if (bound_ident_check) {
// we need to check the marks, too:
assert_eq!(varref_marks,binding_marks.clone());
}
} else {
let fail = (varref.segments.len() == 1)
&& (mtwt_resolve(varref.segments[0].identifier) == binding_name);
@ -1854,6 +1869,46 @@ mod test {
};
}
#[test] fn fmt_in_macro_used_inside_module_macro() {
let crate_str = @"macro_rules! fmt_wrap(($b:expr)=>(fmt!(\"left: %?\", $b)))
macro_rules! foo_module (() => (mod generated { fn a() { let xx = 147; fmt_wrap!(xx);}}))
foo_module!()
";
let cr = expand_crate_str(crate_str);
// find the xx binding
let bindings = @mut ~[];
visit::walk_crate(&mut new_name_finder(bindings), cr, ());
let cxbinds : ~[&ast::Ident] =
bindings.iter().filter(|b|{@"xx" == (ident_to_str(*b))}).collect();
let cxbind = match cxbinds {
[b] => b,
_ => fail!("expected just one binding for ext_cx")
};
let resolved_binding = mtwt_resolve(*cxbind);
// find all the xx varrefs:
let varrefs = @mut ~[];
visit::walk_crate(&mut new_path_finder(varrefs), cr, ());
// the xx binding should bind all of the xx varrefs:
for (idx,v) in varrefs.iter().filter(|p|{ p.segments.len() == 1
&& (@"xx" == (ident_to_str(&p.segments[0].identifier)))
}).enumerate() {
if (mtwt_resolve(v.segments[0].identifier) != resolved_binding) {
std::io::println("uh oh, xx binding didn't match xx varref:");
std::io::println(fmt!("this is xx varref # %?",idx));
std::io::println(fmt!("binding: %?",cxbind));
std::io::println(fmt!("resolves to: %?",resolved_binding));
std::io::println(fmt!("varref: %?",v.segments[0].identifier));
std::io::println(fmt!("resolves to: %?",mtwt_resolve(v.segments[0].identifier)));
let table = get_sctable();
std::io::println("SC table:");
for (idx,val) in table.table.iter().enumerate() {
std::io::println(fmt!("%4u : %?",idx,val));
}
}
assert_eq!(mtwt_resolve(v.segments[0].identifier),resolved_binding);
};
}
#[test]
fn pat_idents(){
let pat = string_to_pat(@"(a,Foo{x:c @ (b,9),y:Bar(4,d)})");