Rewrite the AST to be a bit more user-friendly

This commit is a relatively large rewrite of the AST that `syn` exposes. The
main change is to expose enums-of-structs rather than
enums-with-huge-tuple-variants. The best example of this is `ItemKind::Fn` which
changed from:

    enum ItemKind {
        Fn(Box<FnDecl>, Unsafety, Constness, Option<Abi>, Generics, Box<Block>),
        ...
    }

to

    enum ItemKind {
        Fn(ItemFn),
        ...
    }

    struct ItemFn {
        decl: Box<FnDecl>,
        unsafety: Unsafety,
        constness: Constness,
        abi: Option<Abi>,
        generics: Generics,
        block: Box<Block>,
    }

This change serves a few purposes:

* It's now much easier to add fields to each variant of the ast, ast struct
  fields tend to be "by default ignored" in most contexts.
* It's much easier to document what each field is, as each field can have
  dedicated documentation.
* There's now canonicalized names for each field (the name of the field) which
  can help match `match` statements more consistent across a codebase.

A downside of this representation is that it can be a little more verbose to
work with in `match` statements and during constructions. Overall though I'd
feel at least that the readability improved significantly despite the extra
words required to do various operations.

Closes #136
diff --git a/src/expr.rs b/src/expr.rs
index fae3e65..39ab6c7 100644
--- a/src/expr.rs
+++ b/src/expr.rs
@@ -1,13 +1,14 @@
 use super::*;
 
-/// An expression.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Expr {
-    /// Type of the expression.
-    pub node: ExprKind,
+ast_struct! {
+    /// An expression.
+    pub struct Expr {
+        /// Type of the expression.
+        pub node: ExprKind,
 
-    /// Attributes tagged on the expression.
-    pub attrs: Vec<Attribute>,
+        /// Attributes tagged on the expression.
+        pub attrs: Vec<Attribute>,
+    }
 }
 
 impl From<ExprKind> for Expr {
@@ -19,350 +20,472 @@
     }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum ExprKind {
-    /// A `box x` expression.
-    Box(Box<Expr>),
+ast_enum_of_structs! {
+    pub enum ExprKind {
+        /// A `box x` expression.
+        pub Box(ExprBox {
+            pub expr: Box<Expr>,
+        }),
 
-    /// First expr is the place; second expr is the value.
-    ///
-    /// E.g. 'plae <- val'.
-    InPlace(Box<Expr>, Box<Expr>),
+        /// E.g. 'place <- val'.
+        pub InPlace(ExprInPlace {
+            pub place: Box<Expr>,
+            pub value: Box<Expr>,
+        }),
 
-    /// An array, e.g. `[a, b, c, d]`.
-    Array(Vec<Expr>),
+        /// An array, e.g. `[a, b, c, d]`.
+        pub Array(ExprArray {
+            pub exprs: Vec<Expr>,
+        }),
 
-    /// A function call.
-    ///
-    /// The first field resolves to the function itself,
-    /// and the second field is the list of arguments
-    Call(Box<Expr>, Vec<Expr>),
+        /// A function call.
+        pub Call(ExprCall {
+            pub func: Box<Expr>,
+            pub args: Vec<Expr>,
+        }),
 
-    /// A method call (`x.foo::<Bar, Baz>(a, b, c, d)`)
-    ///
-    /// The `Ident` is the identifier for the method name.
-    /// The vector of `Ty`s are the ascripted type parameters for the method
-    /// (within the angle brackets).
-    ///
-    /// The first element of the vector of `Expr`s is the expression that evaluates
-    /// to the object on which the method is being called on (the receiver),
-    /// and the remaining elements are the rest of the arguments.
-    ///
-    /// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as
-    /// `ExprKind::MethodCall(foo, [Bar, Baz], [x, a, b, c, d])`.
-    MethodCall(Ident, Vec<Ty>, Vec<Expr>),
+        /// A method call (`x.foo::<Bar, Baz>(a, b, c, d)`)
+        ///
+        /// The `Ident` is the identifier for the method name.
+        /// The vector of `Ty`s are the ascripted type parameters for the method
+        /// (within the angle brackets).
+        ///
+        /// The first element of the vector of `Expr`s is the expression that evaluates
+        /// to the object on which the method is being called on (the receiver),
+        /// and the remaining elements are the rest of the arguments.
+        ///
+        /// Thus, `x.foo::<Bar, Baz>(a, b, c, d)` is represented as
+        /// `ExprKind::MethodCall(foo, [Bar, Baz], [x, a, b, c, d])`.
+        pub MethodCall(ExprMethodCall {
+            pub method: Ident,
+            pub typarams: Vec<Ty>,
+            pub args: Vec<Expr>,
+        }),
 
-    /// A tuple, e.g. `(a, b, c, d)`.
-    Tup(Vec<Expr>),
+        /// A tuple, e.g. `(a, b, c, d)`.
+        pub Tup(ExprTup {
+            pub args: Vec<Expr>,
+        }),
 
-    /// A binary operation, e.g. `a + b`, `a * b`.
-    Binary(BinOp, Box<Expr>, Box<Expr>),
+        /// A binary operation, e.g. `a + b`, `a * b`.
+        pub Binary(ExprBinary {
+            pub op: BinOp,
+            pub left: Box<Expr>,
+            pub right: Box<Expr>,
+        }),
 
-    /// A unary operation, e.g. `!x`, `*x`.
-    Unary(UnOp, Box<Expr>),
+        /// A unary operation, e.g. `!x`, `*x`.
+        pub Unary(ExprUnary {
+            pub op: UnOp,
+            pub expr: Box<Expr>,
+        }),
 
-    /// A literal, e.g. `1`, `"foo"`.
-    Lit(Lit),
+        /// A literal, e.g. `1`, `"foo"`.
+        pub Lit(Lit),
 
-    /// A cast, e.g. `foo as f64`.
-    Cast(Box<Expr>, Box<Ty>),
+        /// A cast, e.g. `foo as f64`.
+        pub Cast(ExprCast {
+            pub expr: Box<Expr>,
+            pub ty: Box<Ty>,
+        }),
 
-    /// A type ascription, e.g. `foo: f64`.
-    Type(Box<Expr>, Box<Ty>),
+        /// A type ascription, e.g. `foo: f64`.
+        pub Type(ExprType {
+            pub expr: Box<Expr>,
+            pub ty: Box<Ty>,
+        }),
 
-    /// An `if` block, with an optional else block
-    ///
-    /// E.g., `if expr { block } else { expr }`
-    If(Box<Expr>, Block, Option<Box<Expr>>),
+        /// An `if` block, with an optional else block
+        ///
+        /// E.g., `if expr { block } else { expr }`
+        pub If(ExprIf {
+            pub cond: Box<Expr>,
+            pub if_true: Block,
+            pub if_false: Option<Box<Expr>>,
+        }),
 
-    /// An `if let` expression with an optional else block
-    ///
-    /// E.g., `if let pat = expr { block } else { expr }`
-    ///
-    /// This is desugared to a `match` expression.
-    IfLet(Box<Pat>, Box<Expr>, Block, Option<Box<Expr>>),
+        /// An `if let` expression with an optional else block
+        ///
+        /// E.g., `if let pat = expr { block } else { expr }`
+        ///
+        /// This is desugared to a `match` expression.
+        pub IfLet(ExprIfLet {
+            pub pat: Box<Pat>,
+            pub expr: Box<Expr>,
+            pub if_true: Block,
+            pub if_false: Option<Box<Expr>>,
+        }),
 
-    /// A while loop, with an optional label
-    ///
-    /// E.g., `'label: while expr { block }`
-    While(Box<Expr>, Block, Option<Ident>),
+        /// A while loop, with an optional label
+        ///
+        /// E.g., `'label: while expr { block }`
+        pub While(ExprWhile {
+            pub cond: Box<Expr>,
+            pub body: Block,
+            pub label: Option<Ident>,
+        }),
 
-    /// A while-let loop, with an optional label.
-    ///
-    /// E.g., `'label: while let pat = expr { block }`
-    ///
-    /// This is desugared to a combination of `loop` and `match` expressions.
-    WhileLet(Box<Pat>, Box<Expr>, Block, Option<Ident>),
+        /// A while-let loop, with an optional label.
+        ///
+        /// E.g., `'label: while let pat = expr { block }`
+        ///
+        /// This is desugared to a combination of `loop` and `match` expressions.
+        pub WhileLet(ExprWhileLet {
+            pub pat: Box<Pat>,
+            pub expr: Box<Expr>,
+            pub body: Block,
+            pub label: Option<Ident>,
+        }),
 
-    /// A for loop, with an optional label.
-    ///
-    /// E.g., `'label: for pat in expr { block }`
-    ///
-    /// This is desugared to a combination of `loop` and `match` expressions.
-    ForLoop(Box<Pat>, Box<Expr>, Block, Option<Ident>),
+        /// A for loop, with an optional label.
+        ///
+        /// E.g., `'label: for pat in expr { block }`
+        ///
+        /// This is desugared to a combination of `loop` and `match` expressions.
+        pub ForLoop(ExprForLoop {
+            pub pat: Box<Pat>,
+            pub expr: Box<Expr>,
+            pub body: Block,
+            pub label: Option<Ident>,
+        }),
 
-    /// Conditionless loop with an optional label.
-    ///
-    /// E.g. `'label: loop { block }`
-    Loop(Block, Option<Ident>),
+        /// Conditionless loop with an optional label.
+        ///
+        /// E.g. `'label: loop { block }`
+        pub Loop(ExprLoop {
+            pub body: Block,
+            pub label: Option<Ident>,
+        }),
 
-    /// A `match` block.
-    Match(Box<Expr>, Vec<Arm>),
+        /// A `match` block.
+        pub Match(ExprMatch {
+            pub expr: Box<Expr>,
+            pub arms: Vec<Arm>,
+        }),
 
-    /// A closure (for example, `move |a, b, c| a + b + c`)
-    Closure(CaptureBy, Box<FnDecl>, Box<Expr>),
+        /// A closure (for example, `move |a, b, c| a + b + c`)
+        pub Closure(ExprClosure {
+            pub capture: CaptureBy,
+            pub decl: Box<FnDecl>,
+            pub body: Box<Expr>,
+        }),
 
-    /// A block (`{ ... }` or `unsafe { ... }`)
-    Block(Unsafety, Block),
+        /// A block (`{ ... }` or `unsafe { ... }`)
+        pub Block(ExprBlock {
+            pub unsafety: Unsafety,
+            pub block: Block,
+        }),
 
-    /// An assignment (`a = foo()`)
-    Assign(Box<Expr>, Box<Expr>),
+        /// An assignment (`a = foo()`)
+        pub Assign(ExprAssign {
+            pub left: Box<Expr>,
+            pub right: Box<Expr>,
+        }),
 
-    /// An assignment with an operator
-    ///
-    /// For example, `a += 1`.
-    AssignOp(BinOp, Box<Expr>, Box<Expr>),
+        /// An assignment with an operator
+        ///
+        /// For example, `a += 1`.
+        pub AssignOp(ExprAssignOp {
+            pub op: BinOp,
+            pub left: Box<Expr>,
+            pub right: Box<Expr>,
+        }),
 
-    /// Access of a named struct field (`obj.foo`)
-    Field(Box<Expr>, Ident),
+        /// Access of a named struct field (`obj.foo`)
+        pub Field(ExprField {
+            pub expr: Box<Expr>,
+            pub field: Ident,
+        }),
 
-    /// Access of an unnamed field of a struct or tuple-struct
-    ///
-    /// For example, `foo.0`.
-    TupField(Box<Expr>, usize),
+        /// Access of an unnamed field of a struct or tuple-struct
+        ///
+        /// For example, `foo.0`.
+        pub TupField(ExprTupField {
+            pub expr: Box<Expr>,
+            pub field: usize,
+        }),
 
-    /// An indexing operation (`foo[2]`)
-    Index(Box<Expr>, Box<Expr>),
+        /// An indexing operation (`foo[2]`)
+        pub Index(ExprIndex {
+            pub expr: Box<Expr>,
+            pub index: Box<Expr>,
+        }),
 
-    /// A range (`1..2`, `1..`, `..2`, `1...2`, `1...`, `...2`)
-    Range(Option<Box<Expr>>, Option<Box<Expr>>, RangeLimits),
+        /// A range (`1..2`, `1..`, `..2`, `1...2`, `1...`, `...2`)
+        pub Range(ExprRange {
+            pub from: Option<Box<Expr>>,
+            pub to: Option<Box<Expr>>,
+            pub limits: RangeLimits,
+        }),
 
-    /// Variable reference, possibly containing `::` and/or type
-    /// parameters, e.g. foo::bar::<baz>.
-    ///
-    /// Optionally "qualified",
-    /// E.g. `<Vec<T> as SomeTrait>::SomeType`.
-    Path(Option<QSelf>, Path),
+        /// Variable reference, possibly containing `::` and/or type
+        /// parameters, e.g. foo::bar::<baz>.
+        ///
+        /// Optionally "qualified",
+        /// E.g. `<Vec<T> as SomeTrait>::SomeType`.
+        pub Path(ExprPath {
+            pub qself: Option<QSelf>,
+            pub path: Path,
+        }),
 
-    /// A referencing operation (`&a` or `&mut a`)
-    AddrOf(Mutability, Box<Expr>),
+        /// A referencing operation (`&a` or `&mut a`)
+        pub AddrOf(ExprAddrOf {
+            pub mutbl: Mutability,
+            pub expr: Box<Expr>,
+        }),
 
-    /// A `break`, with an optional label to break, and an optional expression
-    Break(Option<Ident>, Option<Box<Expr>>),
+        /// A `break`, with an optional label to break, and an optional expression
+        pub Break(ExprBreak {
+            pub label: Option<Ident>,
+            pub expr: Option<Box<Expr>>,
+        }),
 
-    /// A `continue`, with an optional label
-    Continue(Option<Ident>),
+        /// A `continue`, with an optional label
+        pub Continue(ExprContinue {
+            pub label: Option<Ident>,
+        }),
 
-    /// A `return`, with an optional value to be returned
-    Ret(Option<Box<Expr>>),
+        /// A `return`, with an optional value to be returned
+        pub Ret(ExprRet {
+            pub expr: Option<Box<Expr>>,
+        }),
 
-    /// A macro invocation; pre-expansion
-    Mac(Mac),
+        /// A macro invocation; pre-expansion
+        pub Mac(Mac),
 
-    /// A struct literal expression.
-    ///
-    /// For example, `Foo {x: 1, y: 2}`, or
-    /// `Foo {x: 1, .. base}`, where `base` is the `Option<Expr>`.
-    Struct(Path, Vec<FieldValue>, Option<Box<Expr>>),
+        /// A struct literal expression.
+        ///
+        /// For example, `Foo {x: 1, y: 2}`, or
+        /// `Foo {x: 1, .. base}`, where `base` is the `Option<Expr>`.
+        pub Struct(ExprStruct {
+            pub path: Path,
+            pub fields: Vec<FieldValue>,
+            pub rest: Option<Box<Expr>>,
+        }),
 
-    /// An array literal constructed from one repeated element.
-    ///
-    /// For example, `[1; 5]`. The first expression is the element
-    /// to be repeated; the second is the number of times to repeat it.
-    Repeat(Box<Expr>, Box<Expr>),
+        /// An array literal constructed from one repeated element.
+        ///
+        /// For example, `[1; 5]`. The first expression is the element
+        /// to be repeated; the second is the number of times to repeat it.
+        pub Repeat(ExprRepeat {
+            pub expr: Box<Expr>,
+            pub amt: Box<Expr>,
+        }),
 
-    /// No-op: used solely so we can pretty-print faithfully
-    Paren(Box<Expr>),
+        /// No-op: used solely so we can pretty-print faithfully
+        pub Paren(ExprParen {
+            pub expr: Box<Expr>,
+        }),
 
-    /// `expr?`
-    Try(Box<Expr>),
+        /// `expr?`
+        pub Try(ExprTry {
+            pub expr: Box<Expr>,
+        }),
 
-    /// A catch expression.
-    ///
-    /// E.g. `do catch { block }`
-    Catch(Block),
+        /// A catch expression.
+        ///
+        /// E.g. `do catch { block }`
+        pub Catch(ExprCatch {
+            pub block: Block,
+        }),
+    }
 }
 
-/// A field-value pair in a struct literal.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct FieldValue {
-    /// Name of the field.
-    pub ident: Ident,
+ast_struct! {
+    /// A field-value pair in a struct literal.
+    pub struct FieldValue {
+        /// Name of the field.
+        pub ident: Ident,
 
-    /// Value of the field.
-    pub expr: Expr,
+        /// Value of the field.
+        pub expr: Expr,
 
-    /// Whether this is a shorthand field, e.g. `Struct { x }`
-    /// instead of `Struct { x: x }`.
-    pub is_shorthand: bool,
+        /// Whether this is a shorthand field, e.g. `Struct { x }`
+        /// instead of `Struct { x: x }`.
+        pub is_shorthand: bool,
 
-    /// Attributes tagged on the field.
-    pub attrs: Vec<Attribute>,
+        /// Attributes tagged on the field.
+        pub attrs: Vec<Attribute>,
+    }
 }
 
-/// A Block (`{ .. }`).
-///
-/// E.g. `{ .. }` as in `fn foo() { .. }`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Block {
-    /// Statements in a block
-    pub stmts: Vec<Stmt>,
+ast_struct! {
+    /// A Block (`{ .. }`).
+    ///
+    /// E.g. `{ .. }` as in `fn foo() { .. }`
+    pub struct Block {
+        /// Statements in a block
+        pub stmts: Vec<Stmt>,
+    }
 }
 
-/// A statement, usually ending in a semicolon.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Stmt {
-    /// A local (let) binding.
-    Local(Box<Local>),
+ast_enum! {
+    /// A statement, usually ending in a semicolon.
+    pub enum Stmt {
+        /// A local (let) binding.
+        Local(Box<Local>),
 
-    /// An item definition.
-    Item(Box<Item>),
+        /// An item definition.
+        Item(Box<Item>),
 
-    /// Expr without trailing semicolon.
-    Expr(Box<Expr>),
+        /// Expr without trailing semicolon.
+        Expr(Box<Expr>),
 
-    /// Expression with trailing semicolon;
-    Semi(Box<Expr>),
+        /// Expression with trailing semicolon;
+        Semi(Box<Expr>),
 
-    /// Macro invocation.
-    Mac(Box<(Mac, MacStmtStyle, Vec<Attribute>)>),
+        /// Macro invocation.
+        Mac(Box<(Mac, MacStmtStyle, Vec<Attribute>)>),
+    }
 }
 
-/// How a macro was invoked.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum MacStmtStyle {
-    /// The macro statement had a trailing semicolon, e.g. `foo! { ... };`
-    /// `foo!(...);`, `foo![...];`
-    Semicolon,
+ast_enum! {
+    /// How a macro was invoked.
+    #[derive(Copy)]
+    pub enum MacStmtStyle {
+        /// The macro statement had a trailing semicolon, e.g. `foo! { ... };`
+        /// `foo!(...);`, `foo![...];`
+        Semicolon,
 
-    /// The macro statement had braces; e.g. foo! { ... }
-    Braces,
+        /// The macro statement had braces; e.g. foo! { ... }
+        Braces,
 
-    /// The macro statement had parentheses or brackets and no semicolon; e.g.
-    /// `foo!(...)`. All of these will end up being converted into macro
-    /// expressions.
-    NoBraces,
+        /// The macro statement had parentheses or brackets and no semicolon; e.g.
+        /// `foo!(...)`. All of these will end up being converted into macro
+        /// expressions.
+        NoBraces,
+    }
 }
 
-/// Local represents a `let` statement, e.g., `let <pat>:<ty> = <expr>;`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Local {
-    pub pat: Box<Pat>,
-    pub ty: Option<Box<Ty>>,
+ast_struct! {
+    /// Local represents a `let` statement, e.g., `let <pat>:<ty> = <expr>;`
+    pub struct Local {
+        pub pat: Box<Pat>,
+        pub ty: Option<Box<Ty>>,
 
-    /// Initializer expression to set the value, if any
-    pub init: Option<Box<Expr>>,
-    pub attrs: Vec<Attribute>,
+        /// Initializer expression to set the value, if any
+        pub init: Option<Box<Expr>>,
+        pub attrs: Vec<Attribute>,
+    }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-// Clippy false positive
-// https://github.com/Manishearth/rust-clippy/issues/1241
-#[cfg_attr(feature = "cargo-clippy", allow(enum_variant_names))]
-pub enum Pat {
-    /// Represents a wildcard pattern (`_`)
-    Wild,
+ast_enum! {
+    // Clippy false positive
+    // https://github.com/Manishearth/rust-clippy/issues/1241
+    #[cfg_attr(feature = "cargo-clippy", allow(enum_variant_names))]
+    pub enum Pat {
+        /// Represents a wildcard pattern (`_`)
+        Wild,
 
-    /// A `Pat::Ident` may either be a new bound variable (`ref mut binding @ OPT_SUBPATTERN`),
-    /// or a unit struct/variant pattern, or a const pattern (in the last two cases the third
-    /// field must be `None`). Disambiguation cannot be done with parser alone, so it happens
-    /// during name resolution.
-    Ident(BindingMode, Ident, Option<Box<Pat>>),
+        /// A `Pat::Ident` may either be a new bound variable (`ref mut binding @ OPT_SUBPATTERN`),
+        /// or a unit struct/variant pattern, or a const pattern (in the last two cases the third
+        /// field must be `None`). Disambiguation cannot be done with parser alone, so it happens
+        /// during name resolution.
+        Ident(BindingMode, Ident, Option<Box<Pat>>),
 
-    /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
-    /// The `bool` is `true` in the presence of a `..`.
-    Struct(Path, Vec<FieldPat>, bool),
+        /// A struct or struct variant pattern, e.g. `Variant {x, y, ..}`.
+        /// The `bool` is `true` in the presence of a `..`.
+        Struct(Path, Vec<FieldPat>, bool),
 
-    /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
-    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
-    /// 0 <= position <= subpats.len()
-    TupleStruct(Path, Vec<Pat>, Option<usize>),
+        /// A tuple struct/variant pattern `Variant(x, y, .., z)`.
+        /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
+        /// 0 <= position <= subpats.len()
+        TupleStruct(Path, Vec<Pat>, Option<usize>),
 
-    /// A possibly qualified path pattern.
-    /// Unquailfied path patterns `A::B::C` can legally refer to variants, structs, constants
-    /// or associated constants. Quailfied path patterns `<A>::B::C`/`<A as Trait>::B::C` can
-    /// only legally refer to associated constants.
-    Path(Option<QSelf>, Path),
+        /// A possibly qualified path pattern.
+        /// Unquailfied path patterns `A::B::C` can legally refer to variants, structs, constants
+        /// or associated constants. Quailfied path patterns `<A>::B::C`/`<A as Trait>::B::C` can
+        /// only legally refer to associated constants.
+        Path(Option<QSelf>, Path),
 
-    /// A tuple pattern `(a, b)`.
-    /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
-    /// 0 <= position <= subpats.len()
-    Tuple(Vec<Pat>, Option<usize>),
-    /// A `box` pattern
-    Box(Box<Pat>),
-    /// A reference pattern, e.g. `&mut (a, b)`
-    Ref(Box<Pat>, Mutability),
-    /// A literal
-    Lit(Box<Expr>),
-    /// A range pattern, e.g. `1...2`
-    Range(Box<Expr>, Box<Expr>, RangeLimits),
-    /// `[a, b, ..i, y, z]` is represented as:
-    ///     `Pat::Slice(box [a, b], Some(i), box [y, z])`
-    Slice(Vec<Pat>, Option<Box<Pat>>, Vec<Pat>),
-    /// A macro pattern; pre-expansion
-    Mac(Mac),
+        /// A tuple pattern `(a, b)`.
+        /// If the `..` pattern fragment is present, then `Option<usize>` denotes its position.
+        /// 0 <= position <= subpats.len()
+        Tuple(Vec<Pat>, Option<usize>),
+        /// A `box` pattern
+        Box(Box<Pat>),
+        /// A reference pattern, e.g. `&mut (a, b)`
+        Ref(Box<Pat>, Mutability),
+        /// A literal
+        Lit(Box<Expr>),
+        /// A range pattern, e.g. `1...2`
+        Range(Box<Expr>, Box<Expr>, RangeLimits),
+        /// `[a, b, ..i, y, z]` is represented as:
+        ///     `Pat::Slice(box [a, b], Some(i), box [y, z])`
+        Slice(Vec<Pat>, Option<Box<Pat>>, Vec<Pat>),
+        /// A macro pattern; pre-expansion
+        Mac(Mac),
+    }
 }
 
-/// An arm of a 'match'.
-///
-/// E.g. `0...10 => { println!("match!") }` as in
-///
-/// ```rust,ignore
-/// match n {
-///     0...10 => { println!("match!") },
-///     // ..
-/// }
-/// ```
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Arm {
-    pub attrs: Vec<Attribute>,
-    pub pats: Vec<Pat>,
-    pub guard: Option<Box<Expr>>,
-    pub body: Box<Expr>,
+ast_struct! {
+    /// An arm of a 'match'.
+    ///
+    /// E.g. `0...10 => { println!("match!") }` as in
+    ///
+    /// ```rust,ignore
+    /// match n {
+    ///     0...10 => { println!("match!") },
+    ///     // ..
+    /// }
+    /// ```
+    pub struct Arm {
+        pub attrs: Vec<Attribute>,
+        pub pats: Vec<Pat>,
+        pub guard: Option<Box<Expr>>,
+        pub body: Box<Expr>,
+    }
 }
 
-/// A capture clause
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum CaptureBy {
-    Value,
-    Ref,
+ast_enum! {
+    /// A capture clause
+    #[derive(Copy)]
+    pub enum CaptureBy {
+        Value,
+        Ref,
+    }
 }
 
-/// Limit types of a range (inclusive or exclusive)
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum RangeLimits {
-    /// Inclusive at the beginning, exclusive at the end
-    HalfOpen,
-    /// Inclusive at the beginning and end
-    Closed,
+ast_enum! {
+    /// Limit types of a range (inclusive or exclusive)
+    #[derive(Copy)]
+    pub enum RangeLimits {
+        /// Inclusive at the beginning, exclusive at the end
+        HalfOpen,
+        /// Inclusive at the beginning and end
+        Closed,
+    }
 }
 
-/// A single field in a struct pattern
-///
-/// Patterns like the fields of Foo `{ x, ref y, ref mut z }`
-/// are treated the same as `x: x, y: ref y, z: ref mut z`,
-/// except `is_shorthand` is true
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct FieldPat {
-    /// The identifier for the field
-    pub ident: Ident,
-    /// The pattern the field is destructured to
-    pub pat: Box<Pat>,
-    pub is_shorthand: bool,
-    pub attrs: Vec<Attribute>,
+ast_struct! {
+    /// A single field in a struct pattern
+    ///
+    /// Patterns like the fields of Foo `{ x, ref y, ref mut z }`
+    /// are treated the same as `x: x, y: ref y, z: ref mut z`,
+    /// except `is_shorthand` is true
+    pub struct FieldPat {
+        /// The identifier for the field
+        pub ident: Ident,
+        /// The pattern the field is destructured to
+        pub pat: Box<Pat>,
+        pub is_shorthand: bool,
+        pub attrs: Vec<Attribute>,
+    }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum BindingMode {
-    ByRef(Mutability),
-    ByValue(Mutability),
+ast_enum! {
+    #[derive(Copy)]
+    pub enum BindingMode {
+        ByRef(Mutability),
+        ByValue(Mutability),
+    }
 }
 
 #[cfg(feature = "parsing")]
 pub mod parsing {
     use super::*;
     use {BinOp, Delimited, DelimToken, FnArg, FnDecl, FunctionRetTy, Ident, Lifetime, Mac,
-         TokenTree, Ty, UnOp, Unsafety};
+         TokenTree, Ty, UnOp, Unsafety, ArgCaptured, TyInfer};
     use attr::parsing::outer_attr;
     use generics::parsing::lifetime;
     use ident::parsing::{ident, wordlike};
@@ -448,56 +571,93 @@
             ) >>
             many0!(alt!(
                 tap!(args: and_call => {
-                    e = ExprKind::Call(Box::new(e.into()), args);
+                    e = ExprCall {
+                        func: Box::new(e.into()),
+                        args: args,
+                    }.into();
                 })
                 |
                 tap!(more: and_method_call => {
                     let (method, ascript, mut args) = more;
                     args.insert(0, e.into());
-                    e = ExprKind::MethodCall(method, ascript, args);
+                    e = ExprMethodCall {
+                        method: method,
+                        typarams: ascript,
+                        args: args,
+                    }.into();
                 })
                 |
                 tap!(more: call!(and_binary, allow_struct) => {
                     let (op, other) = more;
-                    e = ExprKind::Binary(op, Box::new(e.into()), Box::new(other));
+                    e = ExprBinary {
+                        op: op,
+                        left: Box::new(e.into()),
+                        right: Box::new(other),
+                    }.into();
                 })
                 |
                 tap!(ty: and_cast => {
-                    e = ExprKind::Cast(Box::new(e.into()), Box::new(ty));
+                    e = ExprCast {
+                        expr: Box::new(e.into()),
+                        ty: Box::new(ty),
+                    }.into();
                 })
                 |
                 tap!(ty: and_ascription => {
-                    e = ExprKind::Type(Box::new(e.into()), Box::new(ty));
+                    e = ExprType {
+                        expr: Box::new(e.into()),
+                        ty: Box::new(ty),
+                    }.into();
                 })
                 |
                 tap!(v: call!(and_assign, allow_struct) => {
-                    e = ExprKind::Assign(Box::new(e.into()), Box::new(v));
+                    e = ExprAssign {
+                        left: Box::new(e.into()),
+                        right: Box::new(v),
+                    }.into();
                 })
                 |
                 tap!(more: call!(and_assign_op, allow_struct) => {
                     let (op, v) = more;
-                    e = ExprKind::AssignOp(op, Box::new(e.into()), Box::new(v));
+                    e = ExprAssignOp {
+                        op: op,
+                        left: Box::new(e.into()),
+                        right: Box::new(v),
+                    }.into();
                 })
                 |
                 tap!(field: and_field => {
-                    e = ExprKind::Field(Box::new(e.into()), field);
+                    e = ExprField {
+                        expr: Box::new(e.into()),
+                        field: field,
+                    }.into();
                 })
                 |
                 tap!(field: and_tup_field => {
-                    e = ExprKind::TupField(Box::new(e.into()), field as usize);
+                    e = ExprTupField {
+                        expr: Box::new(e.into()),
+                        field: field as usize,
+                    }.into();
                 })
                 |
                 tap!(i: and_index => {
-                    e = ExprKind::Index(Box::new(e.into()), Box::new(i));
+                    e = ExprIndex {
+                        expr: Box::new(e.into()),
+                        index: Box::new(i),
+                    }.into();
                 })
                 |
                 tap!(more: call!(and_range, allow_struct) => {
                     let (limits, hi) = more;
-                    e = ExprKind::Range(Some(Box::new(e.into())), hi.map(Box::new), limits);
+                    e = ExprRange {
+                        from: Some(Box::new(e.into())),
+                        to: hi.map(Box::new),
+                        limits: limits,
+                    }.into();
                 })
                 |
                 tap!(_try: punct!("?") => {
-                    e = ExprKind::Try(Box::new(e.into()));
+                    e = ExprTry { expr: Box::new(e.into()) }.into();
                 })
             )) >>
             (e.into())
@@ -510,13 +670,13 @@
         punct!("(") >>
         e: expr >>
         punct!(")") >>
-        (ExprKind::Paren(Box::new(e)))
+        (ExprParen { expr: Box::new(e) }.into())
     ));
 
     named_ambiguous_expr!(expr_box -> ExprKind, allow_struct, do_parse!(
         keyword!("box") >>
         inner: ambiguous_expr!(allow_struct) >>
-        (ExprKind::Box(Box::new(inner)))
+        (ExprBox { expr: Box::new(inner) }.into())
     ));
 
     named!(expr_in_place -> ExprKind, do_parse!(
@@ -525,19 +685,23 @@
         punct!("{") >>
         value: within_block >>
         punct!("}") >>
-        (ExprKind::InPlace(
-            Box::new(place),
-            Box::new(ExprKind::Block(Unsafety::Normal, Block {
-                stmts: value,
-            }).into()),
-        ))
+        (ExprInPlace {
+            place: Box::new(place),
+            value: Box::new(Expr {
+                node: ExprBlock {
+                    unsafety: Unsafety::Normal,
+                    block: Block { stmts: value, },
+                }.into(),
+                attrs: Vec::new(),
+            }),
+        }.into())
     ));
 
     named!(expr_array -> ExprKind, do_parse!(
         punct!("[") >>
         elems: terminated_list!(punct!(","), expr) >>
         punct!("]") >>
-        (ExprKind::Array(elems))
+        (ExprArray { exprs: elems }.into())
     ));
 
     named!(and_call -> Vec<Expr>, do_parse!(
@@ -568,7 +732,7 @@
         punct!("(") >>
         elems: terminated_list!(punct!(","), expr) >>
         punct!(")") >>
-        (ExprKind::Tup(elems))
+        (ExprTup { args: elems }.into())
     ));
 
     named_ambiguous_expr!(and_binary -> (BinOp, Expr), allow_struct, tuple!(
@@ -579,7 +743,7 @@
     named_ambiguous_expr!(expr_unary -> ExprKind, allow_struct, do_parse!(
         operator: unop >>
         operand: ambiguous_expr!(allow_struct) >>
-        (ExprKind::Unary(operator, Box::new(operand)))
+        (ExprUnary { op: operator, expr: Box::new(operand) }.into())
     ));
 
     named!(expr_lit -> ExprKind, map!(lit, ExprKind::Lit));
@@ -624,28 +788,29 @@
                     punct!("{") >>
                     else_block: within_block >>
                     punct!("}") >>
-                    (ExprKind::Block(Unsafety::Normal, Block {
-                        stmts: else_block,
+                    (ExprKind::Block(ExprBlock {
+                        unsafety: Unsafety::Normal,
+                        block: Block { stmts: else_block },
                     }).into())
                 )
             )
         )) >>
         (match cond {
-            Cond::Let(pat, expr) => ExprKind::IfLet(
-                Box::new(pat),
-                Box::new(expr),
-                Block {
+            Cond::Let(pat, expr) => ExprIfLet {
+                pat: Box::new(pat),
+                expr: Box::new(expr),
+                if_true: Block {
                     stmts: then_block,
                 },
-                else_block.map(|els| Box::new(els.into())),
-            ),
-            Cond::Expr(cond) => ExprKind::If(
-                Box::new(cond),
-                Block {
+                if_false: else_block.map(|els| Box::new(els.into())),
+            }.into(),
+            Cond::Expr(cond) => ExprIf {
+                cond: Box::new(cond),
+                if_true: Block {
                     stmts: then_block,
                 },
-                else_block.map(|els| Box::new(els.into())),
-            ),
+                if_false: else_block.map(|els| Box::new(els.into())),
+            }.into(),
         })
     ));
 
@@ -656,14 +821,19 @@
         keyword!("in") >>
         expr: expr_no_struct >>
         loop_block: block >>
-        (ExprKind::ForLoop(Box::new(pat), Box::new(expr), loop_block, lbl))
+        (ExprForLoop {
+            pat: Box::new(pat),
+            expr: Box::new(expr),
+            body: loop_block,
+            label: lbl,
+        }.into())
     ));
 
     named!(expr_loop -> ExprKind, do_parse!(
         lbl: option!(terminated!(label, punct!(":"))) >>
         keyword!("loop") >>
         loop_block: block >>
-        (ExprKind::Loop(loop_block, lbl))
+        (ExprLoop { body: loop_block, label: lbl }.into())
     ));
 
     named!(expr_match -> ExprKind, do_parse!(
@@ -678,21 +848,24 @@
         )) >>
         last_arm: option!(match_arm) >>
         punct!("}") >>
-        (ExprKind::Match(Box::new(obj), {
-            arms.extend(last_arm);
-            arms
-        }))
+        (ExprMatch {
+            expr: Box::new(obj),
+            arms: {
+                arms.extend(last_arm);
+                arms
+            },
+        }.into())
     ));
 
     named!(expr_catch -> ExprKind, do_parse!(
         keyword!("do") >>
         keyword!("catch") >>
         catch_block: block >>
-        (ExprKind::Catch(catch_block))
+        (ExprCatch { block: catch_block }.into())
     ));
 
     fn arm_requires_comma(arm: &Arm) -> bool {
-        if let ExprKind::Block(Unsafety::Normal, _) = arm.body.node {
+        if let ExprKind::Block(ExprBlock { unsafety: Unsafety::Normal, .. }) = arm.body.node {
             false
         } else {
             true
@@ -705,7 +878,12 @@
         guard: option!(preceded!(keyword!("if"), expr)) >>
         punct!("=>") >>
         body: alt!(
-            map!(block, |blk| ExprKind::Block(Unsafety::Normal, blk).into())
+            map!(block, |blk| {
+                ExprKind::Block(ExprBlock {
+                    unsafety: Unsafety::Normal,
+                    block: blk,
+                }).into()
+            })
             |
             expr
         ) >>
@@ -727,26 +905,32 @@
                 punct!("->") >>
                 ty: ty >>
                 body: block >>
-                (FunctionRetTy::Ty(ty), ExprKind::Block(Unsafety::Normal, body).into())
+                (FunctionRetTy::Ty(ty), ExprKind::Block(ExprBlock {
+                    unsafety: Unsafety::Normal,
+                    block: body,
+                }).into())
             )
             |
             map!(ambiguous_expr!(allow_struct), |e| (FunctionRetTy::Default, e))
         ) >>
-        (ExprKind::Closure(
-            capture,
-            Box::new(FnDecl {
+        (ExprClosure {
+            capture: capture,
+            decl: Box::new(FnDecl {
                 inputs: inputs,
                 output: ret_and_body.0,
                 variadic: false,
             }),
-            Box::new(ret_and_body.1),
-        ))
+            body: Box::new(ret_and_body.1),
+        }.into())
     ));
 
     named!(closure_arg -> FnArg, do_parse!(
         pat: pat >>
         ty: option!(preceded!(punct!(":"), ty)) >>
-        (FnArg::Captured(pat, ty.unwrap_or(Ty::Infer)))
+        (ArgCaptured {
+            pat: pat,
+            ty: ty.unwrap_or_else(|| TyInfer {}.into()),
+        }.into())
     ));
 
     named!(expr_while -> ExprKind, do_parse!(
@@ -755,37 +939,37 @@
         cond: cond >>
         while_block: block >>
         (match cond {
-            Cond::Let(pat, expr) => ExprKind::WhileLet(
-                Box::new(pat),
-                Box::new(expr),
-                while_block,
-                lbl,
-            ),
-            Cond::Expr(cond) => ExprKind::While(
-                Box::new(cond),
-                while_block,
-                lbl,
-            ),
+            Cond::Let(pat, expr) => ExprWhileLet {
+                pat: Box::new(pat),
+                expr: Box::new(expr),
+                body: while_block,
+                label: lbl,
+            }.into(),
+            Cond::Expr(cond) => ExprWhile {
+                cond: Box::new(cond),
+                body: while_block,
+                label: lbl,
+            }.into(),
         })
     ));
 
     named!(expr_continue -> ExprKind, do_parse!(
         keyword!("continue") >>
         lbl: option!(label) >>
-        (ExprKind::Continue(lbl))
+        (ExprContinue { label: lbl }.into())
     ));
 
     named_ambiguous_expr!(expr_break -> ExprKind, allow_struct, do_parse!(
         keyword!("break") >>
         lbl: option!(label) >>
         val: option!(call!(ambiguous_expr, allow_struct, false)) >>
-        (ExprKind::Break(lbl, val.map(Box::new)))
+        (ExprBreak { label: lbl, expr: val.map(Box::new) }.into())
     ));
 
     named_ambiguous_expr!(expr_ret -> ExprKind, allow_struct, do_parse!(
         keyword!("return") >>
         ret_value: option!(ambiguous_expr!(allow_struct)) >>
-        (ExprKind::Ret(ret_value.map(Box::new)))
+        (ExprRet { expr: ret_value.map(Box::new) }.into())
     ));
 
     named!(expr_struct -> ExprKind, do_parse!(
@@ -800,7 +984,11 @@
         )) >>
         cond!(!fields.is_empty() && base.is_none(), option!(punct!(","))) >>
         punct!("}") >>
-        (ExprKind::Struct(path, fields, base.map(Box::new)))
+        (ExprStruct {
+            path: path,
+            fields: fields,
+            rest: base.map(Box::new),
+        }.into())
     ));
 
     named!(field_value -> FieldValue, alt!(
@@ -818,7 +1006,7 @@
         |
         map!(ident, |name: Ident| FieldValue {
             ident: name.clone(),
-            expr: ExprKind::Path(None, name.into()).into(),
+            expr: ExprKind::Path(ExprPath { qself: None, path: name.into() }).into(),
             is_shorthand: true,
             attrs: Vec::new(),
         })
@@ -830,21 +1018,22 @@
         punct!(";") >>
         times: expr >>
         punct!("]") >>
-        (ExprKind::Repeat(Box::new(value), Box::new(times)))
+        (ExprRepeat { expr: Box::new(value), amt: Box::new(times) }.into())
     ));
 
     named!(expr_block -> ExprKind, do_parse!(
         rules: unsafety >>
         b: block >>
-        (ExprKind::Block(rules, Block {
-            stmts: b.stmts,
-        }))
+        (ExprBlock {
+            unsafety: rules,
+            block: Block { stmts: b.stmts },
+        }.into())
     ));
 
     named_ambiguous_expr!(expr_range -> ExprKind, allow_struct, do_parse!(
         limits: range_limits >>
         hi: option!(ambiguous_expr!(allow_struct)) >>
-        (ExprKind::Range(None, hi.map(Box::new), limits))
+        (ExprRange { from: None, to: hi.map(Box::new), limits: limits }.into())
     ));
 
     named!(range_limits -> RangeLimits, alt!(
@@ -853,13 +1042,15 @@
         punct!("..") => { |_| RangeLimits::HalfOpen }
     ));
 
-    named!(expr_path -> ExprKind, map!(qpath, |(qself, path)| ExprKind::Path(qself, path)));
+    named!(expr_path -> ExprKind, map!(qpath, |(qself, path)| {
+        ExprPath { qself: qself, path: path }.into()
+    }));
 
     named_ambiguous_expr!(expr_addr_of -> ExprKind, allow_struct, do_parse!(
         punct!("&") >>
         mutability: mutability >>
         expr: ambiguous_expr!(allow_struct) >>
-        (ExprKind::AddrOf(mutability, Box::new(expr)))
+        (ExprAddrOf { mutbl: mutability, expr: Box::new(expr) }.into())
     ));
 
     named_ambiguous_expr!(and_assign -> Expr, allow_struct, preceded!(
@@ -961,14 +1152,14 @@
 
     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,
+            ExprKind::If(_) |
+            ExprKind::IfLet(_) |
+            ExprKind::While(_) |
+            ExprKind::WhileLet(_) |
+            ExprKind::ForLoop(_) |
+            ExprKind::Loop(_) |
+            ExprKind::Match(_) |
+            ExprKind::Block(_) => false,
 
             _ => true,
         }
@@ -1146,7 +1337,7 @@
 
     named!(pat_lit -> Pat, do_parse!(
         lit: pat_lit_expr >>
-        (if let ExprKind::Path(_, _) = lit.node {
+        (if let ExprKind::Path(_) = lit.node {
             return IResult::Error; // these need to be parsed by pat_path
         } else {
             Pat::Lit(Box::new(lit))
@@ -1165,10 +1356,13 @@
         v: alt!(
             lit => { ExprKind::Lit }
             |
-            path => { |p| ExprKind::Path(None, p) }
+            path => { |p| ExprPath { qself: None, path: p }.into() }
         ) >>
         (if neg.is_some() {
-            ExprKind::Unary(UnOp::Neg, Box::new(v.into())).into()
+            ExprKind::Unary(ExprUnary {
+                op: UnOp::Neg,
+                expr: Box::new(v.into())
+            }).into()
         } else {
             v.into()
         })
@@ -1213,279 +1407,384 @@
 #[cfg(feature = "printing")]
 mod printing {
     use super::*;
-    use {FnArg, FunctionRetTy, Mutability, Ty, Unsafety};
+    use {FnArg, FunctionRetTy, Mutability, Ty, Unsafety, ArgCaptured};
     use attr::FilterAttrs;
     use quote::{Tokens, ToTokens};
 
     impl ToTokens for Expr {
         fn to_tokens(&self, tokens: &mut Tokens) {
             tokens.append_all(self.attrs.outer());
-            match self.node {
-                ExprKind::Box(ref inner) => {
-                    tokens.append("box");
-                    inner.to_tokens(tokens);
+            self.node.to_tokens(tokens)
+        }
+    }
+
+    impl ToTokens for ExprBox {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("box");
+            self.expr.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprInPlace {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("in");
+            self.place.to_tokens(tokens);
+            self.value.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprArray {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("[");
+            tokens.append_separated(&self.exprs, ",");
+            tokens.append("]");
+        }
+    }
+
+    impl ToTokens for ExprCall {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.func.to_tokens(tokens);
+            tokens.append("(");
+            tokens.append_separated(&self.args, ",");
+            tokens.append(")");
+        }
+    }
+
+    impl ToTokens for ExprMethodCall {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.args[0].to_tokens(tokens);
+            tokens.append(".");
+            self.method.to_tokens(tokens);
+            if !self.typarams.is_empty() {
+                tokens.append("::");
+                tokens.append("<");
+                tokens.append_separated(&self.typarams, ",");
+                tokens.append(">");
+            }
+            tokens.append("(");
+            tokens.append_separated(&self.args[1..], ",");
+            tokens.append(")");
+        }
+    }
+
+    impl ToTokens for ExprTup {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("(");
+            tokens.append_separated(&self.args, ",");
+            if self.args.len() == 1 {
+                tokens.append(",");
+            }
+            tokens.append(")");
+        }
+    }
+
+    impl ToTokens for ExprBinary {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.left.to_tokens(tokens);
+            self.op.to_tokens(tokens);
+            self.right.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprUnary {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.op.to_tokens(tokens);
+            self.expr.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprCast {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.expr.to_tokens(tokens);
+            tokens.append("as");
+            self.ty.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprType {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.expr.to_tokens(tokens);
+            tokens.append(":");
+            self.ty.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprIf {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("if");
+            self.cond.to_tokens(tokens);
+            self.if_true.to_tokens(tokens);
+            if let Some(ref else_block) = self.if_false {
+                tokens.append("else");
+                else_block.to_tokens(tokens);
+            }
+        }
+    }
+
+    impl ToTokens for ExprIfLet {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("if");
+            tokens.append("let");
+            self.pat.to_tokens(tokens);
+            tokens.append("=");
+            self.expr.to_tokens(tokens);
+            self.if_true.to_tokens(tokens);
+            if let Some(ref else_block) = self.if_false {
+                tokens.append("else");
+                else_block.to_tokens(tokens);
+            }
+        }
+    }
+
+    impl ToTokens for ExprWhile {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            if let Some(ref label) = self.label {
+                label.to_tokens(tokens);
+                tokens.append(":");
+            }
+            tokens.append("while");
+            self.cond.to_tokens(tokens);
+            self.body.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprWhileLet {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            if let Some(ref label) = self.label {
+                label.to_tokens(tokens);
+                tokens.append(":");
+            }
+            tokens.append("while");
+            tokens.append("let");
+            self.pat.to_tokens(tokens);
+            tokens.append("=");
+            self.expr.to_tokens(tokens);
+            self.body.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprForLoop {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            if let Some(ref label) = self.label {
+                label.to_tokens(tokens);
+                tokens.append(":");
+            }
+            tokens.append("for");
+            self.pat.to_tokens(tokens);
+            tokens.append("in");
+            self.expr.to_tokens(tokens);
+            self.body.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprLoop {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            if let Some(ref label) = self.label {
+                label.to_tokens(tokens);
+                tokens.append(":");
+            }
+            tokens.append("loop");
+            self.body.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprMatch {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("match");
+            self.expr.to_tokens(tokens);
+            tokens.append("{");
+            tokens.append_all(&self.arms);
+            tokens.append("}");
+        }
+    }
+
+    impl ToTokens for ExprCatch {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("do");
+            tokens.append("catch");
+            self.block.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprClosure {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.capture.to_tokens(tokens);
+            tokens.append("|");
+            for (i, input) in self.decl.inputs.iter().enumerate() {
+                if i > 0 {
+                    tokens.append(",");
                 }
-                ExprKind::InPlace(ref place, ref value) => {
-                    tokens.append("in");
-                    place.to_tokens(tokens);
-                    value.to_tokens(tokens);
-                }
-                ExprKind::Array(ref tys) => {
-                    tokens.append("[");
-                    tokens.append_separated(tys, ",");
-                    tokens.append("]");
-                }
-                ExprKind::Call(ref func, ref args) => {
-                    func.to_tokens(tokens);
-                    tokens.append("(");
-                    tokens.append_separated(args, ",");
-                    tokens.append(")");
-                }
-                ExprKind::MethodCall(ref ident, ref ascript, ref args) => {
-                    args[0].to_tokens(tokens);
-                    tokens.append(".");
-                    ident.to_tokens(tokens);
-                    if !ascript.is_empty() {
-                        tokens.append("::");
-                        tokens.append("<");
-                        tokens.append_separated(ascript, ",");
-                        tokens.append(">");
+                match *input {
+                    FnArg::Captured(ArgCaptured { ref pat, ty: Ty::Infer(_) }) => {
+                        pat.to_tokens(tokens);
                     }
-                    tokens.append("(");
-                    tokens.append_separated(&args[1..], ",");
-                    tokens.append(")");
-                }
-                ExprKind::Tup(ref fields) => {
-                    tokens.append("(");
-                    tokens.append_separated(fields, ",");
-                    if fields.len() == 1 {
-                        tokens.append(",");
-                    }
-                    tokens.append(")");
-                }
-                ExprKind::Binary(op, ref left, ref right) => {
-                    left.to_tokens(tokens);
-                    op.to_tokens(tokens);
-                    right.to_tokens(tokens);
-                }
-                ExprKind::Unary(op, ref expr) => {
-                    op.to_tokens(tokens);
-                    expr.to_tokens(tokens);
-                }
-                ExprKind::Lit(ref lit) => lit.to_tokens(tokens),
-                ExprKind::Cast(ref expr, ref ty) => {
-                    expr.to_tokens(tokens);
-                    tokens.append("as");
-                    ty.to_tokens(tokens);
-                }
-                ExprKind::Type(ref expr, ref ty) => {
-                    expr.to_tokens(tokens);
-                    tokens.append(":");
-                    ty.to_tokens(tokens);
-                }
-                ExprKind::If(ref cond, ref then_block, ref else_block) => {
-                    tokens.append("if");
-                    cond.to_tokens(tokens);
-                    then_block.to_tokens(tokens);
-                    if let Some(ref else_block) = *else_block {
-                        tokens.append("else");
-                        else_block.to_tokens(tokens);
-                    }
-                }
-                ExprKind::IfLet(ref pat, ref expr, ref then_block, ref else_block) => {
-                    tokens.append("if");
-                    tokens.append("let");
-                    pat.to_tokens(tokens);
-                    tokens.append("=");
-                    expr.to_tokens(tokens);
-                    then_block.to_tokens(tokens);
-                    if let Some(ref else_block) = *else_block {
-                        tokens.append("else");
-                        else_block.to_tokens(tokens);
-                    }
-                }
-                ExprKind::While(ref cond, ref body, ref label) => {
-                    if let Some(ref label) = *label {
-                        label.to_tokens(tokens);
-                        tokens.append(":");
-                    }
-                    tokens.append("while");
-                    cond.to_tokens(tokens);
-                    body.to_tokens(tokens);
-                }
-                ExprKind::WhileLet(ref pat, ref expr, ref body, ref label) => {
-                    if let Some(ref label) = *label {
-                        label.to_tokens(tokens);
-                        tokens.append(":");
-                    }
-                    tokens.append("while");
-                    tokens.append("let");
-                    pat.to_tokens(tokens);
-                    tokens.append("=");
-                    expr.to_tokens(tokens);
-                    body.to_tokens(tokens);
-                }
-                ExprKind::ForLoop(ref pat, ref expr, ref body, ref label) => {
-                    if let Some(ref label) = *label {
-                        label.to_tokens(tokens);
-                        tokens.append(":");
-                    }
-                    tokens.append("for");
-                    pat.to_tokens(tokens);
-                    tokens.append("in");
-                    expr.to_tokens(tokens);
-                    body.to_tokens(tokens);
-                }
-                ExprKind::Loop(ref body, ref label) => {
-                    if let Some(ref label) = *label {
-                        label.to_tokens(tokens);
-                        tokens.append(":");
-                    }
-                    tokens.append("loop");
-                    body.to_tokens(tokens);
-                }
-                ExprKind::Match(ref expr, ref arms) => {
-                    tokens.append("match");
-                    expr.to_tokens(tokens);
-                    tokens.append("{");
-                    tokens.append_all(arms);
-                    tokens.append("}");
-                }
-                ExprKind::Catch(ref body) => {
-                    tokens.append("do");
-                    tokens.append("catch");
-                    body.to_tokens(tokens);
-                }
-                ExprKind::Closure(capture, ref decl, ref expr) => {
-                    capture.to_tokens(tokens);
-                    tokens.append("|");
-                    for (i, input) in decl.inputs.iter().enumerate() {
-                        if i > 0 {
-                            tokens.append(",");
-                        }
-                        match *input {
-                            FnArg::Captured(ref pat, Ty::Infer) => {
-                                pat.to_tokens(tokens);
-                            }
-                            _ => input.to_tokens(tokens),
-                        }
-                    }
-                    tokens.append("|");
-                    match decl.output {
-                        FunctionRetTy::Default => { /* nothing */ }
-                        FunctionRetTy::Ty(ref ty) => {
-                            tokens.append("->");
-                            ty.to_tokens(tokens);
-                        }
-                    }
-                    expr.to_tokens(tokens);
-                }
-                ExprKind::Block(rules, ref block) => {
-                    rules.to_tokens(tokens);
-                    block.to_tokens(tokens);
-                }
-                ExprKind::Assign(ref var, ref expr) => {
-                    var.to_tokens(tokens);
-                    tokens.append("=");
-                    expr.to_tokens(tokens);
-                }
-                ExprKind::AssignOp(op, ref var, ref expr) => {
-                    var.to_tokens(tokens);
-                    tokens.append(op.assign_op().unwrap());
-                    expr.to_tokens(tokens);
-                }
-                ExprKind::Field(ref expr, ref field) => {
-                    expr.to_tokens(tokens);
-                    tokens.append(".");
-                    field.to_tokens(tokens);
-                }
-                ExprKind::TupField(ref expr, field) => {
-                    expr.to_tokens(tokens);
-                    tokens.append(".");
-                    tokens.append(&field.to_string());
-                }
-                ExprKind::Index(ref expr, ref index) => {
-                    expr.to_tokens(tokens);
-                    tokens.append("[");
-                    index.to_tokens(tokens);
-                    tokens.append("]");
-                }
-                ExprKind::Range(ref from, ref to, limits) => {
-                    from.to_tokens(tokens);
-                    limits.to_tokens(tokens);
-                    to.to_tokens(tokens);
-                }
-                ExprKind::Path(None, ref path) => path.to_tokens(tokens),
-                ExprKind::Path(Some(ref qself), ref path) => {
-                    tokens.append("<");
-                    qself.ty.to_tokens(tokens);
-                    if qself.position > 0 {
-                        tokens.append("as");
-                        for (i, segment) in path.segments
-                                .iter()
-                                .take(qself.position)
-                                .enumerate() {
-                            if i > 0 || path.global {
-                                tokens.append("::");
-                            }
-                            segment.to_tokens(tokens);
-                        }
-                    }
-                    tokens.append(">");
-                    for segment in path.segments.iter().skip(qself.position) {
-                        tokens.append("::");
-                        segment.to_tokens(tokens);
-                    }
-                }
-                ExprKind::AddrOf(mutability, ref expr) => {
-                    tokens.append("&");
-                    mutability.to_tokens(tokens);
-                    expr.to_tokens(tokens);
-                }
-                ExprKind::Break(ref opt_label, ref opt_val) => {
-                    tokens.append("break");
-                    opt_label.to_tokens(tokens);
-                    opt_val.to_tokens(tokens);
-                }
-                ExprKind::Continue(ref opt_label) => {
-                    tokens.append("continue");
-                    opt_label.to_tokens(tokens);
-                }
-                ExprKind::Ret(ref opt_expr) => {
-                    tokens.append("return");
-                    opt_expr.to_tokens(tokens);
-                }
-                ExprKind::Mac(ref mac) => mac.to_tokens(tokens),
-                ExprKind::Struct(ref path, ref fields, ref base) => {
-                    path.to_tokens(tokens);
-                    tokens.append("{");
-                    tokens.append_separated(fields, ",");
-                    if let Some(ref base) = *base {
-                        if !fields.is_empty() {
-                            tokens.append(",");
-                        }
-                        tokens.append("..");
-                        base.to_tokens(tokens);
-                    }
-                    tokens.append("}");
-                }
-                ExprKind::Repeat(ref expr, ref times) => {
-                    tokens.append("[");
-                    expr.to_tokens(tokens);
-                    tokens.append(";");
-                    times.to_tokens(tokens);
-                    tokens.append("]");
-                }
-                ExprKind::Paren(ref expr) => {
-                    tokens.append("(");
-                    expr.to_tokens(tokens);
-                    tokens.append(")");
-                }
-                ExprKind::Try(ref expr) => {
-                    expr.to_tokens(tokens);
-                    tokens.append("?");
+                    _ => input.to_tokens(tokens),
                 }
             }
+            tokens.append("|");
+            match self.decl.output {
+                FunctionRetTy::Default => { /* nothing */ }
+                FunctionRetTy::Ty(ref ty) => {
+                    tokens.append("->");
+                    ty.to_tokens(tokens);
+                }
+            }
+            self.body.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprBlock {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.unsafety.to_tokens(tokens);
+            self.block.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprAssign {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.left.to_tokens(tokens);
+            tokens.append("=");
+            self.right.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprAssignOp {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.left.to_tokens(tokens);
+            tokens.append(self.op.assign_op().unwrap());
+            self.right.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprField {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.expr.to_tokens(tokens);
+            tokens.append(".");
+            self.field.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprTupField {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.expr.to_tokens(tokens);
+            tokens.append(".");
+            tokens.append(&self.field.to_string());
+        }
+    }
+
+    impl ToTokens for ExprIndex {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.expr.to_tokens(tokens);
+            tokens.append("[");
+            self.index.to_tokens(tokens);
+            tokens.append("]");
+        }
+    }
+
+    impl ToTokens for ExprRange {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.from.to_tokens(tokens);
+            self.limits.to_tokens(tokens);
+            self.to.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprPath {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            let qself = match self.qself {
+                Some(ref qself) => qself,
+                None => return self.path.to_tokens(tokens),
+            };
+            tokens.append("<");
+            qself.ty.to_tokens(tokens);
+            if qself.position > 0 {
+                tokens.append("as");
+                for (i, segment) in self.path.segments
+                        .iter()
+                        .take(qself.position)
+                        .enumerate() {
+                    if i > 0 || self.path.global {
+                        tokens.append("::");
+                    }
+                    segment.to_tokens(tokens);
+                }
+            }
+            tokens.append(">");
+            for segment in self.path.segments.iter().skip(qself.position) {
+                tokens.append("::");
+                segment.to_tokens(tokens);
+            }
+        }
+    }
+
+    impl ToTokens for ExprAddrOf {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("&");
+            self.mutbl.to_tokens(tokens);
+            self.expr.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprBreak {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("break");
+            self.label.to_tokens(tokens);
+            self.expr.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprContinue {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("continue");
+            self.label.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprRet {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("return");
+            self.expr.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ExprStruct {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.path.to_tokens(tokens);
+            tokens.append("{");
+            tokens.append_separated(&self.fields, ",");
+            if let Some(ref base) = self.rest {
+                if !self.fields.is_empty() {
+                    tokens.append(",");
+                }
+                tokens.append("..");
+                base.to_tokens(tokens);
+            }
+            tokens.append("}");
+        }
+    }
+
+    impl ToTokens for ExprRepeat {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("[");
+            self.expr.to_tokens(tokens);
+            tokens.append(";");
+            self.amt.to_tokens(tokens);
+            tokens.append("]");
+        }
+    }
+
+    impl ToTokens for ExprParen {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("(");
+            self.expr.to_tokens(tokens);
+            tokens.append(")");
+        }
+    }
+
+    impl ToTokens for ExprTry {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.expr.to_tokens(tokens);
+            tokens.append("?");
         }
     }
 
@@ -1512,7 +1811,7 @@
             tokens.append("=>");
             self.body.to_tokens(tokens);
             match self.body.node {
-                ExprKind::Block(Unsafety::Normal, _) => {
+                ExprKind::Block(ExprBlock { unsafety: Unsafety::Normal, .. }) => {
                     // no comma
                 }
                 _ => tokens.append(","),