Merge pull request #173 from mystor/macroinput

Remove the MacroInput type alias
diff --git a/src/expr.rs b/src/expr.rs
index 2b73998..79a4b56 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -1098,6 +1098,26 @@
         syn!(ExprRepeat) => { ExprKind::Repeat }
     ));
 
+    named!(expr_nosemi -> Expr, map!(alt!(
+        syn!(ExprIf) => { ExprKind::If }
+        |
+        syn!(ExprIfLet) => { ExprKind::IfLet }
+        |
+        syn!(ExprWhile) => { ExprKind::While }
+        |
+        syn!(ExprWhileLet) => { ExprKind::WhileLet }
+        |
+        syn!(ExprForLoop) => { ExprKind::ForLoop }
+        |
+        syn!(ExprLoop) => { ExprKind::Loop }
+        |
+        syn!(ExprMatch) => { ExprKind::Match }
+        |
+        syn!(ExprCatch) => { ExprKind::Catch }
+        |
+        syn!(ExprBlock) => { ExprKind::Block }
+    ), Expr::from));
+
     impl Synom for ExprGroup {
         named!(parse -> Self, do_parse!(
             e: grouped!(syn!(Expr)) >>
@@ -1665,6 +1685,8 @@
             |
             stmt_item
             |
+            stmt_blockexpr
+            |
             stmt_expr
         ));
     }
@@ -1716,34 +1738,31 @@
 
     named!(stmt_item -> Stmt, map!(syn!(Item), |i| Stmt::Item(Box::new(i))));
 
-    fn requires_semi(e: &Expr) -> bool {
-        match e.node {
-            ExprKind::If(_) |
-            ExprKind::IfLet(_) |
-            ExprKind::While(_) |
-            ExprKind::WhileLet(_) |
-            ExprKind::ForLoop(_) |
-            ExprKind::Loop(_) |
-            ExprKind::Match(_) |
-            ExprKind::Block(_) => false,
-
-            _ => true,
-        }
-    }
+    named!(stmt_blockexpr -> Stmt, do_parse!(
+        attrs: many0!(call!(Attribute::parse_outer)) >>
+        mut e: expr_nosemi >>
+        // If the next token is a `.` or a `?` it is special-cased to parse as
+        // an expression instead of a blockexpression.
+        not!(syn!(Dot)) >>
+        not!(syn!(Question)) >>
+        semi: option!(syn!(Semi)) >>
+        ({
+            e.attrs = attrs;
+            if let Some(semi) = semi {
+                Stmt::Semi(Box::new(e), semi)
+            } else {
+                Stmt::Expr(Box::new(e))
+            }
+        })
+    ));
 
     named!(stmt_expr -> Stmt, do_parse!(
         attrs: many0!(call!(Attribute::parse_outer)) >>
         mut e: syn!(Expr) >>
-        semi: option!(syn!(Semi)) >>
+        semi: syn!(Semi) >>
         ({
             e.attrs = attrs;
-            if let Some(s) = semi {
-                Stmt::Semi(Box::new(e), s)
-            } else if requires_semi(&e) {
-                return parse_error();
-            } else {
-                Stmt::Expr(Box::new(e))
-            }
+            Stmt::Semi(Box::new(e), semi)
         })
     ));
 
diff --git a/src/fold.rs b/src/fold.rs
index 59dd73e..3dae69c 100644
--- a/src/fold.rs
+++ b/src/fold.rs
@@ -599,10 +599,10 @@
     use proc_macro2::{TokenKind, TokenTree as TokenTree2};
     match tt.0.kind {
         TokenKind::Word(sym) => {
-            let sym = folder.fold_ident(Ident::new(sym, Span(tt.0.span)));
+            // XXX: Do we want to fold over idents
             TokenTree(TokenTree2 {
-                span: sym.span.0,
-                kind: TokenKind::Word(sym.sym),
+                span: tt.0.span,
+                kind: TokenKind::Word(sym),
             })
         }
         TokenKind::Op(..) => tt,
@@ -837,6 +837,7 @@
             }
             MethodCall(e) => {
                 MethodCall(ExprMethodCall {
+                    expr: e.expr.lift(|e| folder.fold_expr(e)),
                     method: folder.fold_ident(e.method),
                     typarams: e.typarams.lift(|t| folder.fold_ty(t)),
                     args: e.args.lift(|e| folder.fold_expr(e)),
diff --git a/tests/common/mod.rs b/tests/common/mod.rs
new file mode 100644
index 0000000..fcb8c61
--- /dev/null
+++ b/tests/common/mod.rs
@@ -0,0 +1,85 @@
+extern crate walkdir;
+
+use walkdir::DirEntry;
+use std::env;
+use std::u32;
+
+macro_rules! errorf {
+    ($($tt:tt)*) => {
+        {
+            use ::std::io::Write;
+            write!(::std::io::stderr(), $($tt)*).unwrap();
+        }
+    };
+}
+
+pub mod parse;
+pub mod respan;
+
+pub fn check_min_stack() {
+    let min_stack_value = env::var("RUST_MIN_STACK")
+        .expect("RUST_MIN_STACK env var should be set since some tests require it.");
+    let min_stack_value: usize = min_stack_value.parse()
+        .expect("RUST_MIN_STACK env var should be set since some tests require it.");
+    assert!(min_stack_value >= 16000000);
+}
+
+/// Read the `ABORT_AFTER_FAILURE` environment variable, and parse it.
+pub fn abort_after() -> u32 {
+    if let Ok(s) = env::var("ABORT_AFTER_FAILURE") {
+        if let Ok(n) = s.parse::<u32>() {
+            return n;
+        }
+    }
+    u32::MAX
+}
+
+pub fn base_dir_filter(entry: &DirEntry) -> bool {
+    let path = entry.path();
+    if path.is_dir() {
+        return true; // otherwise walkdir does not visit the files
+    }
+    if path.extension().map(|e| e != "rs").unwrap_or(true) {
+        return false;
+    }
+    let path_string = path.to_string_lossy();
+    let path_string = if cfg!(windows) {
+        path_string.replace('\\', "/").into()
+    } else {
+        path_string
+    };
+    // TODO assert that parsing fails on the parse-fail cases
+    if path_string.starts_with("tests/rust/src/test/parse-fail") ||
+       path_string.starts_with("tests/rust/src/test/compile-fail") {
+        return false;
+    }
+
+    if path_string.starts_with("tests/rust/src/test/ui") {
+        let stderr_path = path.with_extension("stderr");
+        if stderr_path.exists() {
+            // Expected to fail in some way
+            return false;
+        }
+    }
+
+    match path_string.as_ref() {
+        // TODO better support for attributes
+        "tests/rust/src/librustc_data_structures/blake2b.rs" |
+        // TODO better support for attributes
+        "tests/rust/src/test/incremental/hashes/enum_defs.rs" |
+        // TODO better support for attributes
+        "tests/rust/src/test/pretty/stmt_expr_attributes.rs" |
+        // not actually a test case
+        "tests/rust/src/test/run-pass/auxiliary/macro-include-items-expr.rs" |
+        // TODO better support for attributes
+        "tests/rust/src/test/run-pass/cfg_stmt_expr.rs" |
+        // TODO weird glob import
+        "tests/rust/src/test/run-pass/import-glob-crate.rs" |
+        // TODO better support for attributes
+        "tests/rust/src/test/run-pass/inner-attrs-on-impl.rs" |
+        // TODO better support for attributes
+        "tests/rust/src/test/run-pass/item-attributes.rs" => false,
+        _ => true,
+    }
+}
+
diff --git a/tests/common/parse.rs b/tests/common/parse.rs
new file mode 100644
index 0000000..15c2faf
--- /dev/null
+++ b/tests/common/parse.rs
@@ -0,0 +1,47 @@
+extern crate syn;
+extern crate syntex_syntax;
+
+use self::syntex_syntax::ast;
+use self::syntex_syntax::ptr::P;
+use self::syntex_syntax::parse::{self, ParseSess};
+use self::syntex_syntax::codemap::FilePathMapping;
+
+use std::panic;
+
+pub fn syntex_expr(input: &str) -> Option<P<ast::Expr>> {
+    match panic::catch_unwind(|| {
+        let sess = ParseSess::new(FilePathMapping::empty());
+        sess.span_diagnostic.set_continue_after_error(false);
+        let e = parse::parse_expr_from_source_str(
+            "test_precedence".to_string(),
+            input.to_string(),
+            &sess,
+        );
+        Some(match e {
+            Ok(expr) => expr,
+            Err(mut diagnostic) => {
+                diagnostic.emit();;
+                return None
+            }
+        })
+    }) {
+        Ok(Some(e)) => Some(e),
+        Ok(None) => {
+            None
+        }
+        Err(_) => {
+            errorf!("syntex paniced\n");
+            None
+        }
+    }
+}
+
+pub fn syn_expr(input: &str) -> Option<syn::Expr> {
+    match input.parse::<syn::Expr>() {
+        Ok(e) => Some(e),
+        Err(msg) => {
+            errorf!("syn failed to parse\n{:?}\n", msg);
+            None
+        }
+    }
+}
diff --git a/tests/common/respan.rs b/tests/common/respan.rs
new file mode 100644
index 0000000..c9d12f5
--- /dev/null
+++ b/tests/common/respan.rs
@@ -0,0 +1,245 @@
+use std::rc::Rc;
+use syntex_syntax::ast::{Attribute, Expr, ExprKind, Field, FnDecl, FunctionRetTy, ImplItem,
+                         ImplItemKind, ItemKind, Mac, MetaItem, MetaItemKind, MethodSig,
+                         NestedMetaItem, NestedMetaItemKind, TraitItem, TraitItemKind, TyParam,
+                         Visibility};
+use syntex_syntax::codemap::{self, Spanned};
+use syntex_syntax::fold::{self, Folder};
+use syntex_syntax::parse::token::{Lit, Token};
+use syntex_syntax::ptr::P;
+use syntex_syntax::symbol::Symbol;
+use syntex_syntax::tokenstream::{Delimited, TokenTree};
+use syntex_syntax::util::move_map::MoveMap;
+use syntex_syntax::util::small_vector::SmallVector;
+
+use syntex_pos::{Span, DUMMY_SP};
+use syntex_syntax::ast;
+
+struct Respanner;
+
+impl Respanner {
+    fn fold_spanned<T>(&mut self, spanned: Spanned<T>) -> Spanned<T> {
+        codemap::respan(self.new_span(spanned.span), spanned.node)
+    }
+
+    fn fold_lit(&mut self, l: Lit) -> Lit {
+        // Give up on comparing literals inside of macros because there are
+        // so many equivalent representations of the same literal; they are
+        // tested elsewhere
+        match l {
+            Lit::Byte(_) => Lit::Byte(Symbol::intern("")),
+            Lit::Char(_) => Lit::Char(Symbol::intern("")),
+            Lit::Integer(_) => Lit::Integer(Symbol::intern("")),
+            Lit::Float(_) => Lit::Float(Symbol::intern("")),
+            Lit::Str_(_) => Lit::Str_(Symbol::intern("")),
+            Lit::ByteStr(_) => Lit::ByteStr(Symbol::intern("")),
+            _ => l,
+        }
+    }
+}
+
+impl Folder for Respanner {
+    fn new_span(&mut self, _: Span) -> Span {
+        DUMMY_SP
+    }
+
+    fn fold_item_kind(&mut self, i: ItemKind) -> ItemKind {
+        match i {
+            ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
+                let generics = self.fold_generics(generics);
+                let decl = self.fold_fn_decl(decl);
+                let body = self.fold_block(body);
+                // default fold_item_kind does not fold this span
+                let constness = self.fold_spanned(constness);
+                ItemKind::Fn(decl, unsafety, constness, abi, generics, body)
+            }
+            _ => fold::noop_fold_item_kind(i, self),
+        }
+    }
+
+    fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
+        e.map(|e| {
+            let folded = fold::noop_fold_expr(e, self);
+            Expr {
+                node: match folded.node {
+                    ExprKind::Lit(l) => {
+                        // default fold_expr does not fold lits
+                        ExprKind::Lit(l.map(|l| self.fold_spanned(l)))
+                    }
+                    ExprKind::Binary(op, lhs, rhs) => {
+                        // default fold_expr does not fold the op span
+                        ExprKind::Binary(self.fold_spanned(op),
+                                            self.fold_expr(lhs),
+                                            self.fold_expr(rhs))
+                    }
+                    ExprKind::AssignOp(op, lhs, rhs) => {
+                        // default fold_expr does not fold the op span
+                        ExprKind::AssignOp(self.fold_spanned(op),
+                                            self.fold_expr(lhs),
+                                            self.fold_expr(rhs))
+                    }
+                    other => other,
+                },
+                ..folded
+            }
+        })
+    }
+
+    fn fold_ty_param(&mut self, tp: TyParam) -> TyParam {
+        TyParam {
+            // default fold_ty_param does not fold the span
+            span: self.new_span(tp.span),
+            ..fold::noop_fold_ty_param(tp, self)
+        }
+    }
+
+    fn fold_fn_decl(&mut self, decl: P<FnDecl>) -> P<FnDecl> {
+        decl.map(|FnDecl { inputs, output, variadic }| {
+            FnDecl {
+                inputs: inputs.move_map(|x| self.fold_arg(x)),
+                output: match output {
+                    FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(self.fold_ty(ty)),
+                    // default fold_fn_decl does not fold this span
+                    FunctionRetTy::Default(span) => FunctionRetTy::Default(self.new_span(span)),
+                },
+                variadic: variadic,
+            }
+        })
+    }
+
+    fn fold_field(&mut self, field: Field) -> Field {
+        Field {
+            ident: codemap::respan(// default fold_field does not fold this span
+                                    self.new_span(field.ident.span),
+                                    self.fold_ident(field.ident.node)),
+            expr: self.fold_expr(field.expr),
+            span: self.new_span(field.span),
+            is_shorthand: field.is_shorthand,
+            attrs: ast::ThinVec::new(),
+        }
+    }
+
+    fn fold_trait_item(&mut self, i: TraitItem) -> SmallVector<TraitItem> {
+        let noop = fold::noop_fold_trait_item(i, self).expect_one("");
+        SmallVector::one(TraitItem {
+                                node: match noop.node {
+                                    TraitItemKind::Method(sig, body) => {
+                    TraitItemKind::Method(MethodSig {
+                                                constness: self.fold_spanned(sig.constness),
+                                                ..sig
+                                            },
+                                            body)
+                }
+                                    node => node,
+                                },
+                                ..noop
+                            })
+    }
+
+    fn fold_impl_item(&mut self, i: ImplItem) -> SmallVector<ImplItem> {
+        let noop = fold::noop_fold_impl_item(i, self).expect_one("");
+        SmallVector::one(ImplItem {
+                                node: match noop.node {
+                                    ImplItemKind::Method(sig, body) => {
+                    ImplItemKind::Method(MethodSig {
+                                                constness: self.fold_spanned(sig.constness),
+                                                ..sig
+                                            },
+                                            body)
+                }
+                                    node => node,
+                                },
+                                ..noop
+                            })
+    }
+
+    fn fold_attribute(&mut self, mut at: Attribute) -> Option<Attribute> {
+        at.id.0 = 0;
+        fold::noop_fold_attribute(at, self)
+    }
+
+    fn fold_meta_item(&mut self, meta_item: MetaItem) -> MetaItem {
+        let MetaItem { name, node, span } = meta_item;
+        MetaItem {
+            name: name,
+            node: match node {
+                MetaItemKind::Word => MetaItemKind::Word,
+                MetaItemKind::List(nested) => {
+                    MetaItemKind::List(nested.move_map(|e| self.fold_meta_list_item(e)))
+                }
+                // default fold_meta_item does not fold the value span
+                MetaItemKind::NameValue(lit) => MetaItemKind::NameValue(self.fold_spanned(lit)),
+            },
+            span: self.new_span(span),
+        }
+    }
+
+    fn fold_meta_list_item(&mut self, list_item: NestedMetaItem) -> NestedMetaItem {
+        Spanned {
+            node: match list_item.node {
+                NestedMetaItemKind::MetaItem(mi) => {
+                    NestedMetaItemKind::MetaItem(self.fold_meta_item(mi))
+                }
+                // default fold_meta_list_item does not fold the span
+                NestedMetaItemKind::Literal(lit) => {
+                    NestedMetaItemKind::Literal(self.fold_spanned(lit))
+                }
+            },
+            span: self.new_span(list_item.span),
+        }
+    }
+
+    fn fold_mac(&mut self, mac: Mac) -> Mac {
+        fold::noop_fold_mac(mac, self)
+    }
+
+    fn fold_tt(&mut self, tt: TokenTree) -> TokenTree {
+        match tt {
+            TokenTree::Token(span, ref tok) => {
+                TokenTree::Token(self.new_span(span), self.fold_token(tok.clone()))
+            }
+            TokenTree::Delimited(span, ref delimed) => {
+                TokenTree::Delimited(self.new_span(span),
+                                        Delimited {
+                                            delim: delimed.delim,
+                                            tts: self.fold_tts(delimed.tts.clone().into()).into(),
+                                        })
+            }
+        }
+    }
+
+    fn fold_token(&mut self, t: Token) -> Token {
+        match t {
+            // default fold_token does not fold literals
+            Token::Literal(lit, repr) => Token::Literal(self.fold_lit(lit), repr),
+            Token::Ident(id) => Token::Ident(self.fold_ident(id)),
+            Token::Lifetime(id) => Token::Lifetime(self.fold_ident(id)),
+            Token::Interpolated(nt) => {
+                let nt = match Rc::try_unwrap(nt) {
+                    Ok(nt) => nt,
+                    Err(nt) => (*nt).clone(),
+                };
+                Token::Interpolated(Rc::new(self.fold_interpolated(nt)))
+            }
+            Token::SubstNt(ident) => Token::SubstNt(self.fold_ident(ident)),
+            _ => t,
+        }
+    }
+
+    fn fold_vis(&mut self, vis: Visibility) -> Visibility {
+        match vis {
+            Visibility::Crate(span) => Visibility::Crate(self.new_span(span)),
+            _ => fold::noop_fold_vis(vis, self),
+        }
+    }
+}
+
+#[allow(dead_code)]
+pub fn respan_crate(krate: ast::Crate) -> ast::Crate {
+    Respanner.fold_crate(krate)
+}
+
+#[allow(dead_code)]
+pub fn respan_expr(expr: P<ast::Expr>) -> P<ast::Expr> {
+    Respanner.fold_expr(expr)
+}
diff --git a/tests/test_precedence.rs b/tests/test_precedence.rs
new file mode 100644
index 0000000..a97f622
--- /dev/null
+++ b/tests/test_precedence.rs
@@ -0,0 +1,378 @@
+#![cfg(all(feature = "full", feature = "fold"))]
+
+//! The tests in this module do the following:
+//!
+//! 1. Parse a given expression in both `syn` and `syntex`.
+//! 2. Fold over the expression adding brackets around each subexpression (with
+//!    some complications - see the `syn_brackets` and `syntex_brackets`
+//!    methods).
+//! 3. Serialize the `syn` expression back into a string, and re-parse it with
+//!    `syntex`.
+//! 4. Respan all of the expressions, replacing the spans with the default spans.
+//! 5. Compare the expressions with one another, if they are not equal fail.
+
+#[macro_use]
+extern crate quote;
+extern crate syn;
+extern crate synom;
+extern crate syntex_pos;
+extern crate syntex_syntax;
+extern crate walkdir;
+extern crate proc_macro2;
+
+use syntex_syntax::ast;
+use syntex_syntax::ptr::P;
+
+use common::{respan, parse};
+
+#[allow(dead_code)]
+#[macro_use]
+mod common;
+
+/// Test some pre-set expressions chosen by us.
+#[test]
+fn test_simple_precedence() {
+    const EXPRS: &[&str] = &[
+        "1 + 2 * 3 + 4",
+        "1 + 2 * ( 3 + 4 )",
+        "{ for i in r { } *some_ptr += 1; }",
+        "{ loop { break 5; } }",
+        "{ if true { () }.mthd() }",
+    ];
+
+    let mut failed = 0;
+
+    for input in EXPRS {
+        let expr = if let Some(expr) = parse::syn_expr(input) {
+            expr
+        } else {
+            failed += 1;
+            continue;
+        };
+
+        let pf = match test_expressions(vec![expr]) {
+            (1, 0) => "passed",
+            (0, 1) => {
+                failed += 1;
+                "failed"
+            }
+            _ => unreachable!(),
+        };
+        errorf!("=== {}: {}\n", input, pf);
+    }
+
+    if failed > 0 {
+        panic!("Failed {} tests", failed);
+    }
+}
+
+/// Test expressions from rustc, like in `test_round_trip`.
+#[test]
+fn test_rustc_precedence() {
+    use walkdir::{WalkDir, WalkDirIterator};
+    use std::fs::File;
+    use std::io::Read;
+
+    common::check_min_stack();
+    let abort_after = common::abort_after();
+    if abort_after == 0 {
+        panic!("Skipping all precedence tests");
+    }
+
+    let mut passed = 0;
+    let mut failed = 0;
+
+    let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
+    for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
+        let entry = entry.unwrap();
+
+        let path = entry.path();
+        if path.is_dir() {
+            continue;
+        }
+
+        let mut file = File::open(path).unwrap();
+        let mut content = String::new();
+        file.read_to_string(&mut content).unwrap();
+
+        let (l_passed, l_failed) = match content.parse::<syn::Crate>() {
+            Ok(krate) => {
+                let exprs = collect_exprs(krate);
+                test_expressions(exprs)
+            }
+            Err(msg) => {
+                errorf!("syn failed to parse\n{:?}\n", msg);
+                failed += 1;
+                (0, 1)
+            }
+        };
+
+        passed += l_passed;
+        failed += l_failed;
+
+        errorf!("=== {}: {} passed | {} failed\n", path.display(), l_passed, l_failed);
+
+        if failed >= abort_after {
+            errorf!("Aborting Immediately due to ABORT_AFTER_FAILURE\n");
+            break;
+        }
+    }
+
+    errorf!("\n===== Precedence Test Results =====\n");
+    errorf!("{} passed | {} failed\n", passed, failed);
+
+
+    if failed > 0 {
+        panic!("{} failures", failed);
+    }
+}
+
+fn test_expressions(exprs: Vec<syn::Expr>) -> (u32, u32) {
+    let mut passed = 0;
+    let mut failed = 0;
+
+    for expr in exprs {
+        let raw = quote!(#expr).to_string();
+
+        let syntex_ast = if let Some(e) = syntex_parse_and_rewrite(&raw) {
+            e
+        } else {
+            failed += 1;
+            errorf!("\nFAIL - syntex failed to parse raw\n");
+            continue;
+        };
+
+        let syn_expr = syn_brackets(expr);
+        let syn_ast = if let Some(e) = parse::syntex_expr(&quote!(#syn_expr).to_string()) {
+            e
+        } else {
+            failed += 1;
+            errorf!("\nFAIL - syntex failed to parse bracketed\n");
+            continue;
+        };
+
+        let syn_ast = respan::respan_expr(syn_ast);
+        let syntex_ast = respan::respan_expr(syntex_ast);
+
+        if syn_ast == syntex_ast {
+            passed += 1;
+        } else {
+            failed += 1;
+            errorf!("\nFAIL\n{:?}\n!=\n{:?}\n", syn_ast, syntex_ast);
+        }
+    }
+
+    (passed, failed)
+}
+
+fn syntex_parse_and_rewrite(input: &str) -> Option<P<ast::Expr>> {
+    parse::syntex_expr(input).and_then(|e| syntex_brackets(e))
+}
+
+/// Wrap every expression which is not already wrapped in parens with parens, to
+/// reveal the precidence of the parsed expressions, and produce a stringified form
+/// of the resulting expression.
+///
+/// This method operates on syntex objects.
+fn syntex_brackets(syntex_expr: P<ast::Expr>) -> Option<P<ast::Expr>> {
+    use syntex_syntax::ast::{Expr, ExprKind, Mac, Stmt, StmtKind, Pat, Ty, Field};
+    use syntex_syntax::fold::{self, Folder};
+    use syntex_syntax::util::ThinVec;
+    use syntex_syntax::util::small_vector::SmallVector;
+    use syntex_syntax::ext::quote::rt::DUMMY_SP;
+    use syntex_syntax::codemap;
+
+    fn expr(node: ExprKind) -> P<Expr> {
+        P(Expr {
+            id: ast::DUMMY_NODE_ID,
+            node: node,
+            span: DUMMY_SP,
+            attrs: ThinVec::new(),
+        })
+    }
+
+    struct BracketsFolder {
+        failed: bool,
+    };
+    impl Folder for BracketsFolder {
+        fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
+            e.map(|e| {
+                Expr {
+                    node: match e.node {
+                        ExprKind::Paren(inner) => {
+                            ExprKind::Paren(inner.map(|e| {
+                                fold::noop_fold_expr(e, self)
+                            }))
+                        }
+                        ExprKind::If(..) |
+                        ExprKind::Block(..) |
+                        ExprKind::IfLet(..) => {
+                            return fold::noop_fold_expr(e, self);
+                        }
+                        node => {
+                            ExprKind::Paren(expr(node).map(|e| {
+                                fold::noop_fold_expr(e, self)
+                            }))
+                        }
+                    },
+                    ..e
+                }
+            })
+        }
+
+        fn fold_field(&mut self, f: Field) -> Field {
+            Field {
+                ident: codemap::respan(f.ident.span, self.fold_ident(f.ident.node)),
+                expr: if f.is_shorthand {
+                    f.expr.map(|e| fold::noop_fold_expr(e, self))
+                } else {
+                    self.fold_expr(f.expr)
+                },
+                span: self.new_span(f.span),
+                is_shorthand: f.is_shorthand,
+                attrs: fold::fold_thin_attrs(f.attrs, self),
+            }
+        }
+
+        // We don't want to look at expressions that might appear in patterns or
+        // types yet. We'll look into comparing those in the future. For now
+        // focus on expressions appearing in other places.
+        fn fold_pat(&mut self, pat: P<Pat>) -> P<Pat> {
+            pat
+        }
+
+        fn fold_ty(&mut self, ty: P<Ty>) -> P<Ty> {
+            ty
+        }
+
+        fn fold_stmt(&mut self, stmt: Stmt) -> SmallVector<Stmt> {
+            let node = match stmt.node {
+                // Don't wrap toplevel expressions in statements.
+                StmtKind::Expr(e) => {
+                    StmtKind::Expr(e.map(|e| fold::noop_fold_expr(e, self)))
+                }
+                StmtKind::Semi(e) => {
+                    StmtKind::Semi(e.map(|e| fold::noop_fold_expr(e, self)))
+                }
+                s => s,
+            };
+
+            SmallVector::one(Stmt {
+                node: node,
+                ..stmt
+            })
+        }
+
+        fn fold_mac(&mut self, mac: Mac) -> Mac {
+            // By default when folding over macros, syntex panics. This is
+            // because it's usually not what you want, you want to run after
+            // macro expansion. We do want to do that (syn doesn't do macro
+            // expansion), so we implement fold_mac to just return the macro
+            // unchanged.
+            mac
+        }
+    }
+
+    let mut folder = BracketsFolder {
+        failed: false,
+    };
+    let e = folder.fold_expr(syntex_expr);
+    if folder.failed {
+        None
+    } else {
+        Some(e)
+    }
+}
+
+/// Wrap every expression which is not already wrapped in parens with parens, to
+/// reveal the precidence of the parsed expressions, and produce a stringified form
+/// of the resulting expression.
+fn syn_brackets(syn_expr: syn::Expr) -> syn::Expr {
+    use syn::*;
+    use syn::fold::*;
+
+    fn paren(folder: &mut BracketsFolder, node: ExprKind) -> ExprKind {
+        ExprKind::Paren(ExprParen {
+            expr: Box::new(noop_fold_expr(folder, Expr {
+                node: node,
+                attrs: vec![],
+            })),
+            paren_token: tokens::Paren::default(),
+        })
+    }
+
+    struct BracketsFolder;
+    impl Folder for BracketsFolder {
+        fn fold_expr(&mut self, expr: Expr) -> Expr {
+            let kind = match expr.node {
+                ExprKind::Group(_) => unreachable!(),
+                ExprKind::Paren(p) => paren(self, p.expr.node),
+                ExprKind::If(..) |
+                ExprKind::Block(..) |
+                ExprKind::IfLet(..) => {
+                    return noop_fold_expr(self, expr);
+                }
+                node => paren(self, node),
+            };
+
+            Expr {
+                node: kind,
+                ..expr
+            }
+        }
+
+        fn fold_stmt(&mut self, stmt: Stmt) -> Stmt {
+            match stmt {
+                // Don't wrap toplevel expressions in statements.
+                Stmt::Expr(e) => {
+                    Stmt::Expr(Box::new(noop_fold_expr(self, *e)))
+                }
+                Stmt::Semi(e, semi) => {
+                    Stmt::Semi(Box::new(noop_fold_expr(self, *e)), semi)
+                }
+                s => s,
+            }
+        }
+
+        // We don't want to look at expressions that might appear in patterns or
+        // types yet. We'll look into comparing those in the future. For now
+        // focus on expressions appearing in other places.
+        fn fold_pat(&mut self, pat: Pat) -> Pat {
+            pat
+        }
+
+        fn fold_ty(&mut self, ty: Ty) -> Ty {
+            ty
+        }
+    }
+
+    let mut folder = BracketsFolder;
+    folder.fold_expr(syn_expr)
+}
+
+/// Walk through a crate collecting all expressions we can find in it.
+fn collect_exprs(krate: syn::Crate) -> Vec<syn::Expr> {
+    use synom::delimited::Delimited;
+    use syn::*;
+    use syn::fold::*;
+
+    struct CollectExprsFolder(Vec<Expr>);
+    impl Folder for CollectExprsFolder {
+        fn fold_expr(&mut self, expr: Expr) -> Expr {
+            self.0.push(expr);
+
+            Expr {
+                node: ExprKind::Tup(ExprTup {
+                    args: Delimited::new(),
+                    paren_token: tokens::Paren::default(),
+                    lone_comma: None
+                }),
+                attrs: vec![],
+            }
+        }
+    }
+
+    let mut folder = CollectExprsFolder(vec![]);
+    folder.fold_crate(krate);
+    folder.0
+}
diff --git a/tests/test_round_trip.rs b/tests/test_round_trip.rs
index bb07fc7..c07d5f9 100644
--- a/tests/test_round_trip.rs
+++ b/tests/test_round_trip.rs
@@ -8,91 +8,24 @@
 extern crate syntex_syntax;
 extern crate walkdir;
 
-use syntex_pos::Span;
 use syntex_syntax::ast;
 use syntex_syntax::parse::{self, ParseSess, PResult};
 use syntex_syntax::codemap::FilePathMapping;
-use walkdir::{DirEntry, WalkDir, WalkDirIterator};
+use walkdir::{WalkDir, WalkDirIterator};
 
 use std::fs::File;
-use std::io::{self, Read, Write};
+use std::io::Read;
 use std::panic;
 use std::time::Instant;
 
-macro_rules! errorf {
-    ($($tt:tt)*) => {
-        write!(io::stderr(), $($tt)*).unwrap();
-    };
-}
-
-fn filter(entry: &DirEntry) -> bool {
-    let path = entry.path();
-    if path.is_dir() {
-        return true; // otherwise walkdir does not visit the files
-    }
-    if path.extension().map(|e| e != "rs").unwrap_or(true) {
-        return false;
-    }
-    let path_string = path.to_string_lossy();
-    let path_string = if cfg!(windows) {
-        path_string.replace('\\', "/").into()
-    } else {
-        path_string
-    };
-    // TODO assert that parsing fails on the parse-fail cases
-    if path_string.starts_with("tests/rust/src/test/parse-fail") ||
-       path_string.starts_with("tests/rust/src/test/compile-fail") {
-        return false;
-    }
-
-    if path_string.starts_with("tests/rust/src/test/ui") {
-        let stderr_path = path.with_extension("stderr");
-        if stderr_path.exists() {
-            // Expected to fail in some way
-            return false;
-        }
-    }
-
-    match path_string.as_ref() {
-        // TODO better support for attributes
-        "tests/rust/src/librustc_data_structures/blake2b.rs" |
-        // TODO better support for attributes
-        "tests/rust/src/test/incremental/hashes/enum_defs.rs" |
-        // TODO better support for attributes
-        "tests/rust/src/test/pretty/stmt_expr_attributes.rs" |
-        // not actually a test case
-        "tests/rust/src/test/run-pass/auxiliary/macro-include-items-expr.rs" |
-        // TODO better support for attributes
-        "tests/rust/src/test/run-pass/cfg_stmt_expr.rs" |
-        // TODO weird glob import
-        "tests/rust/src/test/run-pass/import-glob-crate.rs" |
-        // TODO better support for attributes
-        "tests/rust/src/test/run-pass/inner-attrs-on-impl.rs" |
-        // TODO better support for attributes
-        "tests/rust/src/test/run-pass/item-attributes.rs" => false,
-        _ => true,
-    }
-}
-
-/// Abort immediately after this many failures.
-fn abort_after() -> u32 {
-    if let Ok(s) = std::env::var("ABORT_AFTER_FAILURE") {
-        if let Ok(n) = s.parse::<u32>() {
-            return n;
-        }
-    }
-    std::u32::MAX
-}
+#[allow(dead_code)]
+#[macro_use]
+mod common;
 
 #[test]
 fn test_round_trip() {
-    {
-        let min_stack_value = std::env::var("RUST_MIN_STACK").expect("RUST_MIN_STACK env var should be set since some tests require it.");
-        let min_stack_value: usize = min_stack_value.parse().expect("RUST_MIN_STACK env var should be set since some tests require it.");
-        assert!(min_stack_value >= 16000000);
-    }
-
-    let abort_after = abort_after();
+    common::check_min_stack();
+    let abort_after = common::abort_after();
     if abort_after == 0 {
         panic!("Skipping all round_trip tests");
     }
@@ -100,7 +33,7 @@
     let mut failed = 0;
 
     let walk = WalkDir::new("tests/rust").sort_by(|a, b| a.cmp(b));
-    for entry in walk.into_iter().filter_entry(filter) {
+    for entry in walk.into_iter().filter_entry(common::base_dir_filter) {
         let entry = entry.unwrap();
 
         let path = entry.path();
@@ -180,242 +113,6 @@
 
 fn syntex_parse(content: String, sess: &ParseSess) -> PResult<ast::Crate> {
     let name = "test_round_trip".to_string();
-    parse::parse_crate_from_source_str(name, content, sess).map(respan_crate)
-}
-
-fn respan_crate(krate: ast::Crate) -> ast::Crate {
-    use std::rc::Rc;
-    use syntex_syntax::ast::{Attribute, Expr, ExprKind, Field, FnDecl, FunctionRetTy, ImplItem,
-                             ImplItemKind, ItemKind, Mac, MetaItem, MetaItemKind, MethodSig,
-                             NestedMetaItem, NestedMetaItemKind, TraitItem, TraitItemKind, TyParam,
-                             Visibility};
-    use syntex_syntax::codemap::{self, Spanned};
-    use syntex_syntax::fold::{self, Folder};
-    use syntex_syntax::parse::token::{Lit, Token};
-    use syntex_syntax::ptr::P;
-    use syntex_syntax::symbol::Symbol;
-    use syntex_syntax::tokenstream::{Delimited, TokenTree};
-    use syntex_syntax::util::move_map::MoveMap;
-    use syntex_syntax::util::small_vector::SmallVector;
-
-    struct Respanner;
-
-    impl Respanner {
-        fn fold_spanned<T>(&mut self, spanned: Spanned<T>) -> Spanned<T> {
-            codemap::respan(self.new_span(spanned.span), spanned.node)
-        }
-
-        fn fold_lit(&mut self, l: Lit) -> Lit {
-            // Give up on comparing literals inside of macros because there are
-            // so many equivalent representations of the same literal; they are
-            // tested elsewhere
-            match l {
-                Lit::Byte(_) => Lit::Byte(Symbol::intern("")),
-                Lit::Char(_) => Lit::Char(Symbol::intern("")),
-                Lit::Integer(_) => Lit::Integer(Symbol::intern("")),
-                Lit::Float(_) => Lit::Float(Symbol::intern("")),
-                Lit::Str_(_) => Lit::Str_(Symbol::intern("")),
-                Lit::ByteStr(_) => Lit::ByteStr(Symbol::intern("")),
-                _ => l,
-            }
-        }
-    }
-
-    impl Folder for Respanner {
-        fn new_span(&mut self, _: Span) -> Span {
-            syntex_pos::DUMMY_SP
-        }
-
-        fn fold_item_kind(&mut self, i: ItemKind) -> ItemKind {
-            match i {
-                ItemKind::Fn(decl, unsafety, constness, abi, generics, body) => {
-                    let generics = self.fold_generics(generics);
-                    let decl = self.fold_fn_decl(decl);
-                    let body = self.fold_block(body);
-                    // default fold_item_kind does not fold this span
-                    let constness = self.fold_spanned(constness);
-                    ItemKind::Fn(decl, unsafety, constness, abi, generics, body)
-                }
-                _ => fold::noop_fold_item_kind(i, self),
-            }
-        }
-
-        fn fold_expr(&mut self, e: P<Expr>) -> P<Expr> {
-            e.map(|e| {
-                let folded = fold::noop_fold_expr(e, self);
-                Expr {
-                    node: match folded.node {
-                        ExprKind::Lit(l) => {
-                            // default fold_expr does not fold lits
-                            ExprKind::Lit(l.map(|l| self.fold_spanned(l)))
-                        }
-                        ExprKind::Binary(op, lhs, rhs) => {
-                            // default fold_expr does not fold the op span
-                            ExprKind::Binary(self.fold_spanned(op),
-                                             self.fold_expr(lhs),
-                                             self.fold_expr(rhs))
-                        }
-                        ExprKind::AssignOp(op, lhs, rhs) => {
-                            // default fold_expr does not fold the op span
-                            ExprKind::AssignOp(self.fold_spanned(op),
-                                               self.fold_expr(lhs),
-                                               self.fold_expr(rhs))
-                        }
-                        other => other,
-                    },
-                    ..folded
-                }
-            })
-        }
-
-        fn fold_ty_param(&mut self, tp: TyParam) -> TyParam {
-            TyParam {
-                // default fold_ty_param does not fold the span
-                span: self.new_span(tp.span),
-                ..fold::noop_fold_ty_param(tp, self)
-            }
-        }
-
-        fn fold_fn_decl(&mut self, decl: P<FnDecl>) -> P<FnDecl> {
-            decl.map(|FnDecl { inputs, output, variadic }| {
-                FnDecl {
-                    inputs: inputs.move_map(|x| self.fold_arg(x)),
-                    output: match output {
-                        FunctionRetTy::Ty(ty) => FunctionRetTy::Ty(self.fold_ty(ty)),
-                        // default fold_fn_decl does not fold this span
-                        FunctionRetTy::Default(span) => FunctionRetTy::Default(self.new_span(span)),
-                    },
-                    variadic: variadic,
-                }
-            })
-        }
-
-        fn fold_field(&mut self, field: Field) -> Field {
-            Field {
-                ident: codemap::respan(// default fold_field does not fold this span
-                                       self.new_span(field.ident.span),
-                                       self.fold_ident(field.ident.node)),
-                expr: self.fold_expr(field.expr),
-                span: self.new_span(field.span),
-                is_shorthand: field.is_shorthand,
-                attrs: ast::ThinVec::new(),
-            }
-        }
-
-        fn fold_trait_item(&mut self, i: TraitItem) -> SmallVector<TraitItem> {
-            let noop = fold::noop_fold_trait_item(i, self).expect_one("");
-            SmallVector::one(TraitItem {
-                                 node: match noop.node {
-                                     TraitItemKind::Method(sig, body) => {
-                        TraitItemKind::Method(MethodSig {
-                                                  constness: self.fold_spanned(sig.constness),
-                                                  ..sig
-                                              },
-                                              body)
-                    }
-                                     node => node,
-                                 },
-                                 ..noop
-                             })
-        }
-
-        fn fold_impl_item(&mut self, i: ImplItem) -> SmallVector<ImplItem> {
-            let noop = fold::noop_fold_impl_item(i, self).expect_one("");
-            SmallVector::one(ImplItem {
-                                 node: match noop.node {
-                                     ImplItemKind::Method(sig, body) => {
-                        ImplItemKind::Method(MethodSig {
-                                                 constness: self.fold_spanned(sig.constness),
-                                                 ..sig
-                                             },
-                                             body)
-                    }
-                                     node => node,
-                                 },
-                                 ..noop
-                             })
-        }
-
-        fn fold_attribute(&mut self, mut at: Attribute) -> Option<Attribute> {
-            at.id.0 = 0;
-            fold::noop_fold_attribute(at, self)
-        }
-
-        fn fold_meta_item(&mut self, meta_item: MetaItem) -> MetaItem {
-            let MetaItem { name, node, span } = meta_item;
-            MetaItem {
-                name: name,
-                node: match node {
-                    MetaItemKind::Word => MetaItemKind::Word,
-                    MetaItemKind::List(nested) => {
-                        MetaItemKind::List(nested.move_map(|e| self.fold_meta_list_item(e)))
-                    }
-                    // default fold_meta_item does not fold the value span
-                    MetaItemKind::NameValue(lit) => MetaItemKind::NameValue(self.fold_spanned(lit)),
-                },
-                span: self.new_span(span),
-            }
-        }
-
-        fn fold_meta_list_item(&mut self, list_item: NestedMetaItem) -> NestedMetaItem {
-            Spanned {
-                node: match list_item.node {
-                    NestedMetaItemKind::MetaItem(mi) => {
-                        NestedMetaItemKind::MetaItem(self.fold_meta_item(mi))
-                    }
-                    // default fold_meta_list_item does not fold the span
-                    NestedMetaItemKind::Literal(lit) => {
-                        NestedMetaItemKind::Literal(self.fold_spanned(lit))
-                    }
-                },
-                span: self.new_span(list_item.span),
-            }
-        }
-
-        fn fold_mac(&mut self, mac: Mac) -> Mac {
-            fold::noop_fold_mac(mac, self)
-        }
-
-        fn fold_tt(&mut self, tt: TokenTree) -> TokenTree {
-            match tt {
-                TokenTree::Token(span, ref tok) => {
-                    TokenTree::Token(self.new_span(span), self.fold_token(tok.clone()))
-                }
-                TokenTree::Delimited(span, ref delimed) => {
-                    TokenTree::Delimited(self.new_span(span),
-                                         Delimited {
-                                             delim: delimed.delim,
-                                             tts: self.fold_tts(delimed.tts.clone().into()).into(),
-                                         })
-                }
-            }
-        }
-
-        fn fold_token(&mut self, t: Token) -> Token {
-            match t {
-                // default fold_token does not fold literals
-                Token::Literal(lit, repr) => Token::Literal(self.fold_lit(lit), repr),
-                Token::Ident(id) => Token::Ident(self.fold_ident(id)),
-                Token::Lifetime(id) => Token::Lifetime(self.fold_ident(id)),
-                Token::Interpolated(nt) => {
-                    let nt = match Rc::try_unwrap(nt) {
-                        Ok(nt) => nt,
-                        Err(nt) => (*nt).clone(),
-                    };
-                    Token::Interpolated(Rc::new(self.fold_interpolated(nt)))
-                }
-                Token::SubstNt(ident) => Token::SubstNt(self.fold_ident(ident)),
-                _ => t,
-            }
-        }
-
-        fn fold_vis(&mut self, vis: Visibility) -> Visibility {
-            match vis {
-                Visibility::Crate(span) => Visibility::Crate(self.new_span(span)),
-                _ => fold::noop_fold_vis(vis, self),
-            }
-        }
-    }
-
-    Respanner.fold_crate(krate)
+    parse::parse_crate_from_source_str(name, content, sess)
+        .map(common::respan::respan_crate)
 }