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/aster/ty.rs b/src/aster/ty.rs
index 7dff3b4..f43e4a5 100644
--- a/src/aster/ty.rs
+++ b/src/aster/ty.rs
@@ -1,4 +1,5 @@
 use {Generics, Lifetime, MutTy, Mutability, Path, QSelf, Ty, TyParamBound};
+use {TyPath, TySlice, TyNever, TyInfer, TyTup, TyRptr, TyImplTrait};
 use aster::ident::ToIdent;
 use aster::invoke::{Invoke, Identity};
 use aster::lifetime::IntoLifetime;
@@ -36,11 +37,11 @@
     }
 
     pub fn build_path(self, path: Path) -> F::Result {
-        self.build(Ty::Path(None, path))
+        self.build(Ty::Path(TyPath { qself: None, path: path }))
     }
 
     pub fn build_qpath(self, qself: QSelf, path: Path) -> F::Result {
-        self.build(Ty::Path(Some(qself), path))
+        self.build(Ty::Path(TyPath { qself: Some(qself), path: path }))
     }
 
     pub fn path(self) -> PathBuilder<TyPathBuilder<F>> {
@@ -115,7 +116,7 @@
     }
 
     pub fn build_slice(self, ty: Ty) -> F::Result {
-        self.build(Ty::Slice(Box::new(ty)))
+        self.build(Ty::Slice(TySlice { ty: Box::new(ty) }))
     }
 
     pub fn slice(self) -> TyBuilder<TySliceBuilder<F>> {
@@ -131,11 +132,11 @@
     }
 
     pub fn never(self) -> F::Result {
-        self.build(Ty::Never)
+        self.build(Ty::Never(TyNever {}))
     }
 
     pub fn infer(self) -> F::Result {
-        self.build(Ty::Infer)
+        self.build(Ty::Infer(TyInfer {}))
     }
 
     pub fn option(self) -> TyBuilder<TyOptionBuilder<F>> {
@@ -236,7 +237,10 @@
             ty: ty,
             mutability: self.mutability,
         };
-        self.builder.build(Ty::Rptr(self.lifetime, Box::new(ty)))
+        self.builder.build(Ty::Rptr(TyRptr {
+            lifetime: self.lifetime,
+            ty: Box::new(ty),
+        }))
     }
 
     pub fn ty(self) -> TyBuilder<Self> {
@@ -432,7 +436,9 @@
 
     pub fn build(self) -> F::Result {
         let bounds = self.bounds;
-        self.builder.build(Ty::ImplTrait(bounds))
+        self.builder.build(Ty::ImplTrait(TyImplTrait {
+            bounds: bounds,
+        }))
     }
 }
 
@@ -473,7 +479,7 @@
     }
 
     pub fn build(self) -> F::Result {
-        self.builder.build(Ty::Tup(self.tys))
+        self.builder.build(Ty::Tup(TyTup { tys: self.tys }))
     }
 }
 
diff --git a/src/attr.rs b/src/attr.rs
index 0a2b016..fecfd9a 100644
--- a/src/attr.rs
+++ b/src/attr.rs
@@ -2,24 +2,25 @@
 
 use std::iter;
 
-/// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Attribute {
-    pub style: AttrStyle,
+ast_struct! {
+    /// Doc-comments are promoted to attributes that have `is_sugared_doc` = true
+    pub struct Attribute {
+        pub style: AttrStyle,
 
-    /// The path of the attribute.
-    ///
-    /// E.g. `derive` in `#[derive(Copy)]`
-    /// E.g. `crate::precondition` in `#[crate::precondition x < 5]`
-    pub path: Path,
+        /// The path of the attribute.
+        ///
+        /// E.g. `derive` in `#[derive(Copy)]`
+        /// E.g. `crate::precondition` in `#[crate::precondition x < 5]`
+        pub path: Path,
 
-    /// Any tokens after the path.
-    ///
-    /// E.g. `( Copy )` in `#[derive(Copy)]`
-    /// E.g. `x < 5` in `#[crate::precondition x < 5]`
-    pub tts: Vec<TokenTree>,
+        /// Any tokens after the path.
+        ///
+        /// E.g. `( Copy )` in `#[derive(Copy)]`
+        /// E.g. `x < 5` in `#[crate::precondition x < 5]`
+        pub tts: Vec<TokenTree>,
 
-    pub is_sugared_doc: bool,
+        pub is_sugared_doc: bool,
+    }
 }
 
 impl Attribute {
@@ -49,7 +50,11 @@
                             if tts.len() >= 3 {
                                 if let TokenTree::Token(Token::Eq) = tts[1] {
                                     if let TokenTree::Token(Token::Literal(ref lit)) = tts[2] {
-                                        return Some((NestedMetaItem::MetaItem(MetaItem::NameValue(ident.clone(), lit.clone())), &tts[3..]));
+                                        let pair = MetaNameValue {
+                                            ident: ident.clone(),
+                                            lit: lit.clone(),
+                                        };
+                                        return Some((NestedMetaItem::MetaItem(MetaItem::NameValue(pair)), &tts[3..]));
                                     }
                                 }
                             }
@@ -58,7 +63,11 @@
                                 if let TokenTree::Delimited(Delimited { delim: DelimToken::Paren, tts: ref inner_tts }) = tts[1] {
                                     return match list_of_nested_meta_items_from_tokens(vec![], inner_tts) {
                                         Some(nested_meta_items) => {
-                                            Some((NestedMetaItem::MetaItem(MetaItem::List(ident.clone(), nested_meta_items)), &tts[2..]))
+                                            let list = MetaItemList {
+                                                ident: ident.clone(),
+                                                nested: nested_meta_items,
+                                            };
+                                            Some((NestedMetaItem::MetaItem(MetaItem::List(list)), &tts[2..]))
                                         }
 
                                         None => None
@@ -97,7 +106,10 @@
                 }
 
                 if let Some(nested_meta_items) = list_of_nested_meta_items_from_tokens(vec![], tts) {
-                    return Some(MetaItem::List(name.clone(), nested_meta_items));
+                    return Some(MetaItem::List(MetaItemList {
+                        ident: name.clone(),
+                        nested: nested_meta_items,
+                    }));
                 }
             }
         }
@@ -105,7 +117,10 @@
         if self.tts.len() == 2 {
             if let TokenTree::Token(Token::Eq) = self.tts[0] {
                 if let TokenTree::Token(Token::Literal(ref lit)) = self.tts[1] {
-                    return Some(MetaItem::NameValue(name.clone(), lit.clone()));
+                    return Some(MetaItem::NameValue(MetaNameValue {
+                        ident: name.clone(),
+                        lit: lit.clone(),
+                    }));
                 }
             }
         }
@@ -114,37 +129,60 @@
     }
 }
 
-/// Distinguishes between Attributes that decorate items and Attributes that
-/// are contained as statements within items. These two cases need to be
-/// distinguished for pretty-printing.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum AttrStyle {
-    /// Attribute of the form `#![...]`.
-    Outer,
+ast_enum! {
+    /// Distinguishes between Attributes that decorate items and Attributes that
+    /// are contained as statements within items. These two cases need to be
+    /// distinguished for pretty-printing.
+    #[derive(Copy)]
+    pub enum AttrStyle {
+        /// Attribute of the form `#![...]`.
+        Outer,
 
-    /// Attribute of the form `#[...]`.
-    Inner,
+        /// Attribute of the form `#[...]`.
+        Inner,
+    }
 }
 
-/// A compile-time attribute item.
-///
-/// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum MetaItem {
-    /// Word meta item.
+ast_enum_of_structs! {
+    /// A compile-time attribute item.
     ///
-    /// E.g. `test` as in `#[test]`
-    Word(Ident),
+    /// E.g. `#[test]`, `#[derive(..)]` or `#[feature = "foo"]`
+    pub enum MetaItem {
+        /// Word meta item.
+        ///
+        /// E.g. `test` as in `#[test]`
+        pub Word(Ident),
 
-    /// List meta item.
-    ///
-    /// E.g. `derive(..)` as in `#[derive(..)]`
-    List(Ident, Vec<NestedMetaItem>),
+        /// List meta item.
+        ///
+        /// E.g. `derive(..)` as in `#[derive(..)]`
+        pub List(MetaItemList {
+            /// Name of this attribute.
+            ///
+            /// E.g. `derive` in `#[derive(..)]`
+            pub ident: Ident,
 
-    /// Name-value meta item.
-    ///
-    /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
-    NameValue(Ident, Lit),
+            /// Arguments to this attribute
+            ///
+            /// E.g. `..` in `#[derive(..)]`
+            pub nested: Vec<NestedMetaItem>,
+        }),
+
+        /// Name-value meta item.
+        ///
+        /// E.g. `feature = "foo"` as in `#[feature = "foo"]`
+        pub NameValue(MetaNameValue {
+            /// Name of this attribute.
+            ///
+            /// E.g. `feature` in `#[feature = "foo"]`
+            pub ident: Ident,
+
+            /// Arguments to this attribute
+            ///
+            /// E.g. `"foo"` in `#[feature = "foo"]`
+            pub lit: Lit,
+        }),
+    }
 }
 
 impl MetaItem {
@@ -154,27 +192,28 @@
     /// `feature` as in `#[feature = "foo"]`.
     pub fn name(&self) -> &str {
         match *self {
-            MetaItem::Word(ref name) |
-            MetaItem::List(ref name, _) |
-            MetaItem::NameValue(ref name, _) => name.as_ref(),
+            MetaItem::Word(ref name) => name.as_ref(),
+            MetaItem::NameValue(ref pair) => pair.ident.as_ref(),
+            MetaItem::List(ref list) => list.ident.as_ref(),
         }
     }
 }
 
-/// Possible values inside of compile-time attribute lists.
-///
-/// E.g. the '..' in `#[name(..)]`.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum NestedMetaItem {
-    /// A full `MetaItem`.
+ast_enum_of_structs! {
+    /// Possible values inside of compile-time attribute lists.
     ///
-    /// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Word(Ident::from("Copy"))`.
-    MetaItem(MetaItem),
+    /// E.g. the '..' in `#[name(..)]`.
+    pub enum NestedMetaItem {
+        /// A full `MetaItem`.
+        ///
+        /// E.g. `Copy` in `#[derive(Copy)]` would be a `MetaItem::Word(Ident::from("Copy"))`.
+        pub MetaItem(MetaItem),
 
-    /// A Rust literal.
-    ///
-    /// E.g. `"name"` in `#[rename("name")]`.
-    Literal(Lit),
+        /// A Rust literal.
+        ///
+        /// E.g. `"name"` in `#[rename("name")]`.
+        pub Literal(Lit),
+    }
 }
 
 pub trait FilterAttrs<'a> {
@@ -378,37 +417,20 @@
         }
     }
 
-    impl ToTokens for MetaItem {
+    impl ToTokens for MetaItemList {
         fn to_tokens(&self, tokens: &mut Tokens) {
-            match *self {
-                MetaItem::Word(ref ident) => {
-                    ident.to_tokens(tokens);
-                }
-                MetaItem::List(ref ident, ref inner) => {
-                    ident.to_tokens(tokens);
-                    tokens.append("(");
-                    tokens.append_separated(inner, ",");
-                    tokens.append(")");
-                }
-                MetaItem::NameValue(ref name, ref value) => {
-                    name.to_tokens(tokens);
-                    tokens.append("=");
-                    value.to_tokens(tokens);
-                }
-            }
+            self.ident.to_tokens(tokens);
+            tokens.append("(");
+            tokens.append_separated(&self.nested, ",");
+            tokens.append(")");
         }
     }
 
-    impl ToTokens for NestedMetaItem {
+    impl ToTokens for MetaNameValue {
         fn to_tokens(&self, tokens: &mut Tokens) {
-            match *self {
-                NestedMetaItem::MetaItem(ref nested) => {
-                    nested.to_tokens(tokens);
-                }
-                NestedMetaItem::Literal(ref lit) => {
-                    lit.to_tokens(tokens);
-                }
-            }
+            self.ident.to_tokens(tokens);
+            tokens.append("=");
+            self.lit.to_tokens(tokens);
         }
     }
 }
diff --git a/src/constant.rs b/src/constant.rs
index ad0d325..2267b50 100644
--- a/src/constant.rs
+++ b/src/constant.rs
@@ -1,38 +1,72 @@
 use super::*;
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum ConstExpr {
-    /// A function call
-    ///
-    /// The first field resolves to the function itself,
-    /// and the second field is the list of arguments
-    Call(Box<ConstExpr>, Vec<ConstExpr>),
+ast_enum_of_structs! {
+    pub enum ConstExpr {
+        /// A function call
+        pub Call(ConstCall {
+            /// The function being called
+            pub func: Box<ConstExpr>,
 
-    /// A binary operation (For example: `a + b`, `a * b`)
-    Binary(BinOp, Box<ConstExpr>, Box<ConstExpr>),
+            /// The arguments to the function being called
+            pub args: Vec<ConstExpr>,
+        }),
 
-    /// A unary operation (For example: `!x`, `*x`)
-    Unary(UnOp, Box<ConstExpr>),
+        /// A binary operation (For example: `a + b`, `a * b`)
+        pub Binary(ConstBinary {
+            /// The binary operation this represents
+            pub op: BinOp,
 
-    /// A literal (For example: `1`, `"foo"`)
-    Lit(Lit),
+            /// The left-hand-side of the constant binary op
+            pub left: Box<ConstExpr>,
 
-    /// A cast (`foo as f64`)
-    Cast(Box<ConstExpr>, Box<Ty>),
+            /// The right-hand-side of the constant binary op
+            pub right: Box<ConstExpr>,
+        }),
 
-    /// Variable reference, possibly containing `::` and/or type
-    /// parameters, e.g. foo::bar::<baz>.
-    Path(Path),
+        /// A unary operation (For example: `!x`, `*x`)
+        pub Unary(ConstUnary {
+            /// Operation being performed
+            pub op: UnOp,
 
-    /// An indexing operation (`foo[2]`)
-    Index(Box<ConstExpr>, Box<ConstExpr>),
+            /// Expression acted on
+            pub expr: Box<ConstExpr>,
+        }),
 
-    /// No-op: used solely so we can pretty-print faithfully
-    Paren(Box<ConstExpr>),
+        /// A literal (For example: `1`, `"foo"`)
+        pub Lit(Lit),
 
-    /// If compiling with full support for expression syntax, any expression is
-    /// allowed
-    Other(Other),
+        /// A cast (`foo as f64`)
+        pub Cast(ConstCast {
+            /// Value being casted
+            pub expr: Box<ConstExpr>,
+
+            /// Type casted to
+            pub ty: Box<Ty>,
+        }),
+
+        /// Variable reference, possibly containing `::` and/or type
+        /// parameters, e.g. foo::bar::<baz>.
+        pub Path(Path),
+
+        /// An indexing operation (`foo[2]`)
+        pub Index(ConstIndex {
+            /// Value that is being indexed
+            pub expr: Box<ConstExpr>,
+
+            /// Index expression
+            pub index: Box<ConstExpr>,
+        }),
+
+        /// No-op: used solely so we can pretty-print faithfully
+        pub Paren(ConstParen {
+            /// Expression that's parenthesized
+            pub expr: Box<ConstExpr>,
+        }),
+
+        /// If compiling with full support for expression syntax, any expression is
+        /// allowed
+        pub Other(Other),
+    }
 }
 
 #[cfg(not(feature = "full"))]
@@ -69,20 +103,20 @@
         ) >>
         many0!(alt!(
             tap!(args: and_call => {
-                e = ConstExpr::Call(Box::new(e), args);
+                e = ConstCall { func: Box::new(e), args: args }.into();
             })
             |
             tap!(more: and_binary => {
                 let (op, other) = more;
-                e = ConstExpr::Binary(op, Box::new(e), Box::new(other));
+                e = ConstBinary { op: op, left: Box::new(e), right: Box::new(other) }.into();
             })
             |
             tap!(ty: and_cast => {
-                e = ConstExpr::Cast(Box::new(e), Box::new(ty));
+                e = ConstCast { expr: Box::new(e), ty: Box::new(ty) }.into();
             })
             |
             tap!(i: and_index => {
-                e = ConstExpr::Index(Box::new(e), Box::new(i));
+                e = ConstIndex { expr: Box::new(e), index: Box::new(i) }.into();
             })
         )) >>
         (e)
@@ -100,7 +134,7 @@
     named!(expr_unary -> ConstExpr, do_parse!(
         operator: unop >>
         operand: const_expr >>
-        (ConstExpr::Unary(operator, Box::new(operand)))
+        (ConstUnary { op: operator, expr: Box::new(operand) }.into())
     ));
 
     named!(expr_lit -> ConstExpr, map!(lit, ConstExpr::Lit));
@@ -113,7 +147,7 @@
         punct!("(") >>
         e: const_expr >>
         punct!(")") >>
-        (ConstExpr::Paren(Box::new(e)))
+        (ConstParen { expr: Box::new(e) }.into())
     ));
 
     named!(and_cast -> Ty, do_parse!(
@@ -128,46 +162,52 @@
     use super::*;
     use quote::{Tokens, ToTokens};
 
-    impl ToTokens for ConstExpr {
+    impl ToTokens for ConstCall {
         fn to_tokens(&self, tokens: &mut Tokens) {
-            match *self {
-                ConstExpr::Call(ref func, ref args) => {
-                    func.to_tokens(tokens);
-                    tokens.append("(");
-                    tokens.append_separated(args, ",");
-                    tokens.append(")");
-                }
-                ConstExpr::Binary(op, ref left, ref right) => {
-                    left.to_tokens(tokens);
-                    op.to_tokens(tokens);
-                    right.to_tokens(tokens);
-                }
-                ConstExpr::Unary(op, ref expr) => {
-                    op.to_tokens(tokens);
-                    expr.to_tokens(tokens);
-                }
-                ConstExpr::Lit(ref lit) => lit.to_tokens(tokens),
-                ConstExpr::Cast(ref expr, ref ty) => {
-                    expr.to_tokens(tokens);
-                    tokens.append("as");
-                    ty.to_tokens(tokens);
-                }
-                ConstExpr::Path(ref path) => path.to_tokens(tokens),
-                ConstExpr::Index(ref expr, ref index) => {
-                    expr.to_tokens(tokens);
-                    tokens.append("[");
-                    index.to_tokens(tokens);
-                    tokens.append("]");
-                }
-                ConstExpr::Paren(ref expr) => {
-                    tokens.append("(");
-                    expr.to_tokens(tokens);
-                    tokens.append(")");
-                }
-                ConstExpr::Other(ref other) => {
-                    other.to_tokens(tokens);
-                }
-            }
+            self.func.to_tokens(tokens);
+            tokens.append("(");
+            tokens.append_separated(&self.args, ",");
+            tokens.append(")");
+        }
+    }
+
+    impl ToTokens for ConstBinary {
+        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 ConstUnary {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.op.to_tokens(tokens);
+            self.expr.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ConstCast {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.expr.to_tokens(tokens);
+            tokens.append("as");
+            self.ty.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for ConstIndex {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.expr.to_tokens(tokens);
+            tokens.append("[");
+            self.index.to_tokens(tokens);
+            tokens.append("]");
+        }
+    }
+
+    impl ToTokens for ConstParen {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("(");
+            self.expr.to_tokens(tokens);
+            tokens.append(")");
         }
     }
 
diff --git a/src/data.rs b/src/data.rs
index 69df303..d01d9e5 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -1,32 +1,34 @@
 use super::*;
 
-/// An enum variant.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Variant {
-    /// Name of the variant.
-    pub ident: Ident,
+ast_struct! {
+    /// An enum variant.
+    pub struct Variant {
+        /// Name of the variant.
+        pub ident: Ident,
 
-    /// Attributes tagged on the variant.
-    pub attrs: Vec<Attribute>,
+        /// Attributes tagged on the variant.
+        pub attrs: Vec<Attribute>,
 
-    /// Type of variant.
-    pub data: VariantData,
+        /// Type of variant.
+        pub data: VariantData,
 
-    /// Explicit discriminant, e.g. `Foo = 1`
-    pub discriminant: Option<ConstExpr>,
+        /// Explicit discriminant, e.g. `Foo = 1`
+        pub discriminant: Option<ConstExpr>,
+    }
 }
 
-/// Data stored within an enum variant or struct.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum VariantData {
-    /// Struct variant, e.g. `Point { x: f64, y: f64 }`.
-    Struct(Vec<Field>),
+ast_enum! {
+    /// Data stored within an enum variant or struct.
+    pub enum VariantData {
+        /// Struct variant, e.g. `Point { x: f64, y: f64 }`.
+        Struct(Vec<Field>),
 
-    /// Tuple variant, e.g. `Some(T)`.
-    Tuple(Vec<Field>),
+        /// Tuple variant, e.g. `Some(T)`.
+        Tuple(Vec<Field>),
 
-    /// Unit variant, e.g. `None`.
-    Unit,
+        /// Unit variant, e.g. `None`.
+        Unit,
+    }
 }
 
 impl VariantData {
@@ -49,39 +51,40 @@
     }
 }
 
-/// A field of a struct or enum variant.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Field {
-    /// Name of the field, if any.
-    ///
-    /// Fields of tuple structs have no names.
-    pub ident: Option<Ident>,
+ast_struct! {
+    /// A field of a struct or enum variant.
+    pub struct Field {
+        /// Name of the field, if any.
+        ///
+        /// Fields of tuple structs have no names.
+        pub ident: Option<Ident>,
 
-    /// Visibility of the field.
-    pub vis: Visibility,
+        /// Visibility of the field.
+        pub vis: Visibility,
 
-    /// Attributes tagged on the field.
-    pub attrs: Vec<Attribute>,
+        /// Attributes tagged on the field.
+        pub attrs: Vec<Attribute>,
 
-    /// Type of the field.
-    pub ty: Ty,
+        /// Type of the field.
+        pub ty: Ty,
+    }
 }
 
+ast_enum! {
+    /// Visibility level of an item.
+    pub enum Visibility {
+        /// Public, i.e. `pub`.
+        Public,
 
-/// Visibility level of an item.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Visibility {
-    /// Public, i.e. `pub`.
-    Public,
+        /// Crate-visible, i.e. `pub(crate)`.
+        Crate,
 
-    /// Crate-visible, i.e. `pub(crate)`.
-    Crate,
+        /// Restricted, e.g. `pub(self)` or `pub(super)` or `pub(in some::module)`.
+        Restricted(Box<Path>),
 
-    /// Restricted, e.g. `pub(self)` or `pub(super)` or `pub(in some::module)`.
-    Restricted(Box<Path>),
-
-    /// Inherited, i.e. private.
-    Inherited,
+        /// Inherited, i.e. private.
+        Inherited,
+    }
 }
 
 #[cfg(feature = "parsing")]
diff --git a/src/derive.rs b/src/derive.rs
index 306c352..3b22b0f 100644
--- a/src/derive.rs
+++ b/src/derive.rs
@@ -1,32 +1,35 @@
 use super::*;
 
-/// Struct or enum sent to a `proc_macro_derive` macro.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct DeriveInput {
-    /// Name of the struct or enum.
-    pub ident: Ident,
+ast_struct! {
+    /// Struct or enum sent to a `proc_macro_derive` macro.
+    pub struct DeriveInput {
+        /// Name of the struct or enum.
+        pub ident: Ident,
 
-    /// Visibility of the struct or enum.
-    pub vis: Visibility,
+        /// Visibility of the struct or enum.
+        pub vis: Visibility,
 
-    /// Attributes tagged on the whole struct or enum.
-    pub attrs: Vec<Attribute>,
+        /// Attributes tagged on the whole struct or enum.
+        pub attrs: Vec<Attribute>,
 
-    /// Generics required to complete the definition.
-    pub generics: Generics,
+        /// Generics required to complete the definition.
+        pub generics: Generics,
 
-    /// Data within the struct or enum.
-    pub body: Body,
+        /// Data within the struct or enum.
+        pub body: Body,
+    }
 }
 
-/// Body of a derived struct or enum.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Body {
-    /// It's an enum.
-    Enum(Vec<Variant>),
 
-    /// It's a struct.
-    Struct(VariantData),
+ast_enum! {
+    /// Body of a derived struct or enum.
+    pub enum Body {
+        /// It's an enum.
+        Enum(Vec<Variant>),
+
+        /// It's a struct.
+        Struct(VariantData),
+    }
 }
 
 #[cfg(feature = "parsing")]
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(","),
diff --git a/src/fold.rs b/src/fold.rs
index def73f2..3a5c763 100644
--- a/src/fold.rs
+++ b/src/fold.rs
@@ -198,59 +198,89 @@
 }
 
 pub fn noop_fold_ty<F: ?Sized + Folder>(folder: &mut F, ty: Ty) -> Ty {
+    use ty::*;
     use Ty::*;
+
     match ty {
-        Slice(inner) => Slice(inner.lift(|v| folder.fold_ty(v))),
-        Paren(inner) => Paren(inner.lift(|v| folder.fold_ty(v))),
-        Ptr(mutable_type) => {
-            let mutable_type_ = *mutable_type;
-            let MutTy { ty, mutability }: MutTy = mutable_type_;
-            Ptr(Box::new(MutTy {
-                             ty: folder.fold_ty(ty),
-                             mutability: mutability,
-                         }))
+        Slice(TySlice { ty }) => {
+            Slice(TySlice {
+                ty: ty.lift(|v| folder.fold_ty(v)),
+            })
         }
-        Rptr(opt_lifetime, mutable_type) => {
-            let mutable_type_ = *mutable_type;
-            let MutTy { ty, mutability }: MutTy = mutable_type_;
-            Rptr(opt_lifetime.map(|l| folder.fold_lifetime(l)),
-                 Box::new(MutTy {
-                              ty: folder.fold_ty(ty),
-                              mutability: mutability,
-                          }))
+        Paren(TyParen { ty }) => {
+            Paren(TyParen {
+                ty: ty.lift(|v| folder.fold_ty(v)),
+            })
         }
-        Never => Never,
-        Infer => Infer,
-        Tup(tuple_element_types) => Tup(tuple_element_types.lift(|x| folder.fold_ty(x))),
-        BareFn(bare_fn) => {
-            let bf_ = *bare_fn;
-            let BareFnTy { unsafety, abi, lifetimes, inputs, output, variadic } = bf_;
-            BareFn(Box::new(BareFnTy {
-                                unsafety: unsafety,
-                                abi: abi,
-                                lifetimes: lifetimes.lift(|l| folder.fold_lifetime_def(l)),
-                                inputs: inputs.lift(|v| {
-                BareFnArg {
-                    name: v.name.map(|n| folder.fold_ident(n)),
-                    ty: folder.fold_ty(v.ty),
-                }
-            }),
-                                output: folder.fold_fn_ret_ty(output),
-                                variadic: variadic,
-                            }))
+        Ptr(TyPtr { ty }) => {
+            let ty = *ty;
+            let MutTy { ty, mutability } = ty;
+            Ptr(TyPtr {
+                ty: Box::new(MutTy {
+                    ty: folder.fold_ty(ty),
+                    mutability: mutability,
+                }),
+            })
         }
-        Path(maybe_qself, path) => {
-            Path(maybe_qself.map(|v| noop_fold_qself(folder, v)),
-                 folder.fold_path(path))
+        Rptr(TyRptr { lifetime, ty }) => {
+            let ty = *ty;
+            let MutTy { ty, mutability } = ty;
+            Rptr(TyRptr {
+                lifetime: lifetime.map(|l| folder.fold_lifetime(l)),
+                ty: Box::new(MutTy {
+                    ty: folder.fold_ty(ty),
+                    mutability: mutability,
+                }),
+            })
         }
-        Array(inner, len) => {
-            Array({
-                      inner.lift(|v| folder.fold_ty(v))
-                  },
-                  folder.fold_const_expr(len))
+        Never(t) => Never(t),
+        Infer(t) => Infer(t),
+        Tup(TyTup { tys }) => {
+            Tup(TyTup {
+                tys: tys.lift(|x| folder.fold_ty(x)),
+            })
         }
-        TraitObject(bounds) => TraitObject(bounds.lift(|v| folder.fold_ty_param_bound(v))),
-        ImplTrait(bounds) => ImplTrait(bounds.lift(|v| folder.fold_ty_param_bound(v))),
+        BareFn(TyBareFn { ty }) => {
+            let ty = *ty;
+            let BareFnTy { unsafety, abi, lifetimes, inputs, output, variadic } = ty;
+            BareFn(TyBareFn {
+                ty: Box::new(BareFnTy {
+                    unsafety: unsafety,
+                    abi: abi,
+                    lifetimes: lifetimes.lift(|l| folder.fold_lifetime_def(l)),
+                    inputs: inputs.lift(|v| {
+                        BareFnArg {
+                            name: v.name.map(|n| folder.fold_ident(n)),
+                            ty: folder.fold_ty(v.ty),
+                        }
+                    }),
+                    output: folder.fold_fn_ret_ty(output),
+                    variadic: variadic,
+                }),
+            })
+        }
+        Path(TyPath { qself, path }) => {
+            Path(TyPath {
+                qself: qself.map(|v| noop_fold_qself(folder, v)),
+                path: folder.fold_path(path),
+            })
+        }
+        Array(TyArray { ty, amt }) => {
+            Array(TyArray {
+                ty: ty.lift(|v| folder.fold_ty(v)),
+                amt: folder.fold_const_expr(amt),
+            })
+        }
+        TraitObject(TyTraitObject { bounds }) => {
+            TraitObject(TyTraitObject {
+                bounds: bounds.lift(|v| folder.fold_ty_param_bound(v)),
+            })
+        }
+        ImplTrait(TyImplTrait { bounds }) => {
+            ImplTrait(TyImplTrait {
+                bounds: bounds.lift(|v| folder.fold_ty_param_bound(v)),
+            })
+        }
         Mac(mac) => Mac(folder.fold_mac(mac)),
     }
 }
@@ -435,29 +465,48 @@
 }
 
 pub fn noop_fold_const_expr<F: ?Sized + Folder>(folder: &mut F, expr: ConstExpr) -> ConstExpr {
-    use ConstExpr::*;
+    use constant::*;
+    use constant::ConstExpr::*;
+
     match expr {
-        Call(f, args) => {
-            Call(f.lift(|e| folder.fold_const_expr(e)),
-                 args.lift(|v| folder.fold_const_expr(v)))
+        Call(ConstCall { func, args }) => {
+            Call(ConstCall {
+                func: func.lift(|e| folder.fold_const_expr(e)),
+                args: args.lift(|v| folder.fold_const_expr(v)),
+            })
         }
-        Binary(op, lhs, rhs) => {
-            Binary(op,
-                   lhs.lift(|e| folder.fold_const_expr(e)),
-                   rhs.lift(|e| folder.fold_const_expr(e)))
+        Binary(ConstBinary { op, left, right }) => {
+            Binary(ConstBinary {
+                op: op,
+                left: left.lift(|e| folder.fold_const_expr(e)),
+                right: right.lift(|e| folder.fold_const_expr(e)),
+            })
         }
-        Unary(op, e) => Unary(op, e.lift(|e| folder.fold_const_expr(e))),
+        Unary(ConstUnary { op, expr }) => {
+            Unary(ConstUnary {
+                op: op,
+                expr: expr.lift(|e| folder.fold_const_expr(e)),
+            })
+        }
         Lit(l) => Lit(folder.fold_lit(l)),
-        Cast(e, ty) => {
-            Cast(e.lift(|e| folder.fold_const_expr(e)),
-                 ty.lift(|v| folder.fold_ty(v)))
+        Cast(ConstCast { expr, ty }) => {
+            Cast(ConstCast {
+                expr: expr.lift(|e| folder.fold_const_expr(e)),
+                ty: ty.lift(|v| folder.fold_ty(v)),
+            })
         }
         Path(p) => Path(folder.fold_path(p)),
-        Index(o, i) => {
-            Index(o.lift(|e| folder.fold_const_expr(e)),
-                  i.lift(|e| folder.fold_const_expr(e)))
+        Index(ConstIndex { expr, index }) => {
+            Index(ConstIndex {
+                expr: expr.lift(|e| folder.fold_const_expr(e)),
+                index: index.lift(|e| folder.fold_const_expr(e)),
+            })
         }
-        Paren(no_op) => Paren(no_op.lift(|e| folder.fold_const_expr(e))),
+        Paren(ConstParen { expr }) => {
+            Paren(ConstParen {
+                expr: expr.lift(|e| folder.fold_const_expr(e)),
+            })
+        }
         Other(e) => Other(noop_fold_other_const_expr(folder, e)),
     }
 }
@@ -537,70 +586,113 @@
 pub fn noop_fold_item<F: ?Sized + Folder>(folder: &mut F,
                                           Item { ident, vis, attrs, node }: Item)
                                           -> Item {
+    use item::*;
     use ItemKind::*;
     Item {
         ident: folder.fold_ident(ident.clone()),
         vis: noop_fold_vis(folder, vis),
         attrs: attrs.lift(|a| folder.fold_attribute(a)),
         node: match node {
-            ExternCrate(name) => ExternCrate(name.map(|i| folder.fold_ident(i))),
-            Use(view_path) => Use(Box::new(folder.fold_view_path(*view_path))),
-            Static(ty, mutability, expr) => {
-                Static(Box::new(folder.fold_ty(*ty)),
-                       mutability,
-                       expr.lift(|e| folder.fold_expr(e)))
+            ExternCrate(ItemExternCrate { original }) => {
+                ExternCrate(ItemExternCrate {
+                    original: original.map(|i| folder.fold_ident(i)),
+                })
             }
-            Const(ty, expr) => {
-                Const(ty.lift(|ty| folder.fold_ty(ty)),
-                      expr.lift(|e| folder.fold_expr(e)))
+            Use(ItemUse { path }) => {
+                Use(ItemUse {
+                    path: Box::new(folder.fold_view_path(*path)),
+                })
             }
-            Fn(fn_decl, unsafety, constness, abi, generics, block) => {
-                Fn(fn_decl.lift(|v| folder.fold_fn_decl(v)),
-                   unsafety,
-                   constness,
-                   abi,
-                   folder.fold_generics(generics),
-                   block.lift(|v| folder.fold_block(v)))
+            Static(ItemStatic { ty, mutbl, expr }) => {
+                Static(ItemStatic {
+                    ty: Box::new(folder.fold_ty(*ty)),
+                    mutbl: mutbl,
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                })
             }
-            Mod(items) => Mod(items.map(|items| items.lift(|i| folder.fold_item(i)))),
-            ForeignMod(super::ForeignMod { abi, items }) => {
-                ForeignMod(super::ForeignMod {
-                               abi: abi,
-                               items: items.lift(|foreign_item| {
-                                                     folder.fold_foreign_item(foreign_item)
-                                                 }),
-                           })
+            Const(ItemConst { ty, expr }) => {
+                Const(ItemConst {
+                    ty: ty.lift(|ty| folder.fold_ty(ty)),
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                })
             }
-            Ty(ty, generics) => {
-                Ty(ty.lift(|ty| folder.fold_ty(ty)),
-                   folder.fold_generics(generics))
+            Fn(ItemFn { decl, unsafety, constness, abi, generics, block }) => {
+                Fn(ItemFn {
+                    decl: decl.lift(|v| folder.fold_fn_decl(v)),
+                    unsafety: unsafety,
+                    constness: constness,
+                    abi: abi,
+                    generics: folder.fold_generics(generics),
+                    block: block.lift(|v| folder.fold_block(v)),
+                })
             }
-            Enum(variants, generics) => {
-                Enum(variants.lift(|v| folder.fold_variant(v)),
-                     folder.fold_generics(generics))
+            Mod(ItemMod { items }) => {
+                Mod(ItemMod {
+                    items: items.map(|items| items.lift(|i| folder.fold_item(i))),
+                })
             }
-            Struct(variant_data, generics) => {
-                Struct(folder.fold_variant_data(variant_data),
-                       folder.fold_generics(generics))
+            ForeignMod(ItemForeignMod { abi, items }) => {
+                ForeignMod(ItemForeignMod {
+                    abi: abi,
+                    items: items.lift(|foreign_item| {
+                        folder.fold_foreign_item(foreign_item)
+                    }),
+                })
             }
-            Union(variant_data, generics) => {
-                Union(folder.fold_variant_data(variant_data),
-                      folder.fold_generics(generics))
+            Ty(ItemTy { ty, generics }) => {
+                Ty(ItemTy {
+                    ty: ty.lift(|ty| folder.fold_ty(ty)),
+                    generics: folder.fold_generics(generics),
+                })
             }
-            Trait(unsafety, generics, typbs, trait_items) => {
-                Trait(unsafety,
-                      folder.fold_generics(generics),
-                      typbs.lift(|typb| folder.fold_ty_param_bound(typb)),
-                      trait_items.lift(|ti| folder.fold_trait_item(ti)))
+            Enum(ItemEnum { variants, generics }) => {
+                Enum(ItemEnum {
+                    variants: variants.lift(|v| folder.fold_variant(v)),
+                    generics: folder.fold_generics(generics),
+                })
             }
-            DefaultImpl(unsafety, path) => DefaultImpl(unsafety, folder.fold_path(path)),
-            Impl(unsafety, impl_polarity, generics, path, ty, impl_items) => {
-                Impl(unsafety,
-                     impl_polarity,
-                     folder.fold_generics(generics),
-                     path.map(|p| folder.fold_path(p)),
-                     ty.lift(|ty| folder.fold_ty(ty)),
-                     impl_items.lift(|i| folder.fold_impl_item(i)))
+            Struct(ItemStruct { data, generics }) => {
+                Struct(ItemStruct {
+                    data: folder.fold_variant_data(data),
+                    generics: folder.fold_generics(generics),
+                })
+            }
+            Union(ItemUnion { data, generics }) => {
+                Union(ItemUnion {
+                    data: folder.fold_variant_data(data),
+                    generics: folder.fold_generics(generics),
+                })
+            }
+            Trait(ItemTrait { unsafety, generics, supertraits, items }) => {
+                Trait(ItemTrait {
+                    unsafety: unsafety,
+                    generics: folder.fold_generics(generics),
+                    supertraits: supertraits.lift(|typb| folder.fold_ty_param_bound(typb)),
+                    items: items.lift(|ti| folder.fold_trait_item(ti)),
+                })
+            }
+            DefaultImpl(ItemDefaultImpl { unsafety, path }) => {
+                DefaultImpl(ItemDefaultImpl {
+                    unsafety: unsafety,
+                    path: folder.fold_path(path),
+                })
+            }
+            Impl(ItemImpl {
+                unsafety,
+                polarity,
+                generics,
+                trait_,
+                self_ty,
+                items,
+            }) => {
+                Impl(ItemImpl {
+                    unsafety: unsafety,
+                    polarity: polarity,
+                    generics: folder.fold_generics(generics),
+                    trait_: trait_.map(|p| folder.fold_path(p)),
+                    self_ty: self_ty.lift(|ty| folder.fold_ty(ty)),
+                    items: items.lift(|i| folder.fold_impl_item(i)),
+                })
             }
             Mac(mac) => Mac(folder.fold_mac(mac)),
         },
@@ -609,142 +701,235 @@
 
 #[cfg(feature = "full")]
 pub fn noop_fold_expr<F: ?Sized + Folder>(folder: &mut F, Expr { node, attrs }: Expr) -> Expr {
-    use ExprKind::*;
+    use expr::*;
+    use expr::ExprKind::*;
+
     Expr {
         node: match node {
-            ExprKind::Box(e) => ExprKind::Box(e.lift(|e| folder.fold_expr(e))),
-            InPlace(place, value) => {
-                InPlace(place.lift(|e| folder.fold_expr(e)),
-                        value.lift(|e| folder.fold_expr(e)))
+            Box(ExprBox { expr }) => {
+                Box(ExprBox { expr: expr.lift(|e| folder.fold_expr(e)) })
             }
-            Array(array) => Array(array.lift(|e| folder.fold_expr(e))),
-            Call(function, args) => {
-                Call(function.lift(|e| folder.fold_expr(e)),
-                     args.lift(|e| folder.fold_expr(e)))
+            InPlace(ExprInPlace { place, value }) => {
+                InPlace(ExprInPlace {
+                    place: place.lift(|e| folder.fold_expr(e)),
+                    value: value.lift(|e| folder.fold_expr(e)),
+                })
             }
-            MethodCall(method, tys, args) => {
-                MethodCall(folder.fold_ident(method),
-                           tys.lift(|t| folder.fold_ty(t)),
-                           args.lift(|e| folder.fold_expr(e)))
+            Array(ExprArray { exprs }) => {
+                Array(ExprArray {
+                    exprs: exprs.lift(|e| folder.fold_expr(e)),
+                })
             }
-            Tup(args) => Tup(args.lift(|e| folder.fold_expr(e))),
-            Binary(bop, lhs, rhs) => {
-                Binary(bop,
-                       lhs.lift(|e| folder.fold_expr(e)),
-                       rhs.lift(|e| folder.fold_expr(e)))
+            Call(ExprCall { func, args }) => {
+                Call(ExprCall {
+                    func: func.lift(|e| folder.fold_expr(e)),
+                    args: args.lift(|e| folder.fold_expr(e)),
+                })
             }
-            Unary(uop, e) => Unary(uop, e.lift(|e| folder.fold_expr(e))),
+            MethodCall(ExprMethodCall { method, typarams, args }) => {
+                MethodCall(ExprMethodCall {
+                    method: folder.fold_ident(method),
+                    typarams: typarams.lift(|t| folder.fold_ty(t)),
+                    args: args.lift(|e| folder.fold_expr(e)),
+                })
+            }
+            Tup(ExprTup { args }) => {
+                Tup(ExprTup {
+                    args: args.lift(|e| folder.fold_expr(e)),
+                })
+            }
+            Binary(ExprBinary { op, left, right }) => {
+                Binary(ExprBinary {
+                    op: op,
+                    left: left.lift(|e| folder.fold_expr(e)),
+                    right: right.lift(|e| folder.fold_expr(e)),
+                })
+            }
+            Unary(ExprUnary { op, expr }) => {
+                Unary(ExprUnary {
+                    op: op,
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                })
+            }
             Lit(lit) => Lit(folder.fold_lit(lit)),
-            Cast(e, ty) => {
-                Cast(e.lift(|e| folder.fold_expr(e)),
-                     ty.lift(|t| folder.fold_ty(t)))
+            Cast(ExprCast { expr, ty }) => {
+                Cast(ExprCast {
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    ty: ty.lift(|t| folder.fold_ty(t)),
+                })
             }
-            Type(e, ty) => {
-                Type(e.lift(|e| folder.fold_expr(e)),
-                     ty.lift(|t| folder.fold_ty(t)))
+            Type(ExprType { expr, ty }) => {
+                Type(ExprType {
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    ty: ty.lift(|t| folder.fold_ty(t)),
+                })
             }
-            If(e, if_block, else_block) => {
-                If(e.lift(|e| folder.fold_expr(e)),
-                   folder.fold_block(if_block),
-                   else_block.map(|v| v.lift(|e| folder.fold_expr(e))))
+            If(ExprIf { cond, if_true, if_false }) => {
+                If(ExprIf {
+                    cond: cond.lift(|e| folder.fold_expr(e)),
+                    if_true: folder.fold_block(if_true),
+                    if_false: if_false.map(|v| v.lift(|e| folder.fold_expr(e))),
+                })
             }
-            IfLet(pat, expr, block, else_block) => {
-                IfLet(pat.lift(|p| folder.fold_pat(p)),
-                      expr.lift(|e| folder.fold_expr(e)),
-                      folder.fold_block(block),
-                      else_block.map(|v| v.lift(|e| folder.fold_expr(e))))
+            IfLet(ExprIfLet { pat, expr, if_true, if_false }) => {
+                IfLet(ExprIfLet {
+                    pat: pat.lift(|p| folder.fold_pat(p)),
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    if_true: folder.fold_block(if_true),
+                    if_false: if_false.map(|v| v.lift(|e| folder.fold_expr(e))),
+                })
             }
-            While(e, block, label) => {
-                While(e.lift(|e| folder.fold_expr(e)),
-                      folder.fold_block(block),
-                      label.map(|i| folder.fold_ident(i)))
+            While(ExprWhile { cond, body, label }) => {
+                While(ExprWhile {
+                    cond: cond.lift(|e| folder.fold_expr(e)),
+                    body: folder.fold_block(body),
+                    label: label.map(|i| folder.fold_ident(i)),
+                })
             }
-            WhileLet(pat, expr, block, label) => {
-                WhileLet(pat.lift(|p| folder.fold_pat(p)),
-                         expr.lift(|e| folder.fold_expr(e)),
-                         folder.fold_block(block),
-                         label.map(|i| folder.fold_ident(i)))
+            WhileLet(ExprWhileLet { pat, expr, body, label }) => {
+                WhileLet(ExprWhileLet {
+                    pat: pat.lift(|p| folder.fold_pat(p)),
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    body: folder.fold_block(body),
+                    label: label.map(|i| folder.fold_ident(i)),
+                })
             }
-            ForLoop(pat, expr, block, label) => {
-                ForLoop(pat.lift(|p| folder.fold_pat(p)),
-                        expr.lift(|e| folder.fold_expr(e)),
-                        folder.fold_block(block),
-                        label.map(|i| folder.fold_ident(i)))
+            ForLoop(ExprForLoop { pat, expr, body, label }) => {
+                ForLoop(ExprForLoop {
+                    pat: pat.lift(|p| folder.fold_pat(p)),
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    body: folder.fold_block(body),
+                    label: label.map(|i| folder.fold_ident(i)),
+                })
             }
-            Loop(block, label) => {
-                Loop(folder.fold_block(block),
-                     label.map(|i| folder.fold_ident(i)))
+            Loop(ExprLoop { body, label }) => {
+                Loop(ExprLoop {
+                    body: folder.fold_block(body),
+                    label: label.map(|i| folder.fold_ident(i)),
+                })
             }
-            Match(e, arms) => {
-                Match(e.lift(|e| folder.fold_expr(e)),
-                      arms.lift(|Arm { attrs, pats, guard, body }: Arm| {
-                    Arm {
-                        attrs: attrs.lift(|a| folder.fold_attribute(a)),
-                        pats: pats.lift(|p| folder.fold_pat(p)),
-                        guard: guard.map(|v| v.lift(|e| folder.fold_expr(e))),
-                        body: body.lift(|e| folder.fold_expr(e)),
-                    }
-                }))
+            Match(ExprMatch { expr, arms }) => {
+                Match(ExprMatch {
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    arms: arms.lift(|Arm { attrs, pats, guard, body }: Arm| {
+                        Arm {
+                            attrs: attrs.lift(|a| folder.fold_attribute(a)),
+                            pats: pats.lift(|p| folder.fold_pat(p)),
+                            guard: guard.map(|v| v.lift(|e| folder.fold_expr(e))),
+                            body: body.lift(|e| folder.fold_expr(e)),
+                        }
+                    })
+                })
             }
-            Catch(block) => {
-                Catch(folder.fold_block(block))
+            Catch(ExprCatch { block }) => {
+                Catch(ExprCatch { block: folder.fold_block(block) })
             }
-            Closure(capture_by, fn_decl, expr) => {
-                Closure(capture_by,
-                        fn_decl.lift(|v| folder.fold_fn_decl(v)),
-                        expr.lift(|e| folder.fold_expr(e)))
+            Closure(ExprClosure { capture, decl, body }) => {
+                Closure(ExprClosure {
+                    capture: capture,
+                    decl: decl.lift(|v| folder.fold_fn_decl(v)),
+                    body: body.lift(|e| folder.fold_expr(e)),
+                })
             }
-            Block(unsafety, block) => Block(unsafety, folder.fold_block(block)),
-            Assign(lhs, rhs) => {
-                Assign(lhs.lift(|e| folder.fold_expr(e)),
-                       rhs.lift(|e| folder.fold_expr(e)))
+            Block(ExprBlock { unsafety, block }) => {
+                Block(ExprBlock {
+                    unsafety: unsafety,
+                    block: folder.fold_block(block),
+                })
             }
-            AssignOp(bop, lhs, rhs) => {
-                AssignOp(bop,
-                         lhs.lift(|e| folder.fold_expr(e)),
-                         rhs.lift(|e| folder.fold_expr(e)))
+            Assign(ExprAssign { left, right }) => {
+                Assign(ExprAssign {
+                    left: left.lift(|e| folder.fold_expr(e)),
+                    right: right.lift(|e| folder.fold_expr(e)),
+                })
             }
-            Field(expr, name) => Field(expr.lift(|e| folder.fold_expr(e)), folder.fold_ident(name)),
-            TupField(expr, index) => TupField(expr.lift(|e| folder.fold_expr(e)), index),
-            Index(expr, index) => {
-                Index(expr.lift(|e| folder.fold_expr(e)),
-                      index.lift(|e| folder.fold_expr(e)))
+            AssignOp(ExprAssignOp { op, left, right }) => {
+                AssignOp(ExprAssignOp {
+                    op: op,
+                    left: left.lift(|e| folder.fold_expr(e)),
+                    right: right.lift(|e| folder.fold_expr(e)),
+                })
             }
-            Range(lhs, rhs, limits) => {
-                Range(lhs.map(|v| v.lift(|e| folder.fold_expr(e))),
-                      rhs.map(|v| v.lift(|e| folder.fold_expr(e))),
-                      limits)
+            Field(ExprField { expr, field }) => {
+                Field(ExprField {
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    field: folder.fold_ident(field),
+                })
             }
-            Path(qself, path) => {
-                Path(qself.map(|v| noop_fold_qself(folder, v)),
-                     folder.fold_path(path))
+            TupField(ExprTupField { expr, field }) => {
+                TupField(ExprTupField {
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    field: field,
+                })
             }
-            AddrOf(mutability, expr) => AddrOf(mutability, expr.lift(|e| folder.fold_expr(e))),
-            Break(label, expr) => {
-                Break(label.map(|i| folder.fold_ident(i)),
-                      expr.map(|v| v.lift(|e| folder.fold_expr(e))))
+            Index(ExprIndex { expr, index }) => {
+                Index(ExprIndex {
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    index: index.lift(|e| folder.fold_expr(e)),
+                })
             }
-            Continue(label) => Continue(label.map(|i| folder.fold_ident(i))),
-            Ret(expr) => Ret(expr.map(|v| v.lift(|e| folder.fold_expr(e)))),
-            ExprKind::Mac(mac) => ExprKind::Mac(folder.fold_mac(mac)),
-            Struct(path, fields, expr) => {
-                Struct(folder.fold_path(path),
-                       fields.lift(|FieldValue { ident, expr, is_shorthand, attrs }: FieldValue| {
-                    FieldValue {
-                        ident: folder.fold_ident(ident),
-                        expr: folder.fold_expr(expr),
-                        is_shorthand: is_shorthand,
-                        attrs: attrs.lift(|v| folder.fold_attribute(v)),
-                    }
-                }),
-                       expr.map(|v| v.lift(|e| folder.fold_expr(e))))
+            Range(ExprRange { from, to, limits }) => {
+                Range(ExprRange {
+                    from: from.map(|v| v.lift(|e| folder.fold_expr(e))),
+                    to: to.map(|v| v.lift(|e| folder.fold_expr(e))),
+                    limits: limits,
+                })
             }
-            Repeat(element, number) => {
-                Repeat(element.lift(|e| folder.fold_expr(e)),
-                       number.lift(|e| folder.fold_expr(e)))
+            Path(ExprPath { qself, path }) => {
+                Path(ExprPath {
+                    qself: qself.map(|v| noop_fold_qself(folder, v)),
+                    path: folder.fold_path(path),
+                })
             }
-            Paren(expr) => Paren(expr.lift(|e| folder.fold_expr(e))),
-            Try(expr) => Try(expr.lift(|e| folder.fold_expr(e))),
+            AddrOf(ExprAddrOf { mutbl, expr }) => {
+                AddrOf(ExprAddrOf {
+                    mutbl: mutbl,
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                })
+            }
+            Break(ExprBreak { label, expr }) => {
+                Break(ExprBreak {
+                    label: label.map(|i| folder.fold_ident(i)),
+                    expr: expr.map(|v| v.lift(|e| folder.fold_expr(e))),
+                })
+            }
+            Continue(ExprContinue { label }) => {
+                Continue(ExprContinue {
+                    label: label.map(|i| folder.fold_ident(i)),
+                })
+            }
+            Ret(ExprRet { expr }) => {
+                Ret(ExprRet {
+                    expr: expr.map(|v| v.lift(|e| folder.fold_expr(e))),
+                })
+            }
+            Mac(mac) => Mac(folder.fold_mac(mac)),
+            Struct(ExprStruct { path, fields, rest }) => {
+                Struct(ExprStruct {
+                    path: folder.fold_path(path),
+                    fields: fields.lift(|FieldValue { ident, expr, is_shorthand, attrs }: FieldValue| {
+                        FieldValue {
+                            ident: folder.fold_ident(ident),
+                            expr: folder.fold_expr(expr),
+                            is_shorthand: is_shorthand,
+                            attrs: attrs.lift(|v| folder.fold_attribute(v)),
+                        }
+                    }),
+                    rest: rest.map(|v| v.lift(|e| folder.fold_expr(e))),
+                })
+            }
+            Repeat(ExprRepeat { expr, amt }) => {
+                Repeat(ExprRepeat {
+                    expr: expr.lift(|e| folder.fold_expr(e)),
+                    amt: amt.lift(|e| folder.fold_expr(e)),
+                })
+            }
+            Paren(ExprParen { expr }) => {
+                Paren(ExprParen { expr: expr.lift(|e| folder.fold_expr(e)) })
+            }
+            Try(ExprTry { expr }) => {
+                Try(ExprTry { expr: expr.lift(|e| folder.fold_expr(e)) })
+            }
         },
         attrs: attrs.into_iter().map(|a| folder.fold_attribute(a)).collect(),
     }
@@ -754,16 +939,23 @@
 pub fn noop_fold_foreign_item<F: ?Sized + Folder>(folder: &mut F,
                                          ForeignItem { ident, attrs, node, vis }: ForeignItem)
 -> ForeignItem{
+    use item::*;
+
     ForeignItem {
         ident: folder.fold_ident(ident),
         attrs: attrs.into_iter().map(|a| folder.fold_attribute(a)).collect(),
         node: match node {
-            ForeignItemKind::Fn(fn_dcl, generics) => {
-                ForeignItemKind::Fn(fn_dcl.lift(|v| folder.fold_fn_decl(v)),
-                                    folder.fold_generics(generics))
+            ForeignItemKind::Fn(ForeignItemFn { decl, generics }) => {
+                ForeignItemKind::Fn(ForeignItemFn {
+                    decl: decl.lift(|v| folder.fold_fn_decl(v)),
+                    generics: folder.fold_generics(generics),
+                })
             }
-            ForeignItemKind::Static(ty, mutability) => {
-                ForeignItemKind::Static(ty.lift(|v| folder.fold_ty(v)), mutability)
+            ForeignItemKind::Static(ForeignItemStatic { ty, mutbl }) => {
+                ForeignItemKind::Static(ForeignItemStatic {
+                    ty: ty.lift(|v| folder.fold_ty(v)),
+                    mutbl: mutbl,
+                })
             }
         },
         vis: noop_fold_vis(folder, vis),
@@ -826,13 +1018,26 @@
 
     FnDecl {
         inputs: inputs.lift(|a| {
+            use item::*;
             use FnArg::*;
             match a {
-                SelfRef(lifetime, mutability) => {
-                    SelfRef(lifetime.map(|v| folder.fold_lifetime(v)), mutability)
+                SelfRef(ArgSelfRef { lifetime, mutbl }) => {
+                    SelfRef(ArgSelfRef {
+                        lifetime: lifetime.map(|v| folder.fold_lifetime(v)),
+                        mutbl: mutbl,
+                    })
                 }
-                SelfValue(mutability) => SelfValue(mutability),
-                Captured(pat, ty) => Captured(folder.fold_pat(pat), folder.fold_ty(ty)),
+                SelfValue(ArgSelf { mutbl } ) => {
+                    SelfValue(ArgSelf {
+                        mutbl: mutbl,
+                    })
+                }
+                Captured(ArgCaptured { pat, ty }) => {
+                    Captured(ArgCaptured {
+                        pat: folder.fold_pat(pat),
+                        ty: folder.fold_ty(ty),
+                    })
+                }
                 Ignored(ty) => Ignored(folder.fold_ty(ty)),
             }
         }),
@@ -846,19 +1051,29 @@
 pub fn noop_fold_trait_item<F: ?Sized + Folder>(folder: &mut F,
                                                 TraitItem { ident, attrs, node }: TraitItem)
                                                 -> TraitItem {
+    use item::*;
     use TraitItemKind::*;
     TraitItem {
         ident: folder.fold_ident(ident),
         attrs: attrs.lift(|v| folder.fold_attribute(v)),
         node: match node {
-            Const(ty, expr) => Const(folder.fold_ty(ty), expr.map(|v| folder.fold_expr(v))),
-            Method(sig, block) => {
-                Method(folder.fold_method_sig(sig),
-                       block.map(|v| folder.fold_block(v)))
+            Const(TraitItemConst { ty, default }) => {
+                Const(TraitItemConst {
+                    ty: folder.fold_ty(ty),
+                    default: default.map(|v| folder.fold_expr(v)),
+                })
             }
-            Type(ty_pbs, ty) => {
-                Type(ty_pbs.lift(|v| folder.fold_ty_param_bound(v)),
-                     ty.map(|v| folder.fold_ty(v)))
+            Method(TraitItemMethod { sig, default }) => {
+                Method(TraitItemMethod {
+                    sig: folder.fold_method_sig(sig),
+                    default: default.map(|v| folder.fold_block(v)),
+                })
+            }
+            Type(TraitItemType { bounds, default }) => {
+                Type(TraitItemType {
+                    bounds: bounds.lift(|v| folder.fold_ty_param_bound(v)),
+                    default: default.map(|v| folder.fold_ty(v)),
+                })
             }
             Macro(mac) => Macro(folder.fold_mac(mac)),
         },
@@ -869,16 +1084,32 @@
 pub fn noop_fold_impl_item<F: ?Sized + Folder>(folder: &mut F,
                                       ImplItem { ident, vis, defaultness, attrs, node }: ImplItem)
 -> ImplItem{
+    use item::*;
     use ImplItemKind::*;
+
     ImplItem {
         ident: folder.fold_ident(ident),
         vis: noop_fold_vis(folder, vis),
         defaultness: defaultness,
         attrs: attrs.lift(|v| folder.fold_attribute(v)),
         node: match node {
-            Const(ty, expr) => Const(folder.fold_ty(ty), folder.fold_expr(expr)),
-            Method(sig, block) => Method(folder.fold_method_sig(sig), folder.fold_block(block)),
-            Type(ty) => Type(folder.fold_ty(ty)),
+            Const(ImplItemConst { ty, expr }) => {
+                Const(ImplItemConst {
+                    ty: folder.fold_ty(ty),
+                    expr: folder.fold_expr(expr),
+                })
+            }
+            Method(ImplItemMethod { sig, block }) => {
+                Method(ImplItemMethod {
+                    sig: folder.fold_method_sig(sig),
+                    block: folder.fold_block(block),
+                })
+            }
+            Type(ImplItemType { ty }) => {
+                Type(ImplItemType {
+                    ty: folder.fold_ty(ty),
+                })
+            }
             Macro(mac) => Macro(folder.fold_mac(mac)),
         },
     }
@@ -929,18 +1160,30 @@
 
 #[cfg(feature = "full")]
 pub fn noop_fold_view_path<F: ?Sized + Folder>(folder: &mut F, view_path: ViewPath) -> ViewPath {
+    use item::*;
     use ViewPath::*;
     match view_path {
-        Simple(path, ident) => Simple(folder.fold_path(path), ident.map(|i| folder.fold_ident(i))),
-        Glob(path) => Glob(folder.fold_path(path)),
-        List(path, items) => {
-            List(folder.fold_path(path),
-                 items.lift(|PathListItem { name, rename }: PathListItem| {
-                                PathListItem {
-                                    name: folder.fold_ident(name),
-                                    rename: rename.map(|i| folder.fold_ident(i)),
-                                }
-                            }))
+        Simple(PathSimple { path, rename }) => {
+            Simple(PathSimple {
+                path: folder.fold_path(path),
+                rename: rename.map(|i| folder.fold_ident(i)),
+            })
+        }
+        Glob(PathGlob { path }) => {
+            Glob(PathGlob {
+                path: folder.fold_path(path),
+            })
+        }
+        List(PathList { path, items }) => {
+            List(PathList {
+                path: folder.fold_path(path),
+                items: items.lift(|PathListItem { name, rename }: PathListItem| {
+                    PathListItem {
+                        name: folder.fold_ident(name),
+                        rename: rename.map(|i| folder.fold_ident(i)),
+                    }
+                }),
+            })
         }
     }
 }
diff --git a/src/generics.rs b/src/generics.rs
index fbb89b1..2841a47 100644
--- a/src/generics.rs
+++ b/src/generics.rs
@@ -1,12 +1,14 @@
 use super::*;
 
-/// Represents lifetimes and type parameters attached to a declaration
-/// of a function, enum, trait, etc.
-#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
-pub struct Generics {
-    pub lifetimes: Vec<LifetimeDef>,
-    pub ty_params: Vec<TyParam>,
-    pub where_clause: WhereClause,
+ast_struct! {
+    /// Represents lifetimes and type parameters attached to a declaration
+    /// of a function, enum, trait, etc.
+    #[derive(Default)]
+    pub struct Generics {
+        pub lifetimes: Vec<LifetimeDef>,
+        pub ty_params: Vec<TyParam>,
+        pub where_clause: WhereClause,
+    }
 }
 
 #[cfg(feature = "printing")]
@@ -58,9 +60,10 @@
     }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
-pub struct Lifetime {
-    pub ident: Ident,
+ast_struct! {
+    pub struct Lifetime {
+        pub ident: Ident,
+    }
 }
 
 impl Lifetime {
@@ -75,12 +78,13 @@
     }
 }
 
-/// A lifetime definition, e.g. `'a: 'b+'c+'d`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct LifetimeDef {
-    pub attrs: Vec<Attribute>,
-    pub lifetime: Lifetime,
-    pub bounds: Vec<Lifetime>,
+ast_struct! {
+    /// A lifetime definition, e.g. `'a: 'b+'c+'d`
+    pub struct LifetimeDef {
+        pub attrs: Vec<Attribute>,
+        pub lifetime: Lifetime,
+        pub bounds: Vec<Lifetime>,
+    }
 }
 
 impl LifetimeDef {
@@ -93,13 +97,14 @@
     }
 }
 
-/// A generic type parameter, e.g. `T: Into<String>`.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct TyParam {
-    pub attrs: Vec<Attribute>,
-    pub ident: Ident,
-    pub bounds: Vec<TyParamBound>,
-    pub default: Option<Ty>,
+ast_struct! {
+    /// A generic type parameter, e.g. `T: Into<String>`.
+    pub struct TyParam {
+        pub attrs: Vec<Attribute>,
+        pub ident: Ident,
+        pub bounds: Vec<TyParamBound>,
+        pub default: Option<Ty>,
+    }
 }
 
 impl From<Ident> for TyParam {
@@ -113,28 +118,33 @@
     }
 }
 
-/// The AST represents all type param bounds as types.
-/// `typeck::collect::compute_bounds` matches these against
-/// the "special" built-in traits (see `middle::lang_items`) and
-/// detects Copy, Send and Sync.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum TyParamBound {
-    Trait(PolyTraitRef, TraitBoundModifier),
-    Region(Lifetime),
+ast_enum! {
+    /// The AST represents all type param bounds as types.
+    /// `typeck::collect::compute_bounds` matches these against
+    /// the "special" built-in traits (see `middle::lang_items`) and
+    /// detects Copy, Send and Sync.
+    pub enum TyParamBound {
+        Trait(PolyTraitRef, TraitBoundModifier),
+        Region(Lifetime),
+    }
 }
 
-/// A modifier on a bound, currently this is only used for `?Sized`, where the
-/// modifier is `Maybe`. Negative bounds should also be handled here.
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum TraitBoundModifier {
-    None,
-    Maybe,
+ast_enum! {
+    /// A modifier on a bound, currently this is only used for `?Sized`, where the
+    /// modifier is `Maybe`. Negative bounds should also be handled here.
+    #[derive(Copy)]
+    pub enum TraitBoundModifier {
+        None,
+        Maybe,
+    }
 }
 
-/// A `where` clause in a definition
-#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
-pub struct WhereClause {
-    pub predicates: Vec<WherePredicate>,
+ast_struct! {
+    /// A `where` clause in a definition
+    #[derive(Default)]
+    pub struct WhereClause {
+        pub predicates: Vec<WherePredicate>,
+    }
 }
 
 impl WhereClause {
@@ -143,46 +153,31 @@
     }
 }
 
-/// A single predicate in a `where` clause
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum WherePredicate {
-    /// A type binding, e.g. `for<'c> Foo: Send+Clone+'c`
-    BoundPredicate(WhereBoundPredicate),
-    /// A lifetime predicate, e.g. `'a: 'b+'c`
-    RegionPredicate(WhereRegionPredicate),
-    /// An equality predicate (unsupported)
-    EqPredicate(WhereEqPredicate),
-}
+ast_enum_of_structs! {
+    /// A single predicate in a `where` clause
+    pub enum WherePredicate {
+        /// A type binding, e.g. `for<'c> Foo: Send+Clone+'c`
+        pub BoundPredicate(WhereBoundPredicate {
+            /// Any lifetimes from a `for` binding
+            pub bound_lifetimes: Vec<LifetimeDef>,
+            /// The type being bounded
+            pub bounded_ty: Ty,
+            /// Trait and lifetime bounds (`Clone+Send+'static`)
+            pub bounds: Vec<TyParamBound>,
+        }),
 
-/// A type bound.
-///
-/// E.g. `for<'c> Foo: Send+Clone+'c`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct WhereBoundPredicate {
-    /// Any lifetimes from a `for` binding
-    pub bound_lifetimes: Vec<LifetimeDef>,
-    /// The type being bounded
-    pub bounded_ty: Ty,
-    /// Trait and lifetime bounds (`Clone+Send+'static`)
-    pub bounds: Vec<TyParamBound>,
-}
+        /// A lifetime predicate, e.g. `'a: 'b+'c`
+        pub RegionPredicate(WhereRegionPredicate {
+            pub lifetime: Lifetime,
+            pub bounds: Vec<Lifetime>,
+        }),
 
-/// A lifetime predicate.
-///
-/// E.g. `'a: 'b+'c`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct WhereRegionPredicate {
-    pub lifetime: Lifetime,
-    pub bounds: Vec<Lifetime>,
-}
-
-/// An equality predicate (unsupported).
-///
-/// E.g. `T=int`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct WhereEqPredicate {
-    pub lhs_ty: Ty,
-    pub rhs_ty: Ty,
+        /// An equality predicate (unsupported)
+        pub EqPredicate(WhereEqPredicate {
+            pub lhs_ty: Ty,
+            pub rhs_ty: Ty,
+        }),
+    }
 }
 
 #[cfg(feature = "parsing")]
@@ -441,8 +436,8 @@
         fn to_tokens(&self, tokens: &mut Tokens) {
             match *self {
                 TyParamBound::Region(ref lifetime) => lifetime.to_tokens(tokens),
-                TyParamBound::Trait(ref trait_ref, modifier) => {
-                    match modifier {
+                TyParamBound::Trait(ref trait_ref, ref modifier) => {
+                    match *modifier {
                         TraitBoundModifier::None => {}
                         TraitBoundModifier::Maybe => tokens.append("?"),
                     }
@@ -461,22 +456,6 @@
         }
     }
 
-    impl ToTokens for WherePredicate {
-        fn to_tokens(&self, tokens: &mut Tokens) {
-            match *self {
-                WherePredicate::BoundPredicate(ref predicate) => {
-                    predicate.to_tokens(tokens);
-                }
-                WherePredicate::RegionPredicate(ref predicate) => {
-                    predicate.to_tokens(tokens);
-                }
-                WherePredicate::EqPredicate(ref predicate) => {
-                    predicate.to_tokens(tokens);
-                }
-            }
-        }
-    }
-
     impl ToTokens for WhereBoundPredicate {
         fn to_tokens(&self, tokens: &mut Tokens) {
             if !self.bound_lifetimes.is_empty() {
diff --git a/src/item.rs b/src/item.rs
index 4cbac33..7b25f2f 100644
--- a/src/item.rs
+++ b/src/item.rs
@@ -1,83 +1,129 @@
 use super::*;
 
-/// An item
-///
-/// The name might be a dummy name in case of anonymous items
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Item {
-    pub ident: Ident,
-    pub vis: Visibility,
-    pub attrs: Vec<Attribute>,
-    pub node: ItemKind,
+ast_struct! {
+    /// An item
+    ///
+    /// The name might be a dummy name in case of anonymous items
+    pub struct Item {
+        pub ident: Ident,
+        pub vis: Visibility,
+        pub attrs: Vec<Attribute>,
+        pub node: ItemKind,
+    }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum ItemKind {
-    /// An`extern crate` item, with optional original crate name.
-    ///
-    /// E.g. `extern crate foo` or `extern crate foo_bar as foo`
-    ExternCrate(Option<Ident>),
-    /// A use declaration (`use` or `pub use`) item.
-    ///
-    /// E.g. `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`
-    Use(Box<ViewPath>),
-    /// A static item (`static` or `pub static`).
-    ///
-    /// E.g. `static FOO: i32 = 42;` or `static FOO: &'static str = "bar";`
-    Static(Box<Ty>, Mutability, Box<Expr>),
-    /// A constant item (`const` or `pub const`).
-    ///
-    /// E.g. `const FOO: i32 = 42;`
-    Const(Box<Ty>, Box<Expr>),
-    /// A function declaration (`fn` or `pub fn`).
-    ///
-    /// E.g. `fn foo(bar: usize) -> usize { .. }`
-    Fn(Box<FnDecl>, Unsafety, Constness, Option<Abi>, Generics, Box<Block>),
-    /// A module declaration (`mod` or `pub mod`).
-    ///
-    /// E.g. `mod foo;` or `mod foo { .. }`
-    Mod(Option<Vec<Item>>),
-    /// An external module (`extern` or `pub extern`).
-    ///
-    /// E.g. `extern {}` or `extern "C" {}`
-    ForeignMod(ForeignMod),
-    /// A type alias (`type` or `pub type`).
-    ///
-    /// E.g. `type Foo = Bar<u8>;`
-    Ty(Box<Ty>, Generics),
-    /// An enum definition (`enum` or `pub enum`).
-    ///
-    /// E.g. `enum Foo<A, B> { C<A>, D<B> }`
-    Enum(Vec<Variant>, Generics),
-    /// A struct definition (`struct` or `pub struct`).
-    ///
-    /// E.g. `struct Foo<A> { x: A }`
-    Struct(VariantData, Generics),
-    /// A union definition (`union` or `pub union`).
-    ///
-    /// E.g. `union Foo<A, B> { x: A, y: B }`
-    Union(VariantData, Generics),
-    /// A Trait declaration (`trait` or `pub trait`).
-    ///
-    /// E.g. `trait Foo { .. }` or `trait Foo<T> { .. }`
-    Trait(Unsafety, Generics, Vec<TyParamBound>, Vec<TraitItem>),
-    /// Default trait implementation.
-    ///
-    /// E.g. `impl Trait for .. {}` or `impl<T> Trait<T> for .. {}`
-    DefaultImpl(Unsafety, Path),
-    /// An implementation.
-    ///
-    /// E.g. `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`
-    Impl(Unsafety,
-         ImplPolarity,
-         Generics,
-         Option<Path>, // (optional) trait this impl implements
-         Box<Ty>, // self
-         Vec<ImplItem>),
-    /// A macro invocation (which includes macro definition).
-    ///
-    /// E.g. `macro_rules! foo { .. }` or `foo!(..)`
-    Mac(Mac),
+ast_enum_of_structs! {
+    pub enum ItemKind {
+        /// An`extern crate` item, with optional original crate name.
+        ///
+        /// E.g. `extern crate foo` or `extern crate foo_bar as foo`
+        pub ExternCrate(ItemExternCrate {
+            pub original: Option<Ident>,
+        }),
+        /// A use declaration (`use` or `pub use`) item.
+        ///
+        /// E.g. `use foo;`, `use foo::bar;` or `use foo::bar as FooBar;`
+        pub Use(ItemUse {
+            pub path: Box<ViewPath>,
+        }),
+        /// A static item (`static` or `pub static`).
+        ///
+        /// E.g. `static FOO: i32 = 42;` or `static FOO: &'static str = "bar";`
+        pub Static(ItemStatic {
+            pub ty: Box<Ty>,
+            pub mutbl: Mutability,
+            pub expr: Box<Expr>,
+        }),
+        /// A constant item (`const` or `pub const`).
+        ///
+        /// E.g. `const FOO: i32 = 42;`
+        pub Const(ItemConst {
+            pub ty: Box<Ty>,
+            pub expr: Box<Expr>,
+        }),
+        /// A function declaration (`fn` or `pub fn`).
+        ///
+        /// E.g. `fn foo(bar: usize) -> usize { .. }`
+        pub Fn(ItemFn {
+            pub decl: Box<FnDecl>,
+            pub unsafety: Unsafety,
+            pub constness: Constness,
+            pub abi: Option<Abi>,
+            pub generics: Generics,
+            pub block: Box<Block>,
+        }),
+        /// A module declaration (`mod` or `pub mod`).
+        ///
+        /// E.g. `mod foo;` or `mod foo { .. }`
+        pub Mod(ItemMod {
+            pub items: Option<Vec<Item>>,
+        }),
+        /// An external module (`extern` or `pub extern`).
+        ///
+        /// E.g. `extern {}` or `extern "C" {}`
+        pub ForeignMod(ItemForeignMod),
+        /// A type alias (`type` or `pub type`).
+        ///
+        /// E.g. `type Foo = Bar<u8>;`
+        pub Ty(ItemTy {
+            pub ty: Box<Ty>,
+            pub generics: Generics,
+        }),
+        /// An enum definition (`enum` or `pub enum`).
+        ///
+        /// E.g. `enum Foo<A, B> { C<A>, D<B> }`
+        pub Enum(ItemEnum {
+            pub variants: Vec<Variant>,
+            pub generics: Generics,
+        }),
+        /// A struct definition (`struct` or `pub struct`).
+        ///
+        /// E.g. `struct Foo<A> { x: A }`
+        pub Struct(ItemStruct {
+            pub data: VariantData,
+            pub generics: Generics,
+        }),
+        /// A union definition (`union` or `pub union`).
+        ///
+        /// E.g. `union Foo<A, B> { x: A, y: B }`
+        pub Union(ItemUnion {
+            pub data: VariantData,
+            pub generics: Generics,
+        }),
+        /// A Trait declaration (`trait` or `pub trait`).
+        ///
+        /// E.g. `trait Foo { .. }` or `trait Foo<T> { .. }`
+        pub Trait(ItemTrait {
+            pub unsafety: Unsafety,
+            pub generics: Generics,
+            pub supertraits: Vec<TyParamBound>,
+            pub items: Vec<TraitItem>,
+        }),
+        /// Default trait implementation.
+        ///
+        /// E.g. `impl Trait for .. {}` or `impl<T> Trait<T> for .. {}`
+        pub DefaultImpl(ItemDefaultImpl {
+            pub unsafety: Unsafety,
+            pub path: Path,
+        }),
+        /// An implementation.
+        ///
+        /// E.g. `impl<A> Foo<A> { .. }` or `impl<A> Trait for Foo<A> { .. }`
+        pub Impl(ItemImpl {
+            pub unsafety: Unsafety,
+            pub polarity: ImplPolarity,
+            pub generics: Generics,
+            pub trait_: Option<Path>, // (optional) trait this impl implements
+            pub self_ty: Box<Ty>, // self
+            pub items: Vec<ImplItem>,
+        }),
+        /// A macro invocation (which includes macro definition).
+        ///
+        /// E.g. `macro_rules! foo { .. }` or `foo!(..)`
+        pub Mac(Mac),
+    }
+
+    do_not_generate_to_tokens
 }
 
 impl From<DeriveInput> for Item {
@@ -87,148 +133,221 @@
             vis: input.vis,
             attrs: input.attrs,
             node: match input.body {
-                Body::Enum(variants) => ItemKind::Enum(variants, input.generics),
-                Body::Struct(variant_data) => ItemKind::Struct(variant_data, input.generics),
+                Body::Enum(variants) => {
+                    ItemEnum {
+                        variants: variants,
+                        generics: input.generics,
+                    }.into()
+                }
+                Body::Struct(variant_data) => {
+                    ItemStruct {
+                        data: variant_data,
+                        generics: input.generics,
+                    }.into()
+                }
             },
         }
     }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum ViewPath {
-    /// `foo::bar::baz as quux`
+ast_enum_of_structs! {
+    pub enum ViewPath {
+        /// `foo::bar::baz as quux`
+        ///
+        /// or just
+        ///
+        /// `foo::bar::baz` (with `as baz` implicitly on the right)
+        pub Simple(PathSimple {
+            pub path: Path,
+            pub rename: Option<Ident>,
+        }),
+
+        /// `foo::bar::*`
+        pub Glob(PathGlob {
+            pub path: Path,
+        }),
+
+        /// `foo::bar::{a, b, c}`
+        pub List(PathList {
+            pub path: Path,
+            pub items: Vec<PathListItem>,
+        }),
+    }
+}
+
+ast_struct! {
+    pub struct PathListItem {
+        pub name: Ident,
+        /// renamed in list, e.g. `use foo::{bar as baz};`
+        pub rename: Option<Ident>,
+    }
+}
+
+ast_enum! {
+    #[derive(Copy)]
+    pub enum Constness {
+        Const,
+        NotConst,
+    }
+}
+
+ast_enum! {
+    #[derive(Copy)]
+    pub enum Defaultness {
+        Default,
+        Final,
+    }
+}
+
+ast_struct! {
+    /// Foreign module declaration.
     ///
-    /// or just
+    /// E.g. `extern { .. }` or `extern "C" { .. }`
+    pub struct ItemForeignMod {
+        pub abi: Abi,
+        pub items: Vec<ForeignItem>,
+    }
+}
+
+ast_struct! {
+    pub struct ForeignItem {
+        pub ident: Ident,
+        pub attrs: Vec<Attribute>,
+        pub node: ForeignItemKind,
+        pub vis: Visibility,
+    }
+}
+
+ast_enum_of_structs! {
+    /// An item within an `extern` block
+    pub enum ForeignItemKind {
+        /// A foreign function
+        pub Fn(ForeignItemFn {
+            pub decl: Box<FnDecl>,
+            pub generics: Generics,
+        }),
+        /// A foreign static item (`static ext: u8`)
+        pub Static(ForeignItemStatic {
+            pub ty: Box<Ty>,
+            pub mutbl: Mutability,
+        }),
+    }
+
+    do_not_generate_to_tokens
+}
+
+ast_struct! {
+    /// Represents an item declaration within a trait declaration,
+    /// possibly including a default implementation. A trait item is
+    /// either required (meaning it doesn't have an implementation, just a
+    /// signature) or provided (meaning it has a default implementation).
+    pub struct TraitItem {
+        pub ident: Ident,
+        pub attrs: Vec<Attribute>,
+        pub node: TraitItemKind,
+    }
+}
+
+ast_enum_of_structs! {
+    pub enum TraitItemKind {
+        pub Const(TraitItemConst {
+            pub ty: Ty,
+            pub default: Option<Expr>,
+        }),
+        pub Method(TraitItemMethod {
+            pub sig: MethodSig,
+            pub default: Option<Block>,
+        }),
+        pub Type(TraitItemType {
+            pub bounds: Vec<TyParamBound>,
+            pub default: Option<Ty>,
+        }),
+        pub Macro(Mac),
+    }
+
+    do_not_generate_to_tokens
+}
+
+ast_enum! {
+    #[derive(Copy)]
+    pub enum ImplPolarity {
+        /// `impl Trait for Type`
+        Positive,
+        /// `impl !Trait for Type`
+        Negative,
+    }
+}
+
+ast_struct! {
+    pub struct ImplItem {
+        pub ident: Ident,
+        pub vis: Visibility,
+        pub defaultness: Defaultness,
+        pub attrs: Vec<Attribute>,
+        pub node: ImplItemKind,
+    }
+}
+
+ast_enum_of_structs! {
+    pub enum ImplItemKind {
+        pub Const(ImplItemConst {
+            pub ty: Ty,
+            pub expr: Expr,
+        }),
+        pub Method(ImplItemMethod {
+            pub sig: MethodSig,
+            pub block: Block,
+        }),
+        pub Type(ImplItemType {
+            pub ty: Ty,
+        }),
+        pub Macro(Mac),
+    }
+
+    do_not_generate_to_tokens
+}
+
+ast_struct! {
+    /// Represents a method's signature in a trait declaration,
+    /// or in an implementation.
+    pub struct MethodSig {
+        pub unsafety: Unsafety,
+        pub constness: Constness,
+        pub abi: Option<Abi>,
+        pub decl: FnDecl,
+        pub generics: Generics,
+    }
+}
+
+ast_struct! {
+    /// Header (not the body) of a function declaration.
     ///
-    /// `foo::bar::baz` (with `as baz` implicitly on the right)
-    Simple(Path, Option<Ident>),
-
-    /// `foo::bar::*`
-    Glob(Path),
-
-    /// `foo::bar::{a, b, c}`
-    List(Path, Vec<PathListItem>),
+    /// E.g. `fn foo(bar: baz)`
+    pub struct FnDecl {
+        pub inputs: Vec<FnArg>,
+        pub output: FunctionRetTy,
+        pub variadic: bool,
+    }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct PathListItem {
-    pub name: Ident,
-    /// renamed in list, e.g. `use foo::{bar as baz};`
-    pub rename: Option<Ident>,
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Constness {
-    Const,
-    NotConst,
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Defaultness {
-    Default,
-    Final,
-}
-
-/// Foreign module declaration.
-///
-/// E.g. `extern { .. }` or `extern "C" { .. }`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct ForeignMod {
-    pub abi: Abi,
-    pub items: Vec<ForeignItem>,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct ForeignItem {
-    pub ident: Ident,
-    pub attrs: Vec<Attribute>,
-    pub node: ForeignItemKind,
-    pub vis: Visibility,
-}
-
-/// An item within an `extern` block
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum ForeignItemKind {
-    /// A foreign function
-    Fn(Box<FnDecl>, Generics),
-    /// A foreign static item (`static ext: u8`)
-    Static(Box<Ty>, Mutability),
-}
-
-/// Represents an item declaration within a trait declaration,
-/// possibly including a default implementation. A trait item is
-/// either required (meaning it doesn't have an implementation, just a
-/// signature) or provided (meaning it has a default implementation).
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct TraitItem {
-    pub ident: Ident,
-    pub attrs: Vec<Attribute>,
-    pub node: TraitItemKind,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum TraitItemKind {
-    Const(Ty, Option<Expr>),
-    Method(MethodSig, Option<Block>),
-    Type(Vec<TyParamBound>, Option<Ty>),
-    Macro(Mac),
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum ImplPolarity {
-    /// `impl Trait for Type`
-    Positive,
-    /// `impl !Trait for Type`
-    Negative,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct ImplItem {
-    pub ident: Ident,
-    pub vis: Visibility,
-    pub defaultness: Defaultness,
-    pub attrs: Vec<Attribute>,
-    pub node: ImplItemKind,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum ImplItemKind {
-    Const(Ty, Expr),
-    Method(MethodSig, Block),
-    Type(Ty),
-    Macro(Mac),
-}
-
-/// Represents a method's signature in a trait declaration,
-/// or in an implementation.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct MethodSig {
-    pub unsafety: Unsafety,
-    pub constness: Constness,
-    pub abi: Option<Abi>,
-    pub decl: FnDecl,
-    pub generics: Generics,
-}
-
-/// Header (not the body) of a function declaration.
-///
-/// E.g. `fn foo(bar: baz)`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct FnDecl {
-    pub inputs: Vec<FnArg>,
-    pub output: FunctionRetTy,
-    pub variadic: bool,
-}
-
-/// An argument in a function header.
-///
-/// E.g. `bar: usize` as in `fn foo(bar: usize)`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum FnArg {
-    SelfRef(Option<Lifetime>, Mutability),
-    SelfValue(Mutability),
-    Captured(Pat, Ty),
-    Ignored(Ty),
+ast_enum_of_structs! {
+    /// An argument in a function header.
+    ///
+    /// E.g. `bar: usize` as in `fn foo(bar: usize)`
+    pub enum FnArg {
+        pub SelfRef(ArgSelfRef {
+            pub lifetime: Option<Lifetime>,
+            pub mutbl: Mutability,
+        }),
+        pub SelfValue(ArgSelf {
+            pub mutbl: Mutability,
+        }),
+        pub Captured(ArgCaptured {
+            pub pat: Pat,
+            pub ty: Ty,
+        }),
+        pub Ignored(Ty),
+    }
 }
 
 #[cfg(feature = "parsing")]
@@ -319,7 +438,7 @@
                 ident: name,
                 vis: vis,
                 attrs: attrs,
-                node: ItemKind::ExternCrate(original_name),
+                node: ItemExternCrate { original: original_name }.into(),
             }
         })
     ));
@@ -334,7 +453,7 @@
             ident: "".into(),
             vis: vis,
             attrs: attrs,
-            node: ItemKind::Use(Box::new(what)),
+            node: ItemUse { path: Box::new(what) }.into(),
         })
     ));
 
@@ -352,14 +471,14 @@
     named!(view_path_simple -> ViewPath, do_parse!(
         path: path >>
         rename: option!(preceded!(keyword!("as"), ident)) >>
-        (ViewPath::Simple(path, rename))
+        (PathSimple { path: path, rename: rename }.into())
     ));
 
     named!(view_path_glob -> ViewPath, do_parse!(
         path: path >>
         punct!("::") >>
         punct!("*") >>
-        (ViewPath::Glob(path))
+        (PathGlob { path: path }.into())
     ));
 
     named!(view_path_list -> ViewPath, do_parse!(
@@ -368,7 +487,7 @@
         punct!("{") >>
         items: terminated_list!(punct!(","), path_list_item) >>
         punct!("}") >>
-        (ViewPath::List(path, items))
+        (PathList { path: path, items: items }.into())
     ));
 
     named!(view_path_list_root -> ViewPath, do_parse!(
@@ -376,10 +495,13 @@
         punct!("{") >>
         items: terminated_list!(punct!(","), path_list_item) >>
         punct!("}") >>
-        (ViewPath::List(Path {
-            global: global.is_some(),
-            segments: Vec::new(),
-        }, items))
+        (PathList {
+            path: Path {
+                global: global.is_some(),
+                segments: Vec::new(),
+            },
+            items: items,
+        }.into())
     ));
 
     named!(path_list_item -> PathListItem, do_parse!(
@@ -410,7 +532,11 @@
             ident: id,
             vis: vis,
             attrs: attrs,
-            node: ItemKind::Static(Box::new(ty), mutability, Box::new(value)),
+            node: ItemStatic {
+                ty: Box::new(ty),
+                mutbl: mutability,
+                expr: Box::new(value),
+            }.into(),
         })
     ));
 
@@ -428,7 +554,7 @@
             ident: id,
             vis: vis,
             attrs: attrs,
-            node: ItemKind::Const(Box::new(ty), Box::new(value)),
+            node: ItemConst { ty: Box::new(ty), expr: Box::new(value) }.into(),
         })
     ));
 
@@ -458,23 +584,23 @@
                 attrs.extend(inner_attrs);
                 attrs
             },
-            node: ItemKind::Fn(
-                Box::new(FnDecl {
+            node: ItemFn {
+                decl: Box::new(FnDecl {
                     inputs: inputs,
                     output: ret.map(FunctionRetTy::Ty).unwrap_or(FunctionRetTy::Default),
                     variadic: false,
                 }),
-                unsafety,
-                constness,
-                abi,
-                Generics {
+                unsafety: unsafety,
+                constness: constness,
+                abi: abi,
+                generics: Generics {
                     where_clause: where_clause,
                     .. generics
                 },
-                Box::new(Block {
+                block: Box::new(Block {
                     stmts: stmts,
                 }),
-            ),
+            }.into(),
         })
     ));
 
@@ -485,21 +611,21 @@
             mutability: mutability >>
             keyword!("self") >>
             not!(punct!(":")) >>
-            (FnArg::SelfRef(lt, mutability))
+            (ArgSelfRef { lifetime: lt, mutbl: mutability }.into())
         )
         |
         do_parse!(
             mutability: mutability >>
             keyword!("self") >>
             not!(punct!(":")) >>
-            (FnArg::SelfValue(mutability))
+            (ArgSelf { mutbl: mutability }.into())
         )
         |
         do_parse!(
             pat: pat >>
             punct!(":") >>
             ty: ty >>
-            (FnArg::Captured(pat, ty))
+            (ArgCaptured { pat: pat, ty: ty }.into())
         )
         |
         ty => { FnArg::Ignored }
@@ -531,13 +657,13 @@
                     attrs.extend(inner_attrs);
                     attrs
                 },
-                node: ItemKind::Mod(Some(items)),
+                node: ItemMod { items: Some(items) }.into(),
             },
             None => Item {
                 ident: id,
                 vis: vis,
                 attrs: outer_attrs,
-                node: ItemKind::Mod(None),
+                node: ItemMod { items: None }.into(),
             },
         })
     ));
@@ -552,10 +678,10 @@
             ident: "".into(),
             vis: Visibility::Inherited,
             attrs: attrs,
-            node: ItemKind::ForeignMod(ForeignMod {
+            node: ItemForeignMod {
                 abi: abi,
                 items: items,
-            }),
+            }.into(),
         })
     ));
 
@@ -582,17 +708,17 @@
         (ForeignItem {
             ident: name,
             attrs: attrs,
-            node: ForeignItemKind::Fn(
-                Box::new(FnDecl {
+            node: ForeignItemFn {
+                decl: Box::new(FnDecl {
                     inputs: inputs,
                     output: ret.map(FunctionRetTy::Ty).unwrap_or(FunctionRetTy::Default),
                     variadic: variadic.is_some(),
                 }),
-                Generics {
+                generics: Generics {
                     where_clause: where_clause,
                     .. generics
                 },
-            ),
+            }.into(),
             vis: vis,
         })
     ));
@@ -609,7 +735,7 @@
         (ForeignItem {
             ident: id,
             attrs: attrs,
-            node: ForeignItemKind::Static(Box::new(ty), mutability),
+            node: ForeignItemStatic { ty: Box::new(ty), mutbl: mutability }.into(),
             vis: vis,
         })
     ));
@@ -628,13 +754,13 @@
             ident: id,
             vis: vis,
             attrs: attrs,
-            node: ItemKind::Ty(
-                Box::new(ty),
-                Generics {
+            node: ItemTy {
+                ty: Box::new(ty),
+                generics: Generics {
                     where_clause: where_clause,
                     ..generics
                 },
-            ),
+            }.into(),
         })
     ));
 
@@ -646,10 +772,10 @@
             attrs: def.attrs,
             node: match def.body {
                 Body::Enum(variants) => {
-                    ItemKind::Enum(variants, def.generics)
+                    ItemEnum { variants: variants, generics: def.generics }.into()
                 }
                 Body::Struct(variant_data) => {
-                    ItemKind::Struct(variant_data, def.generics)
+                    ItemStruct { data: variant_data, generics: def.generics }.into()
                 }
             }
         }
@@ -667,13 +793,13 @@
             ident: id,
             vis: vis,
             attrs: attrs,
-            node: ItemKind::Union(
-                VariantData::Struct(fields),
-                Generics {
+            node: ItemUnion {
+                data: VariantData::Struct(fields),
+                generics: Generics {
                     where_clause: where_clause,
                     .. generics
                 },
-            ),
+            }.into(),
         })
     ));
 
@@ -696,15 +822,15 @@
             ident: id,
             vis: vis,
             attrs: attrs,
-            node: ItemKind::Trait(
-                unsafety,
-                Generics {
+            node: ItemTrait {
+                unsafety: unsafety,
+                generics: Generics {
                     where_clause: where_clause,
                     .. generics
                 },
-                bounds,
-                body,
-            ),
+                supertraits: bounds,
+                items: body,
+            }.into(),
         })
     ));
 
@@ -721,7 +847,7 @@
             ident: "".into(),
             vis: Visibility::Inherited,
             attrs: attrs,
-            node: ItemKind::DefaultImpl(unsafety, path),
+            node: ItemDefaultImpl { unsafety: unsafety, path: path }.into(),
         })
     ));
 
@@ -746,7 +872,7 @@
         (TraitItem {
             ident: id,
             attrs: attrs,
-            node: TraitItemKind::Const(ty, value),
+            node: TraitItemConst { ty: ty, default: value }.into(),
         })
     ));
 
@@ -781,8 +907,8 @@
                     attrs.extend(inner_attrs);
                     attrs
                 },
-                node: TraitItemKind::Method(
-                    MethodSig {
+                node: TraitItemMethod {
+                    sig: MethodSig {
                         unsafety: unsafety,
                         constness: constness,
                         abi: abi,
@@ -796,8 +922,8 @@
                             .. generics
                         },
                     },
-                    stmts.map(|stmts| Block { stmts: stmts }),
-                ),
+                    default: stmts.map(|stmts| Block { stmts: stmts }),
+                }.into(),
             }
         })
     ));
@@ -815,7 +941,7 @@
         (TraitItem {
             ident: id,
             attrs: attrs,
-            node: TraitItemKind::Type(bounds, default),
+            node: TraitItemType { bounds: bounds, default: default }.into(),
         })
     ));
 
@@ -862,17 +988,17 @@
             ident: "".into(),
             vis: Visibility::Inherited,
             attrs: attrs,
-            node: ItemKind::Impl(
-                unsafety,
-                polarity_path.0,
-                Generics {
+            node: ItemImpl {
+                unsafety: unsafety,
+                polarity: polarity_path.0,
+                generics: Generics {
                     where_clause: where_clause,
                     .. generics
                 },
-                polarity_path.1,
-                Box::new(self_ty),
-                body,
-            ),
+                trait_: polarity_path.1,
+                self_ty: Box::new(self_ty),
+                items: body,
+            }.into(),
         })
     ));
 
@@ -902,7 +1028,7 @@
             vis: vis,
             defaultness: defaultness,
             attrs: attrs,
-            node: ImplItemKind::Const(ty, value),
+            node: ImplItemConst { ty: ty, expr: value}.into(),
         })
     ));
 
@@ -934,8 +1060,8 @@
                 attrs.extend(inner_attrs);
                 attrs
             },
-            node: ImplItemKind::Method(
-                MethodSig {
+            node: ImplItemMethod {
+                sig: MethodSig {
                     unsafety: unsafety,
                     constness: constness,
                     abi: abi,
@@ -949,10 +1075,10 @@
                         .. generics
                     },
                 },
-                Block {
+                block: Block {
                     stmts: stmts,
                 },
-            ),
+            }.into(),
         })
     ));
 
@@ -970,7 +1096,7 @@
             vis: vis,
             defaultness: defaultness,
             attrs: attrs,
-            node: ImplItemKind::Type(ty),
+            node: ImplItemType { ty: ty }.into(),
         })
     ));
 
@@ -1026,70 +1152,70 @@
         fn to_tokens(&self, tokens: &mut Tokens) {
             tokens.append_all(self.attrs.outer());
             match self.node {
-                ItemKind::ExternCrate(ref original) => {
+                ItemKind::ExternCrate(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("extern");
                     tokens.append("crate");
-                    if let Some(ref original) = *original {
+                    if let Some(ref original) = item.original {
                         original.to_tokens(tokens);
                         tokens.append("as");
                     }
                     self.ident.to_tokens(tokens);
                     tokens.append(";");
                 }
-                ItemKind::Use(ref view_path) => {
+                ItemKind::Use(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("use");
-                    view_path.to_tokens(tokens);
+                    item.path.to_tokens(tokens);
                     tokens.append(";");
                 }
-                ItemKind::Static(ref ty, ref mutability, ref expr) => {
+                ItemKind::Static(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("static");
-                    mutability.to_tokens(tokens);
+                    item.mutbl.to_tokens(tokens);
                     self.ident.to_tokens(tokens);
                     tokens.append(":");
-                    ty.to_tokens(tokens);
+                    item.ty.to_tokens(tokens);
                     tokens.append("=");
-                    expr.to_tokens(tokens);
+                    item.expr.to_tokens(tokens);
                     tokens.append(";");
                 }
-                ItemKind::Const(ref ty, ref expr) => {
+                ItemKind::Const(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("const");
                     self.ident.to_tokens(tokens);
                     tokens.append(":");
-                    ty.to_tokens(tokens);
+                    item.ty.to_tokens(tokens);
                     tokens.append("=");
-                    expr.to_tokens(tokens);
+                    item.expr.to_tokens(tokens);
                     tokens.append(";");
                 }
-                ItemKind::Fn(ref decl, unsafety, constness, ref abi, ref generics, ref block) => {
+                ItemKind::Fn(ref item) => {
                     self.vis.to_tokens(tokens);
-                    constness.to_tokens(tokens);
-                    unsafety.to_tokens(tokens);
-                    abi.to_tokens(tokens);
+                    item.constness.to_tokens(tokens);
+                    item.unsafety.to_tokens(tokens);
+                    item.abi.to_tokens(tokens);
                     tokens.append("fn");
                     self.ident.to_tokens(tokens);
-                    generics.to_tokens(tokens);
+                    item.generics.to_tokens(tokens);
                     tokens.append("(");
-                    tokens.append_separated(&decl.inputs, ",");
+                    tokens.append_separated(&item.decl.inputs, ",");
                     tokens.append(")");
-                    if let FunctionRetTy::Ty(ref ty) = decl.output {
+                    if let FunctionRetTy::Ty(ref ty) = item.decl.output {
                         tokens.append("->");
                         ty.to_tokens(tokens);
                     }
-                    generics.where_clause.to_tokens(tokens);
+                    item.generics.where_clause.to_tokens(tokens);
                     tokens.append("{");
                     tokens.append_all(self.attrs.inner());
-                    tokens.append_all(&block.stmts);
+                    tokens.append_all(&item.block.stmts);
                     tokens.append("}");
                 }
-                ItemKind::Mod(ref items) => {
+                ItemKind::Mod(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("mod");
                     self.ident.to_tokens(tokens);
-                    match *items {
+                    match item.items {
                         Some(ref items) => {
                             tokens.append("{");
                             tokens.append_all(self.attrs.inner());
@@ -1106,96 +1232,96 @@
                     tokens.append_all(&foreign_mod.items);
                     tokens.append("}");
                 }
-                ItemKind::Ty(ref ty, ref generics) => {
+                ItemKind::Ty(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("type");
                     self.ident.to_tokens(tokens);
-                    generics.to_tokens(tokens);
-                    generics.where_clause.to_tokens(tokens);
+                    item.generics.to_tokens(tokens);
+                    item.generics.where_clause.to_tokens(tokens);
                     tokens.append("=");
-                    ty.to_tokens(tokens);
+                    item.ty.to_tokens(tokens);
                     tokens.append(";");
                 }
-                ItemKind::Enum(ref variants, ref generics) => {
+                ItemKind::Enum(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("enum");
                     self.ident.to_tokens(tokens);
-                    generics.to_tokens(tokens);
-                    generics.where_clause.to_tokens(tokens);
+                    item.generics.to_tokens(tokens);
+                    item.generics.where_clause.to_tokens(tokens);
                     tokens.append("{");
-                    for variant in variants {
+                    for variant in &item.variants {
                         variant.to_tokens(tokens);
                         tokens.append(",");
                     }
                     tokens.append("}");
                 }
-                ItemKind::Struct(ref variant_data, ref generics) => {
+                ItemKind::Struct(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("struct");
                     self.ident.to_tokens(tokens);
-                    generics.to_tokens(tokens);
-                    match *variant_data {
+                    item.generics.to_tokens(tokens);
+                    match item.data {
                         VariantData::Struct(_) => {
-                            generics.where_clause.to_tokens(tokens);
-                            variant_data.to_tokens(tokens);
+                            item.generics.where_clause.to_tokens(tokens);
+                            item.data.to_tokens(tokens);
                             // no semicolon
                         }
                         VariantData::Tuple(_) => {
-                            variant_data.to_tokens(tokens);
-                            generics.where_clause.to_tokens(tokens);
+                            item.data.to_tokens(tokens);
+                            item.generics.where_clause.to_tokens(tokens);
                             tokens.append(";");
                         }
                         VariantData::Unit => {
-                            generics.where_clause.to_tokens(tokens);
+                            item.generics.where_clause.to_tokens(tokens);
                             tokens.append(";");
                         }
                     }
                 }
-                ItemKind::Union(ref variant_data, ref generics) => {
+                ItemKind::Union(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("union");
                     self.ident.to_tokens(tokens);
-                    generics.to_tokens(tokens);
-                    generics.where_clause.to_tokens(tokens);
-                    variant_data.to_tokens(tokens);
+                    item.generics.to_tokens(tokens);
+                    item.generics.where_clause.to_tokens(tokens);
+                    item.data.to_tokens(tokens);
                 }
-                ItemKind::Trait(unsafety, ref generics, ref bound, ref items) => {
+                ItemKind::Trait(ref item) => {
                     self.vis.to_tokens(tokens);
-                    unsafety.to_tokens(tokens);
+                    item.unsafety.to_tokens(tokens);
                     tokens.append("trait");
                     self.ident.to_tokens(tokens);
-                    generics.to_tokens(tokens);
-                    if !bound.is_empty() {
+                    item.generics.to_tokens(tokens);
+                    if !item.supertraits.is_empty() {
                         tokens.append(":");
-                        tokens.append_separated(bound, "+");
+                        tokens.append_separated(&item.supertraits, "+");
                     }
-                    generics.where_clause.to_tokens(tokens);
+                    item.generics.where_clause.to_tokens(tokens);
                     tokens.append("{");
-                    tokens.append_all(items);
+                    tokens.append_all(&item.items);
                     tokens.append("}");
                 }
-                ItemKind::DefaultImpl(unsafety, ref path) => {
-                    unsafety.to_tokens(tokens);
+                ItemKind::DefaultImpl(ref item) => {
+                    item.unsafety.to_tokens(tokens);
                     tokens.append("impl");
-                    path.to_tokens(tokens);
+                    item.path.to_tokens(tokens);
                     tokens.append("for");
                     tokens.append("..");
                     tokens.append("{");
                     tokens.append("}");
                 }
-                ItemKind::Impl(unsafety, polarity, ref generics, ref path, ref ty, ref items) => {
-                    unsafety.to_tokens(tokens);
+                ItemKind::Impl(ref item) => {
+                    item.unsafety.to_tokens(tokens);
                     tokens.append("impl");
-                    generics.to_tokens(tokens);
-                    if let Some(ref path) = *path {
-                        polarity.to_tokens(tokens);
+                    item.generics.to_tokens(tokens);
+                    if let Some(ref path) = item.trait_ {
+                        item.polarity.to_tokens(tokens);
                         path.to_tokens(tokens);
                         tokens.append("for");
                     }
-                    ty.to_tokens(tokens);
-                    generics.where_clause.to_tokens(tokens);
+                    item.self_ty.to_tokens(tokens);
+                    item.generics.where_clause.to_tokens(tokens);
                     tokens.append("{");
-                    tokens.append_all(items);
+                    tokens.append_all(&item.items);
                     tokens.append("}");
                 }
                 ItemKind::Mac(ref mac) => {
@@ -1216,34 +1342,36 @@
         }
     }
 
-    impl ToTokens for ViewPath {
+    impl ToTokens for PathSimple {
         fn to_tokens(&self, tokens: &mut Tokens) {
-            match *self {
-                ViewPath::Simple(ref path, ref rename) => {
-                    path.to_tokens(tokens);
-                    if let Some(ref rename) = *rename {
-                        tokens.append("as");
-                        rename.to_tokens(tokens);
-                    }
-                }
-                ViewPath::Glob(ref path) => {
-                    path.to_tokens(tokens);
-                    tokens.append("::");
-                    tokens.append("*");
-                }
-                ViewPath::List(ref path, ref items) => {
-                    path.to_tokens(tokens);
-                    if path.global || !path.segments.is_empty() {
-                        tokens.append("::");
-                    }
-                    tokens.append("{");
-                    tokens.append_separated(items, ",");
-                    tokens.append("}");
-                }
+            self.path.to_tokens(tokens);
+            if let Some(ref rename) = self.rename {
+                tokens.append("as");
+                rename.to_tokens(tokens);
             }
         }
     }
 
+    impl ToTokens for PathGlob {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.path.to_tokens(tokens);
+            tokens.append("::");
+            tokens.append("*");
+        }
+    }
+
+    impl ToTokens for PathList {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.path.to_tokens(tokens);
+            if self.path.global || !self.path.segments.is_empty() {
+                tokens.append("::");
+            }
+            tokens.append("{");
+            tokens.append_separated(&self.items, ",");
+            tokens.append("}");
+        }
+    }
+
     impl ToTokens for PathListItem {
         fn to_tokens(&self, tokens: &mut Tokens) {
             self.name.to_tokens(tokens);
@@ -1258,33 +1386,33 @@
         fn to_tokens(&self, tokens: &mut Tokens) {
             tokens.append_all(self.attrs.outer());
             match self.node {
-                TraitItemKind::Const(ref ty, ref expr) => {
+                TraitItemKind::Const(ref item) => {
                     tokens.append("const");
                     self.ident.to_tokens(tokens);
                     tokens.append(":");
-                    ty.to_tokens(tokens);
-                    if let Some(ref expr) = *expr {
+                    item.ty.to_tokens(tokens);
+                    if let Some(ref expr) = item.default {
                         tokens.append("=");
                         expr.to_tokens(tokens);
                     }
                     tokens.append(";");
                 }
-                TraitItemKind::Method(ref sig, ref block) => {
-                    sig.constness.to_tokens(tokens);
-                    sig.unsafety.to_tokens(tokens);
-                    sig.abi.to_tokens(tokens);
+                TraitItemKind::Method(ref item) => {
+                    item.sig.constness.to_tokens(tokens);
+                    item.sig.unsafety.to_tokens(tokens);
+                    item.sig.abi.to_tokens(tokens);
                     tokens.append("fn");
                     self.ident.to_tokens(tokens);
-                    sig.generics.to_tokens(tokens);
+                    item.sig.generics.to_tokens(tokens);
                     tokens.append("(");
-                    tokens.append_separated(&sig.decl.inputs, ",");
+                    tokens.append_separated(&item.sig.decl.inputs, ",");
                     tokens.append(")");
-                    if let FunctionRetTy::Ty(ref ty) = sig.decl.output {
+                    if let FunctionRetTy::Ty(ref ty) = item.sig.decl.output {
                         tokens.append("->");
                         ty.to_tokens(tokens);
                     }
-                    sig.generics.where_clause.to_tokens(tokens);
-                    match *block {
+                    item.sig.generics.where_clause.to_tokens(tokens);
+                    match item.default {
                         Some(ref block) => {
                             tokens.append("{");
                             tokens.append_all(self.attrs.inner());
@@ -1294,14 +1422,14 @@
                         None => tokens.append(";"),
                     }
                 }
-                TraitItemKind::Type(ref bound, ref default) => {
+                TraitItemKind::Type(ref item) => {
                     tokens.append("type");
                     self.ident.to_tokens(tokens);
-                    if !bound.is_empty() {
+                    if !item.bounds.is_empty() {
                         tokens.append(":");
-                        tokens.append_separated(bound, "+");
+                        tokens.append_separated(&item.bounds, "+");
                     }
-                    if let Some(ref default) = *default {
+                    if let Some(ref default) = item.default {
                         tokens.append("=");
                         default.to_tokens(tokens);
                     }
@@ -1324,46 +1452,46 @@
         fn to_tokens(&self, tokens: &mut Tokens) {
             tokens.append_all(self.attrs.outer());
             match self.node {
-                ImplItemKind::Const(ref ty, ref expr) => {
+                ImplItemKind::Const(ref item) => {
                     self.vis.to_tokens(tokens);
                     self.defaultness.to_tokens(tokens);
                     tokens.append("const");
                     self.ident.to_tokens(tokens);
                     tokens.append(":");
-                    ty.to_tokens(tokens);
+                    item.ty.to_tokens(tokens);
                     tokens.append("=");
-                    expr.to_tokens(tokens);
+                    item.expr.to_tokens(tokens);
                     tokens.append(";");
                 }
-                ImplItemKind::Method(ref sig, ref block) => {
+                ImplItemKind::Method(ref item) => {
                     self.vis.to_tokens(tokens);
                     self.defaultness.to_tokens(tokens);
-                    sig.constness.to_tokens(tokens);
-                    sig.unsafety.to_tokens(tokens);
-                    sig.abi.to_tokens(tokens);
+                    item.sig.constness.to_tokens(tokens);
+                    item.sig.unsafety.to_tokens(tokens);
+                    item.sig.abi.to_tokens(tokens);
                     tokens.append("fn");
                     self.ident.to_tokens(tokens);
-                    sig.generics.to_tokens(tokens);
+                    item.sig.generics.to_tokens(tokens);
                     tokens.append("(");
-                    tokens.append_separated(&sig.decl.inputs, ",");
+                    tokens.append_separated(&item.sig.decl.inputs, ",");
                     tokens.append(")");
-                    if let FunctionRetTy::Ty(ref ty) = sig.decl.output {
+                    if let FunctionRetTy::Ty(ref ty) = item.sig.decl.output {
                         tokens.append("->");
                         ty.to_tokens(tokens);
                     }
-                    sig.generics.where_clause.to_tokens(tokens);
+                    item.sig.generics.where_clause.to_tokens(tokens);
                     tokens.append("{");
                     tokens.append_all(self.attrs.inner());
-                    tokens.append_all(&block.stmts);
+                    tokens.append_all(&item.block.stmts);
                     tokens.append("}");
                 }
-                ImplItemKind::Type(ref ty) => {
+                ImplItemKind::Type(ref item) => {
                     self.vis.to_tokens(tokens);
                     self.defaultness.to_tokens(tokens);
                     tokens.append("type");
                     self.ident.to_tokens(tokens);
                     tokens.append("=");
-                    ty.to_tokens(tokens);
+                    item.ty.to_tokens(tokens);
                     tokens.append(";");
                 }
                 ImplItemKind::Macro(ref mac) => {
@@ -1383,62 +1511,61 @@
         fn to_tokens(&self, tokens: &mut Tokens) {
             tokens.append_all(self.attrs.outer());
             match self.node {
-                ForeignItemKind::Fn(ref decl, ref generics) => {
+                ForeignItemKind::Fn(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("fn");
                     self.ident.to_tokens(tokens);
-                    generics.to_tokens(tokens);
+                    item.generics.to_tokens(tokens);
                     tokens.append("(");
-                    tokens.append_separated(&decl.inputs, ",");
-                    if decl.variadic {
-                        if !decl.inputs.is_empty() {
+                    tokens.append_separated(&item.decl.inputs, ",");
+                    if item.decl.variadic {
+                        if !item.decl.inputs.is_empty() {
                             tokens.append(",");
                         }
                         tokens.append("...");
                     }
                     tokens.append(")");
-                    if let FunctionRetTy::Ty(ref ty) = decl.output {
+                    if let FunctionRetTy::Ty(ref ty) = item.decl.output {
                         tokens.append("->");
                         ty.to_tokens(tokens);
                     }
-                    generics.where_clause.to_tokens(tokens);
+                    item.generics.where_clause.to_tokens(tokens);
                     tokens.append(";");
                 }
-                ForeignItemKind::Static(ref ty, mutability) => {
+                ForeignItemKind::Static(ref item) => {
                     self.vis.to_tokens(tokens);
                     tokens.append("static");
-                    mutability.to_tokens(tokens);
+                    item.mutbl.to_tokens(tokens);
                     self.ident.to_tokens(tokens);
                     tokens.append(":");
-                    ty.to_tokens(tokens);
+                    item.ty.to_tokens(tokens);
                     tokens.append(";");
                 }
             }
         }
     }
 
-    impl ToTokens for FnArg {
+    impl ToTokens for ArgSelfRef {
         fn to_tokens(&self, tokens: &mut Tokens) {
-            match *self {
-                FnArg::SelfRef(ref lifetime, mutability) => {
-                    tokens.append("&");
-                    lifetime.to_tokens(tokens);
-                    mutability.to_tokens(tokens);
-                    tokens.append("self");
-                }
-                FnArg::SelfValue(mutability) => {
-                    mutability.to_tokens(tokens);
-                    tokens.append("self");
-                }
-                FnArg::Captured(ref pat, ref ty) => {
-                    pat.to_tokens(tokens);
-                    tokens.append(":");
-                    ty.to_tokens(tokens);
-                }
-                FnArg::Ignored(ref ty) => {
-                    ty.to_tokens(tokens);
-                }
-            }
+            tokens.append("&");
+            self.lifetime.to_tokens(tokens);
+            self.mutbl.to_tokens(tokens);
+            tokens.append("self");
+        }
+    }
+
+    impl ToTokens for ArgSelf {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.mutbl.to_tokens(tokens);
+            tokens.append("self");
+        }
+    }
+
+    impl ToTokens for ArgCaptured {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.pat.to_tokens(tokens);
+            tokens.append(":");
+            self.ty.to_tokens(tokens);
         }
     }
 
diff --git a/src/krate.rs b/src/krate.rs
index 077a36d..113e142 100644
--- a/src/krate.rs
+++ b/src/krate.rs
@@ -1,10 +1,11 @@
 use super::*;
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Crate {
-    pub shebang: Option<String>,
-    pub attrs: Vec<Attribute>,
-    pub items: Vec<Item>,
+ast_struct! {
+    pub struct Crate {
+        pub shebang: Option<String>,
+        pub attrs: Vec<Attribute>,
+        pub items: Vec<Item>,
+    }
 }
 
 #[cfg(feature = "parsing")]
diff --git a/src/lib.rs b/src/lib.rs
index 3f7f0df..f30683f 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -15,11 +15,16 @@
 #[cfg(feature = "aster")]
 pub mod aster;
 
+#[macro_use]
+mod macros;
+
 mod attr;
-pub use attr::{Attribute, AttrStyle, MetaItem, NestedMetaItem};
+pub use attr::{Attribute, AttrStyle, MetaItem, NestedMetaItem, MetaItemList,
+               MetaNameValue};
 
 mod constant;
-pub use constant::ConstExpr;
+pub use constant::{ConstExpr, ConstCall, ConstBinary, ConstUnary, ConstCast,
+                   ConstIndex, ConstParen};
 
 mod data;
 pub use data::{Field, Variant, VariantData, Visibility};
@@ -31,7 +36,13 @@
 mod expr;
 #[cfg(feature = "full")]
 pub use expr::{Arm, BindingMode, Block, CaptureBy, Expr, ExprKind, FieldPat, FieldValue, Local,
-               MacStmtStyle, Pat, RangeLimits, Stmt};
+               MacStmtStyle, Pat, RangeLimits, Stmt, ExprBox, ExprInPlace,
+               ExprArray, ExprCall, ExprMethodCall, ExprTup, ExprBinary, ExprUnary,
+               ExprCast, ExprType, ExprIf, ExprIfLet, ExprWhile, ExprWhileLet,
+               ExprForLoop, ExprLoop, ExprMatch, ExprClosure, ExprBlock,
+               ExprAssign, ExprAssignOp, ExprField, ExprTupField, ExprIndex,
+               ExprRange, ExprPath, ExprAddrOf, ExprBreak, ExprContinue,
+               ExprRet, ExprStruct, ExprRepeat, ExprParen, ExprTry, ExprCatch};
 
 mod generics;
 pub use generics::{Generics, Lifetime, LifetimeDef, TraitBoundModifier, TyParam, TyParamBound,
@@ -46,9 +57,15 @@
 #[cfg(feature = "full")]
 mod item;
 #[cfg(feature = "full")]
-pub use item::{Constness, Defaultness, FnArg, FnDecl, ForeignItemKind, ForeignItem, ForeignMod,
+pub use item::{Constness, Defaultness, FnArg, FnDecl, ForeignItemKind, ForeignItem, ItemForeignMod,
                ImplItem, ImplItemKind, ImplPolarity, Item, ItemKind, MethodSig, PathListItem,
-               TraitItem, TraitItemKind, ViewPath};
+               TraitItem, TraitItemKind, ViewPath, ItemExternCrate, ItemUse,
+               ItemStatic, ItemConst, ItemFn, ItemMod, ItemTy, ItemEnum,
+               ItemStruct, ItemUnion, ItemTrait, ItemDefaultImpl, ItemImpl,
+               PathSimple, PathGlob, PathList, ForeignItemFn, ForeignItemStatic,
+               TraitItemConst, TraitItemMethod, TraitItemType,
+               ImplItemConst, ImplItemMethod, ImplItemType, ArgSelfRef,
+               ArgSelf, ArgCaptured};
 
 #[cfg(feature = "full")]
 mod krate;
@@ -75,7 +92,9 @@
 mod ty;
 pub use ty::{Abi, AngleBracketedParameterData, BareFnArg, BareFnTy, FunctionRetTy, MutTy,
              Mutability, ParenthesizedParameterData, Path, PathParameters, PathSegment,
-             PolyTraitRef, QSelf, Ty, TypeBinding, Unsafety};
+             PolyTraitRef, QSelf, Ty, TypeBinding, Unsafety, TySlice, TyArray,
+             TyPtr, TyRptr, TyBareFn, TyNever, TyTup, TyPath, TyTraitObject,
+             TyImplTrait, TyParen, TyInfer};
 
 #[cfg(feature = "visit")]
 pub mod visit;
diff --git a/src/lit.rs b/src/lit.rs
index 81be285..4811485 100644
--- a/src/lit.rs
+++ b/src/lit.rs
@@ -1,32 +1,34 @@
-/// Literal kind.
-///
-/// E.g. `"foo"`, `42`, `12.34` or `bool`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Lit {
-    /// A string literal (`"foo"`)
-    Str(String, StrStyle),
-    /// A byte string (`b"foo"`)
-    ByteStr(Vec<u8>, StrStyle),
-    /// A byte char (`b'f'`)
-    Byte(u8),
-    /// A character literal (`'a'`)
-    Char(char),
-    /// An integer literal (`1`)
-    Int(u64, IntTy),
-    /// A float literal (`1f64` or `1E10f64` or `1.0E10`)
-    Float(String, FloatTy),
-    /// A boolean literal
-    Bool(bool),
+ast_enum! {
+    /// Literal kind.
+    ///
+    /// E.g. `"foo"`, `42`, `12.34` or `bool`
+    pub enum Lit {
+        /// A string literal (`"foo"`)
+        Str(String, StrStyle),
+        /// A byte string (`b"foo"`)
+        ByteStr(Vec<u8>, StrStyle),
+        /// A byte char (`b'f'`)
+        Byte(u8),
+        /// A character literal (`'a'`)
+        Char(char),
+        /// An integer literal (`1`)
+        Int(u64, IntTy),
+        /// A float literal (`1f64` or `1E10f64` or `1.0E10`)
+        Float(String, FloatTy),
+        /// A boolean literal
+        Bool(bool),
+    }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum StrStyle {
-    /// A regular string, like `"foo"`
-    Cooked,
-    /// A raw string, like `r##"foo"##`
-    ///
-    /// The uint is the number of `#` symbols used
-    Raw(usize),
+ast_enum! {
+    pub enum StrStyle {
+        /// A regular string, like `"foo"`
+        Cooked,
+        /// A raw string, like `r##"foo"##`
+        ///
+        /// The uint is the number of `#` symbols used
+        Raw(usize),
+    }
 }
 
 impl From<String> for Lit {
@@ -65,26 +67,30 @@
     }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum IntTy {
-    Isize,
-    I8,
-    I16,
-    I32,
-    I64,
-    Usize,
-    U8,
-    U16,
-    U32,
-    U64,
-    Unsuffixed,
+ast_enum! {
+    #[derive(Copy)]
+    pub enum IntTy {
+        Isize,
+        I8,
+        I16,
+        I32,
+        I64,
+        Usize,
+        U8,
+        U16,
+        U32,
+        U64,
+        Unsuffixed,
+    }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum FloatTy {
-    F32,
-    F64,
-    Unsuffixed,
+ast_enum! {
+    #[derive(Copy)]
+    pub enum FloatTy {
+        F32,
+        F64,
+        Unsuffixed,
+    }
 }
 
 macro_rules! impl_from_for_lit {
diff --git a/src/mac.rs b/src/mac.rs
index 26b3c61..e6132e4 100644
--- a/src/mac.rs
+++ b/src/mac.rs
@@ -1,112 +1,120 @@
 use super::*;
 
-/// Represents a macro invocation. The Path indicates which macro
-/// is being invoked, and the vector of token-trees contains the source
-/// of the macro invocation.
-///
-/// NB: the additional ident for a `macro_rules`-style macro is actually
-/// stored in the enclosing item. Oog.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Mac {
-    pub path: Path,
-    pub tts: Vec<TokenTree>,
+ast_struct! {
+    /// Represents a macro invocation. The Path indicates which macro
+    /// is being invoked, and the vector of token-trees contains the source
+    /// of the macro invocation.
+    ///
+    /// NB: the additional ident for a `macro_rules`-style macro is actually
+    /// stored in the enclosing item. Oog.
+    pub struct Mac {
+        pub path: Path,
+        pub tts: Vec<TokenTree>,
+    }
 }
 
-/// When the main rust parser encounters a syntax-extension invocation, it
-/// parses the arguments to the invocation as a token-tree. This is a very
-/// loose structure, such that all sorts of different AST-fragments can
-/// be passed to syntax extensions using a uniform type.
-///
-/// If the syntax extension is an MBE macro, it will attempt to match its
-/// LHS token tree against the provided token tree, and if it finds a
-/// match, will transcribe the RHS token tree, splicing in any captured
-/// `macro_parser::matched_nonterminals` into the `SubstNt`s it finds.
-///
-/// The RHS of an MBE macro is the only place `SubstNt`s are substituted.
-/// Nothing special happens to misnamed or misplaced `SubstNt`s.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum TokenTree {
-    /// A single token
-    Token(Token),
-    /// A delimited sequence of token trees
-    Delimited(Delimited),
+ast_enum! {
+    /// When the main rust parser encounters a syntax-extension invocation, it
+    /// parses the arguments to the invocation as a token-tree. This is a very
+    /// loose structure, such that all sorts of different AST-fragments can
+    /// be passed to syntax extensions using a uniform type.
+    ///
+    /// If the syntax extension is an MBE macro, it will attempt to match its
+    /// LHS token tree against the provided token tree, and if it finds a
+    /// match, will transcribe the RHS token tree, splicing in any captured
+    /// `macro_parser::matched_nonterminals` into the `SubstNt`s it finds.
+    ///
+    /// The RHS of an MBE macro is the only place `SubstNt`s are substituted.
+    /// Nothing special happens to misnamed or misplaced `SubstNt`s.
+    pub enum TokenTree {
+        /// A single token
+        Token(Token),
+        /// A delimited sequence of token trees
+        Delimited(Delimited),
+    }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Delimited {
-    /// The type of delimiter
-    pub delim: DelimToken,
-    /// The delimited sequence of token trees
-    pub tts: Vec<TokenTree>,
+ast_struct! {
+    pub struct Delimited {
+        /// The type of delimiter
+        pub delim: DelimToken,
+        /// The delimited sequence of token trees
+        pub tts: Vec<TokenTree>,
+    }
 }
 
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Token {
-    // Expression-operator symbols.
-    Eq,
-    Lt,
-    Le,
-    EqEq,
-    Ne,
-    Ge,
-    Gt,
-    AndAnd,
-    OrOr,
-    Not,
-    Tilde,
-    BinOp(BinOpToken),
-    BinOpEq(BinOpToken),
+ast_enum! {
+    pub enum Token {
+        // Expression-operator symbols.
+        Eq,
+        Lt,
+        Le,
+        EqEq,
+        Ne,
+        Ge,
+        Gt,
+        AndAnd,
+        OrOr,
+        Not,
+        Tilde,
+        BinOp(BinOpToken),
+        BinOpEq(BinOpToken),
 
-    // Structural symbols
-    At,
-    Dot,
-    DotDot,
-    DotDotDot,
-    Comma,
-    Semi,
-    Colon,
-    ModSep,
-    RArrow,
-    LArrow,
-    FatArrow,
-    Pound,
-    Dollar,
-    Question,
+        // Structural symbols
+        At,
+        Dot,
+        DotDot,
+        DotDotDot,
+        Comma,
+        Semi,
+        Colon,
+        ModSep,
+        RArrow,
+        LArrow,
+        FatArrow,
+        Pound,
+        Dollar,
+        Question,
 
-    // Literals
-    Literal(Lit),
+        // Literals
+        Literal(Lit),
 
-    // Name components
-    Ident(Ident),
-    Underscore,
-    Lifetime(Ident),
+        // Name components
+        Ident(Ident),
+        Underscore,
+        Lifetime(Ident),
 
-    DocComment(String),
+        DocComment(String),
+    }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum BinOpToken {
-    Plus,
-    Minus,
-    Star,
-    Slash,
-    Percent,
-    Caret,
-    And,
-    Or,
-    Shl,
-    Shr,
+ast_enum! {
+    #[derive(Copy)]
+    pub enum BinOpToken {
+        Plus,
+        Minus,
+        Star,
+        Slash,
+        Percent,
+        Caret,
+        And,
+        Or,
+        Shl,
+        Shr,
+    }
 }
 
-/// A delimiter token
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum DelimToken {
-    /// A round parenthesis: `(` or `)`
-    Paren,
-    /// A square bracket: `[` or `]`
-    Bracket,
-    /// A curly brace: `{` or `}`
-    Brace,
+ast_enum! {
+    /// A delimiter token
+    #[derive(Copy)]
+    pub enum DelimToken {
+        /// A round parenthesis: `(` or `)`
+        Paren,
+        /// A square bracket: `[` or `]`
+        Bracket,
+        /// A curly brace: `{` or `}`
+        Brace,
+    }
 }
 
 #[cfg(feature = "parsing")]
diff --git a/src/macros.rs b/src/macros.rs
new file mode 100644
index 0000000..656fe90
--- /dev/null
+++ b/src/macros.rs
@@ -0,0 +1,101 @@
+macro_rules! ast_struct {
+    (
+        $(#[$attr:meta])*
+        pub struct $name:ident {
+            $(
+                $(#[$field_attr:meta])*
+                pub $field:ident: $ty:ty,
+            )*
+        }
+    ) => {
+        $(#[$attr])*
+        #[derive(Debug, Clone, Eq, PartialEq, Hash)]
+        pub struct $name {
+            $(
+                $(#[$field_attr])*
+                pub $field: $ty,
+            )*
+        }
+    }
+}
+
+macro_rules! ast_enum {
+    (
+        $(#[$enum_attr:meta])*
+        pub enum $name:ident { $($variants:tt)* }
+    ) => (
+        $(#[$enum_attr])*
+        #[derive(Debug, Clone, Eq, PartialEq, Hash)]
+        pub enum $name {
+            $($variants)*
+        }
+    )
+}
+
+macro_rules! ast_enum_of_structs {
+    (
+        $(#[$enum_attr:meta])*
+        pub enum $name:ident {
+            $(
+                $(#[$variant_attr:meta])*
+                pub $variant:ident($member:ident $($rest:tt)*),
+            )*
+        }
+
+        $($remaining:tt)*
+    ) => (
+        ast_enum! {
+            $(#[$enum_attr])*
+            pub enum $name {
+                $(
+                    $(#[$variant_attr])*
+                    $variant($member),
+                )*
+            }
+        }
+
+        $(
+            maybe_ast_struct! {
+                $(#[$variant_attr])*
+                pub struct $member $($rest)*
+            }
+
+            impl From<$member> for $name {
+                fn from(e: $member) -> $name {
+                    $name::$variant(e)
+                }
+            }
+        )*
+
+        generate_to_tokens! {
+            $($remaining)*
+            enum $name { $($variant,)* }
+        }
+    )
+}
+
+macro_rules! generate_to_tokens {
+    (do_not_generate_to_tokens $($foo:tt)*) => ();
+
+    (enum $name:ident { $($variant:ident,)* }) => (
+        #[cfg(feature = "printing")]
+        impl ::quote::ToTokens for $name {
+            fn to_tokens(&self, tokens: &mut ::quote::Tokens) {
+                match *self {
+                    $(
+                        $name::$variant(ref e) => e.to_tokens(tokens),
+                    )*
+                }
+            }
+        }
+    )
+}
+
+macro_rules! maybe_ast_struct {
+    (
+        $(#[$attr:meta])*
+        pub struct $name:ident
+    ) => ();
+
+    ($($rest:tt)*) => (ast_struct! { $($rest)* });
+}
diff --git a/src/op.rs b/src/op.rs
index d4b0bc7..cac1741 100644
--- a/src/op.rs
+++ b/src/op.rs
@@ -1,51 +1,55 @@
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum BinOp {
-    /// The `+` operator (addition)
-    Add,
-    /// The `-` operator (subtraction)
-    Sub,
-    /// The `*` operator (multiplication)
-    Mul,
-    /// The `/` operator (division)
-    Div,
-    /// The `%` operator (modulus)
-    Rem,
-    /// The `&&` operator (logical and)
-    And,
-    /// The `||` operator (logical or)
-    Or,
-    /// The `^` operator (bitwise xor)
-    BitXor,
-    /// The `&` operator (bitwise and)
-    BitAnd,
-    /// The `|` operator (bitwise or)
-    BitOr,
-    /// The `<<` operator (shift left)
-    Shl,
-    /// The `>>` operator (shift right)
-    Shr,
-    /// The `==` operator (equality)
-    Eq,
-    /// The `<` operator (less than)
-    Lt,
-    /// The `<=` operator (less than or equal to)
-    Le,
-    /// The `!=` operator (not equal to)
-    Ne,
-    /// The `>=` operator (greater than or equal to)
-    Ge,
-    /// The `>` operator (greater than)
-    Gt,
+ast_enum! {
+    #[derive(Copy)]
+    pub enum BinOp {
+        /// The `+` operator (addition)
+        Add,
+        /// The `-` operator (subtraction)
+        Sub,
+        /// The `*` operator (multiplication)
+        Mul,
+        /// The `/` operator (division)
+        Div,
+        /// The `%` operator (modulus)
+        Rem,
+        /// The `&&` operator (logical and)
+        And,
+        /// The `||` operator (logical or)
+        Or,
+        /// The `^` operator (bitwise xor)
+        BitXor,
+        /// The `&` operator (bitwise and)
+        BitAnd,
+        /// The `|` operator (bitwise or)
+        BitOr,
+        /// The `<<` operator (shift left)
+        Shl,
+        /// The `>>` operator (shift right)
+        Shr,
+        /// The `==` operator (equality)
+        Eq,
+        /// The `<` operator (less than)
+        Lt,
+        /// The `<=` operator (less than or equal to)
+        Le,
+        /// The `!=` operator (not equal to)
+        Ne,
+        /// The `>=` operator (greater than or equal to)
+        Ge,
+        /// The `>` operator (greater than)
+        Gt,
+    }
 }
 
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum UnOp {
-    /// The `*` operator for dereferencing
-    Deref,
-    /// The `!` operator for logical inversion
-    Not,
-    /// The `-` operator for negation
-    Neg,
+ast_enum! {
+    #[derive(Copy)]
+    pub enum UnOp {
+        /// The `*` operator for dereferencing
+        Deref,
+        /// The `!` operator for logical inversion
+        Not,
+        /// The `-` operator for negation
+        Neg,
+    }
 }
 
 #[cfg(feature = "parsing")]
diff --git a/src/ty.rs b/src/ty.rs
index 7b7d3af..4fb3328 100644
--- a/src/ty.rs
+++ b/src/ty.rs
@@ -1,67 +1,95 @@
 use super::*;
 
-/// The different kinds of types recognized by the compiler
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Ty {
-    /// A variable-length array (`[T]`)
-    Slice(Box<Ty>),
-    /// A fixed length array (`[T; n]`)
-    Array(Box<Ty>, ConstExpr),
-    /// A raw pointer (`*const T` or `*mut T`)
-    Ptr(Box<MutTy>),
-    /// A reference (`&'a T` or `&'a mut T`)
-    Rptr(Option<Lifetime>, Box<MutTy>),
-    /// A bare function (e.g. `fn(usize) -> bool`)
-    BareFn(Box<BareFnTy>),
-    /// The never type (`!`)
-    Never,
-    /// A tuple (`(A, B, C, D, ...)`)
-    Tup(Vec<Ty>),
-    /// A path (`module::module::...::Type`), optionally
-    /// "qualified", e.g. `<Vec<T> as SomeTrait>::SomeType`.
+ast_enum_of_structs! {
+    /// The different kinds of types recognized by the compiler
+    pub enum Ty {
+        /// A variable-length array (`[T]`)
+        pub Slice(TySlice {
+            pub ty: Box<Ty>,
+        }),
+        /// A fixed length array (`[T; n]`)
+        pub Array(TyArray {
+            pub ty: Box<Ty>,
+            pub amt: ConstExpr,
+        }),
+        /// A raw pointer (`*const T` or `*mut T`)
+        pub Ptr(TyPtr {
+            pub ty: Box<MutTy>,
+        }),
+        /// A reference (`&'a T` or `&'a mut T`)
+        pub Rptr(TyRptr {
+            pub lifetime: Option<Lifetime>,
+            pub ty: Box<MutTy>,
+        }),
+        /// A bare function (e.g. `fn(usize) -> bool`)
+        pub BareFn(TyBareFn {
+            pub ty: Box<BareFnTy>,
+        }),
+        /// The never type (`!`)
+        pub Never(TyNever {}),
+        /// A tuple (`(A, B, C, D, ...)`)
+        pub Tup(TyTup {
+            pub tys: Vec<Ty>,
+        }),
+        /// A path (`module::module::...::Type`), optionally
+        /// "qualified", e.g. `<Vec<T> as SomeTrait>::SomeType`.
+        ///
+        /// Type parameters are stored in the Path itself
+        pub Path(TyPath {
+            pub qself: Option<QSelf>,
+            pub path: Path,
+        }),
+        /// A trait object type `Bound1 + Bound2 + Bound3`
+        /// where `Bound` is a trait or a lifetime.
+        pub TraitObject(TyTraitObject {
+            pub bounds: Vec<TyParamBound>,
+        }),
+        /// An `impl Bound1 + Bound2 + Bound3` type
+        /// where `Bound` is a trait or a lifetime.
+        pub ImplTrait(TyImplTrait {
+            pub bounds: Vec<TyParamBound>,
+        }),
+        /// No-op; kept solely so that we can pretty-print faithfully
+        pub Paren(TyParen {
+            pub ty: Box<Ty>,
+        }),
+        /// TyKind::Infer means the type should be inferred instead of it having been
+        /// specified. This can appear anywhere in a type.
+        pub Infer(TyInfer {}),
+        /// A macro in the type position.
+        pub Mac(Mac),
+    }
+}
+
+ast_struct! {
+    pub struct MutTy {
+        pub ty: Ty,
+        pub mutability: Mutability,
+    }
+}
+
+ast_enum! {
+    #[derive(Copy)]
+    pub enum Mutability {
+        Mutable,
+        Immutable,
+    }
+}
+
+ast_struct! {
+    /// A "Path" is essentially Rust's notion of a name.
     ///
-    /// Type parameters are stored in the Path itself
-    Path(Option<QSelf>, Path),
-    /// A trait object type `Bound1 + Bound2 + Bound3`
-    /// where `Bound` is a trait or a lifetime.
-    TraitObject(Vec<TyParamBound>),
-    /// An `impl Bound1 + Bound2 + Bound3` type
-    /// where `Bound` is a trait or a lifetime.
-    ImplTrait(Vec<TyParamBound>),
-    /// No-op; kept solely so that we can pretty-print faithfully
-    Paren(Box<Ty>),
-    /// TyKind::Infer means the type should be inferred instead of it having been
-    /// specified. This can appear anywhere in a type.
-    Infer,
-    /// A macro in the type position.
-    Mac(Mac),
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct MutTy {
-    pub ty: Ty,
-    pub mutability: Mutability,
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Mutability {
-    Mutable,
-    Immutable,
-}
-
-/// A "Path" is essentially Rust's notion of a name.
-///
-/// It's represented as a sequence of identifiers,
-/// along with a bunch of supporting information.
-///
-/// E.g. `std::cmp::PartialEq`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct Path {
-    /// A `::foo` path, is relative to the crate root rather than current
-    /// module (like paths in an import).
-    pub global: bool,
-    /// The segments in the path: the things separated by `::`.
-    pub segments: Vec<PathSegment>,
+    /// It's represented as a sequence of identifiers,
+    /// along with a bunch of supporting information.
+    ///
+    /// E.g. `std::cmp::PartialEq`
+    pub struct Path {
+        /// A `::foo` path, is relative to the crate root rather than current
+        /// module (like paths in an import).
+        pub global: bool,
+        /// The segments in the path: the things separated by `::`.
+        pub segments: Vec<PathSegment>,
+    }
 }
 
 impl<T> From<T> for Path
@@ -75,19 +103,20 @@
     }
 }
 
-/// A segment of a path: an identifier, an optional lifetime, and a set of types.
-///
-/// E.g. `std`, `String` or `Box<T>`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct PathSegment {
-    /// The identifier portion of this path segment.
-    pub ident: Ident,
-    /// Type/lifetime parameters attached to this path. They come in
-    /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`. Note that
-    /// this is more than just simple syntactic sugar; the use of
-    /// parens affects the region binding rules, so we preserve the
-    /// distinction.
-    pub parameters: PathParameters,
+ast_struct! {
+    /// A segment of a path: an identifier, an optional lifetime, and a set of types.
+    ///
+    /// E.g. `std`, `String` or `Box<T>`
+    pub struct PathSegment {
+        /// The identifier portion of this path segment.
+        pub ident: Ident,
+        /// Type/lifetime parameters attached to this path. They come in
+        /// two flavors: `Path<A,B,C>` and `Path(A,B) -> C`. Note that
+        /// this is more than just simple syntactic sugar; the use of
+        /// parens affects the region binding rules, so we preserve the
+        /// distinction.
+        pub parameters: PathParameters,
+    }
 }
 
 impl<T> From<T> for PathSegment
@@ -101,15 +130,16 @@
     }
 }
 
-/// Parameters of a path segment.
-///
-/// E.g. `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum PathParameters {
-    /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`
-    AngleBracketed(AngleBracketedParameterData),
-    /// The `(A, B)` and `C` in `Foo(A, B) -> C`
-    Parenthesized(ParenthesizedParameterData),
+ast_enum! {
+    /// Parameters of a path segment.
+    ///
+    /// E.g. `<A, B>` as in `Foo<A, B>` or `(A, B)` as in `Foo(A, B)`
+    pub enum PathParameters {
+        /// The `<'a, A, B, C>` in `foo::bar::baz::<'a, A, B, C>`
+        AngleBracketed(AngleBracketedParameterData),
+        /// The `(A, B)` and `C` in `Foo(A, B) -> C`
+        Parenthesized(ParenthesizedParameterData),
+    }
 }
 
 impl PathParameters {
@@ -128,104 +158,118 @@
     }
 }
 
-/// A path like `Foo<'a, T>`
-#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
-pub struct AngleBracketedParameterData {
-    /// The lifetime parameters for this path segment.
-    pub lifetimes: Vec<Lifetime>,
-    /// The type parameters for this path segment, if present.
-    pub types: Vec<Ty>,
-    /// Bindings (equality constraints) on associated types, if present.
+ast_struct! {
+    /// A path like `Foo<'a, T>`
+    #[derive(Default)]
+    pub struct AngleBracketedParameterData {
+        /// The lifetime parameters for this path segment.
+        pub lifetimes: Vec<Lifetime>,
+        /// The type parameters for this path segment, if present.
+        pub types: Vec<Ty>,
+        /// Bindings (equality constraints) on associated types, if present.
+        ///
+        /// E.g., `Foo<A=Bar>`.
+        pub bindings: Vec<TypeBinding>,
+    }
+}
+
+ast_struct! {
+    /// Bind a type to an associated type: `A=Foo`.
+    pub struct TypeBinding {
+        pub ident: Ident,
+        pub ty: Ty,
+    }
+}
+
+
+ast_struct! {
+    /// A path like `Foo(A,B) -> C`
+    pub struct ParenthesizedParameterData {
+        /// `(A, B)`
+        pub inputs: Vec<Ty>,
+        /// `C`
+        pub output: Option<Ty>,
+    }
+}
+
+ast_struct! {
+    pub struct PolyTraitRef {
+        /// The `'a` in `<'a> Foo<&'a T>`
+        pub bound_lifetimes: Vec<LifetimeDef>,
+        /// The `Foo<&'a T>` in `<'a> Foo<&'a T>`
+        pub trait_ref: Path,
+    }
+}
+
+ast_struct! {
+    /// The explicit Self type in a "qualified path". The actual
+    /// path, including the trait and the associated item, is stored
+    /// separately. `position` represents the index of the associated
+    /// item qualified with this Self type.
     ///
-    /// E.g., `Foo<A=Bar>`.
-    pub bindings: Vec<TypeBinding>,
-}
-
-/// Bind a type to an associated type: `A=Foo`.
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct TypeBinding {
-    pub ident: Ident,
-    pub ty: Ty,
-}
-
-/// A path like `Foo(A,B) -> C`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct ParenthesizedParameterData {
-    /// `(A, B)`
-    pub inputs: Vec<Ty>,
-    /// `C`
-    pub output: Option<Ty>,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct PolyTraitRef {
-    /// The `'a` in `<'a> Foo<&'a T>`
-    pub bound_lifetimes: Vec<LifetimeDef>,
-    /// The `Foo<&'a T>` in `<'a> Foo<&'a T>`
-    pub trait_ref: Path,
-}
-
-/// The explicit Self type in a "qualified path". The actual
-/// path, including the trait and the associated item, is stored
-/// separately. `position` represents the index of the associated
-/// item qualified with this Self type.
-///
-/// ```rust,ignore
-/// <Vec<T> as a::b::Trait>::AssociatedItem
-///  ^~~~~     ~~~~~~~~~~~~~~^
-///  ty        position = 3
-///
-/// <Vec<T>>::AssociatedItem
-///  ^~~~~    ^
-///  ty       position = 0
-/// ```
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct QSelf {
-    pub ty: Box<Ty>,
-    pub position: usize,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct BareFnTy {
-    pub unsafety: Unsafety,
-    pub abi: Option<Abi>,
-    pub lifetimes: Vec<LifetimeDef>,
-    pub inputs: Vec<BareFnArg>,
-    pub output: FunctionRetTy,
-    pub variadic: bool,
-}
-
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
-pub enum Unsafety {
-    Unsafe,
-    Normal,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum Abi {
-    Named(String),
-    Rust,
-}
-
-/// An argument in a function type.
-///
-/// E.g. `bar: usize` as in `fn foo(bar: usize)`
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub struct BareFnArg {
-    pub name: Option<Ident>,
-    pub ty: Ty,
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash)]
-pub enum FunctionRetTy {
-    /// Return type is not specified.
+    /// ```rust,ignore
+    /// <Vec<T> as a::b::Trait>::AssociatedItem
+    ///  ^~~~~     ~~~~~~~~~~~~~~^
+    ///  ty        position = 3
     ///
-    /// Functions default to `()` and
-    /// closures default to inference. Span points to where return
-    /// type would be inserted.
-    Default,
-    /// Everything else
-    Ty(Ty),
+    /// <Vec<T>>::AssociatedItem
+    ///  ^~~~~    ^
+    ///  ty       position = 0
+    /// ```
+    pub struct QSelf {
+        pub ty: Box<Ty>,
+        pub position: usize,
+    }
+}
+
+ast_struct! {
+    pub struct BareFnTy {
+        pub unsafety: Unsafety,
+        pub abi: Option<Abi>,
+        pub lifetimes: Vec<LifetimeDef>,
+        pub inputs: Vec<BareFnArg>,
+        pub output: FunctionRetTy,
+        pub variadic: bool,
+    }
+}
+
+ast_enum! {
+    #[derive(Copy)]
+    pub enum Unsafety {
+        Unsafe,
+        Normal,
+    }
+}
+
+ast_enum! {
+    pub enum Abi {
+        Named(String),
+        Rust,
+    }
+}
+
+ast_struct! {
+    /// An argument in a function type.
+    ///
+    /// E.g. `bar: usize` as in `fn foo(bar: usize)`
+    pub struct BareFnArg {
+        pub name: Option<Ident>,
+        pub ty: Ty,
+    }
+}
+
+
+ast_enum! {
+    pub enum FunctionRetTy {
+        /// Return type is not specified.
+        ///
+        /// Functions default to `()` and
+        /// closures default to inference. Span points to where return
+        /// type would be inserted.
+        Default,
+        /// Everything else
+        Ty(Ty),
+    }
 }
 
 #[cfg(feature = "parsing")]
@@ -276,7 +320,7 @@
         punct!("[") >>
         elem: ty >>
         punct!("]") >>
-        (Ty::Slice(Box::new(elem)))
+        (TySlice { ty: Box::new(elem) }.into())
     ));
 
     named!(ty_array -> Ty, do_parse!(
@@ -285,7 +329,7 @@
         punct!(";") >>
         len: array_len >>
         punct!("]") >>
-        (Ty::Array(Box::new(elem), len))
+        (TyArray { ty: Box::new(elem), amt: len }.into())
     ));
 
     #[cfg(not(feature = "full"))]
@@ -309,10 +353,12 @@
             keyword!("mut") => { |_| Mutability::Mutable }
         ) >>
         target: ty >>
-        (Ty::Ptr(Box::new(MutTy {
-            ty: target,
-            mutability: mutability,
-        })))
+        (TyPtr {
+            ty: Box::new(MutTy {
+                ty: target,
+                mutability: mutability,
+            }),
+        }.into())
     ));
 
     named!(ty_rptr -> Ty, do_parse!(
@@ -320,10 +366,13 @@
         life: option!(lifetime) >>
         mutability: mutability >>
         target: ty >>
-        (Ty::Rptr(life, Box::new(MutTy {
-            ty: target,
-            mutability: mutability,
-        })))
+        (TyRptr {
+            lifetime: life,
+            ty: Box::new(MutTy {
+                ty: target,
+                mutability: mutability,
+            }),
+        }.into())
     ));
 
     named!(ty_bare_fn -> Ty, do_parse!(
@@ -346,26 +395,28 @@
             punct!("->"),
             ty
         )) >>
-        (Ty::BareFn(Box::new(BareFnTy {
-            unsafety: unsafety,
-            abi: abi,
-            lifetimes: lifetimes,
-            inputs: inputs,
-            output: match output {
-                Some(ty) => FunctionRetTy::Ty(ty),
-                None => FunctionRetTy::Default,
-            },
-            variadic: variadic.is_some(),
-        })))
+        (TyBareFn {
+            ty: Box::new(BareFnTy {
+                unsafety: unsafety,
+                abi: abi,
+                lifetimes: lifetimes,
+                inputs: inputs,
+                output: match output {
+                    Some(ty) => FunctionRetTy::Ty(ty),
+                    None => FunctionRetTy::Default,
+                },
+                variadic: variadic.is_some(),
+            }),
+        }.into())
     ));
 
-    named!(ty_never -> Ty, map!(punct!("!"), |_| Ty::Never));
+    named!(ty_never -> Ty, map!(punct!("!"), |_| TyNever {}.into()));
 
     named!(ty_tup -> Ty, do_parse!(
         punct!("(") >>
         elems: terminated_list!(punct!(","), ty) >>
         punct!(")") >>
-        (Ty::Tup(elems))
+        (TyTup { tys: elems }.into())
     ));
 
     named!(ty_path -> Ty, do_parse!(
@@ -381,7 +432,7 @@
                 path.segments.last_mut().unwrap().parameters = parenthesized;
             }
             if bounds.is_empty() {
-                Ty::Path(qself, path)
+                TyPath { qself: qself, path: path }.into()
             } else {
                 let path = TyParamBound::Trait(
                     PolyTraitRef {
@@ -391,7 +442,7 @@
                     TraitBoundModifier::None,
                 );
                 let bounds = Some(path).into_iter().chain(bounds).collect();
-                Ty::TraitObject(bounds)
+                TyTraitObject { bounds: bounds }.into()
             }
         })
     ));
@@ -447,20 +498,20 @@
 
     named!(ty_poly_trait_ref -> Ty, map!(
         separated_nonempty_list!(punct!("+"), ty_param_bound),
-        Ty::TraitObject
+        |x| TyTraitObject { bounds: x }.into()
     ));
 
     named!(ty_impl_trait -> Ty, do_parse!(
         keyword!("impl") >>
         elem: separated_nonempty_list!(punct!("+"), ty_param_bound) >>
-        (Ty::ImplTrait(elem))
+        (TyImplTrait { bounds: elem }.into())
     ));
 
     named!(ty_paren -> Ty, do_parse!(
         punct!("(") >>
         elem: ty >>
         punct!(")") >>
-        (Ty::Paren(Box::new(elem)))
+        (TyParen { ty: Box::new(elem) }.into())
     ));
 
     named!(pub mutability -> Mutability, alt!(
@@ -604,90 +655,122 @@
     use super::*;
     use quote::{Tokens, ToTokens};
 
-    impl ToTokens for Ty {
+    impl ToTokens for TySlice {
         fn to_tokens(&self, tokens: &mut Tokens) {
-            match *self {
-                Ty::Slice(ref inner) => {
-                    tokens.append("[");
-                    inner.to_tokens(tokens);
-                    tokens.append("]");
-                }
-                Ty::Array(ref inner, ref len) => {
-                    tokens.append("[");
-                    inner.to_tokens(tokens);
-                    tokens.append(";");
-                    len.to_tokens(tokens);
-                    tokens.append("]");
-                }
-                Ty::Ptr(ref target) => {
-                    tokens.append("*");
-                    match target.mutability {
-                        Mutability::Mutable => tokens.append("mut"),
-                        Mutability::Immutable => tokens.append("const"),
-                    }
-                    target.ty.to_tokens(tokens);
-                }
-                Ty::Rptr(ref lifetime, ref target) => {
-                    tokens.append("&");
-                    lifetime.to_tokens(tokens);
-                    target.mutability.to_tokens(tokens);
-                    target.ty.to_tokens(tokens);
-                }
-                Ty::BareFn(ref func) => {
-                    func.to_tokens(tokens);
-                }
-                Ty::Never => {
-                    tokens.append("!");
-                }
-                Ty::Tup(ref elems) => {
-                    tokens.append("(");
-                    tokens.append_separated(elems, ",");
-                    if elems.len() == 1 {
-                        tokens.append(",");
-                    }
-                    tokens.append(")");
-                }
-                Ty::Path(None, ref path) => {
-                    path.to_tokens(tokens);
-                }
-                Ty::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);
-                    }
-                }
-                Ty::TraitObject(ref bounds) => {
-                    tokens.append_separated(bounds, "+");
-                }
-                Ty::ImplTrait(ref bounds) => {
-                    tokens.append("impl");
-                    tokens.append_separated(bounds, "+");
-                }
-                Ty::Paren(ref inner) => {
-                    tokens.append("(");
-                    inner.to_tokens(tokens);
-                    tokens.append(")");
-                }
-                Ty::Infer => {
-                    tokens.append("_");
-                }
-                Ty::Mac(ref mac) => mac.to_tokens(tokens),
+            tokens.append("[");
+            self.ty.to_tokens(tokens);
+            tokens.append("]");
+        }
+    }
+
+    impl ToTokens for TyArray {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("[");
+            self.ty.to_tokens(tokens);
+            tokens.append(";");
+            self.amt.to_tokens(tokens);
+            tokens.append("]");
+        }
+    }
+
+    impl ToTokens for TyPtr {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("*");
+            match self.ty.mutability {
+                Mutability::Mutable => tokens.append("mut"),
+                Mutability::Immutable => tokens.append("const"),
             }
+            self.ty.ty.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for TyRptr {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("&");
+            self.lifetime.to_tokens(tokens);
+            match self.ty.mutability {
+                Mutability::Mutable => tokens.append("mut"),
+                Mutability::Immutable => {}
+            }
+            self.ty.ty.to_tokens(tokens);
+        }
+    }
+
+    impl ToTokens for TyBareFn {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            self.ty.to_tokens(tokens)
+        }
+    }
+
+    impl ToTokens for TyNever {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("!");
+        }
+    }
+
+    impl ToTokens for TyTup {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("(");
+            tokens.append_separated(&self.tys, ",");
+            if self.tys.len() == 1 {
+                tokens.append(",");
+            }
+            tokens.append(")");
+        }
+    }
+
+    impl ToTokens for TyPath {
+        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 TyTraitObject {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append_separated(&self.bounds, "+");
+        }
+    }
+
+    impl ToTokens for TyImplTrait {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("impl");
+            tokens.append_separated(&self.bounds, "+");
+        }
+    }
+
+    impl ToTokens for TyParen {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("(");
+            self.ty.to_tokens(tokens);
+            tokens.append(")");
+        }
+    }
+
+    impl ToTokens for TyInfer {
+        fn to_tokens(&self, tokens: &mut Tokens) {
+            tokens.append("_");
         }
     }
 
diff --git a/src/visit.rs b/src/visit.rs
index b0a0057..c2b6d53 100644
--- a/src/visit.rs
+++ b/src/visit.rs
@@ -180,38 +180,40 @@
 }
 
 pub fn walk_ty<V: Visitor>(visitor: &mut V, ty: &Ty) {
+    use ty::*;
+
     match *ty {
-        Ty::Slice(ref inner) |
-        Ty::Paren(ref inner) => visitor.visit_ty(inner),
-        Ty::Ptr(ref mutable_type) => visitor.visit_ty(&mutable_type.ty),
-        Ty::Rptr(ref opt_lifetime, ref mutable_type) => {
-            walk_list!(visitor, visit_lifetime, opt_lifetime);
-            visitor.visit_ty(&mutable_type.ty)
+        Ty::Slice(TySlice { ref ty }) |
+        Ty::Paren(TyParen { ref ty }) => visitor.visit_ty(ty),
+        Ty::Ptr(TyPtr { ref ty }) => visitor.visit_ty(&ty.ty),
+        Ty::Rptr(TyRptr { ref lifetime, ref ty }) => {
+            walk_list!(visitor, visit_lifetime, lifetime);
+            visitor.visit_ty(&ty.ty)
         }
-        Ty::Never | Ty::Infer => {}
-        Ty::Tup(ref tuple_element_types) => {
-            walk_list!(visitor, visit_ty, tuple_element_types);
+        Ty::Never(_) | Ty::Infer(_) => {}
+        Ty::Tup(TyTup { ref tys }) => {
+            walk_list!(visitor, visit_ty, tys);
         }
-        Ty::BareFn(ref bare_fn) => {
-            walk_list!(visitor, visit_lifetime_def, &bare_fn.lifetimes);
-            for argument in &bare_fn.inputs {
+        Ty::BareFn(TyBareFn { ref ty }) => {
+            walk_list!(visitor, visit_lifetime_def, &ty.lifetimes);
+            for argument in &ty.inputs {
                 walk_opt_ident(visitor, &argument.name);
                 visitor.visit_ty(&argument.ty)
             }
-            visitor.visit_fn_ret_ty(&bare_fn.output)
+            visitor.visit_fn_ret_ty(&ty.output)
         }
-        Ty::Path(ref maybe_qself, ref path) => {
-            if let Some(ref qself) = *maybe_qself {
+        Ty::Path(TyPath { ref qself, ref path }) => {
+            if let Some(ref qself) = *qself {
                 visitor.visit_ty(&qself.ty);
             }
             visitor.visit_path(path);
         }
-        Ty::Array(ref inner, ref len) => {
-            visitor.visit_ty(inner);
-            visitor.visit_const_expr(len);
+        Ty::Array(TyArray { ref ty, ref amt }) => {
+            visitor.visit_ty(ty);
+            visitor.visit_const_expr(amt);
         }
-        Ty::TraitObject(ref bounds) |
-        Ty::ImplTrait(ref bounds) => {
+        Ty::TraitObject(TyTraitObject { ref bounds })  |
+        Ty::ImplTrait(TyImplTrait { ref bounds }) => {
             walk_list!(visitor, visit_ty_param_bound, bounds);
         }
         Ty::Mac(ref mac) => {
@@ -311,36 +313,37 @@
 }
 
 pub fn walk_const_expr<V: Visitor>(visitor: &mut V, len: &ConstExpr) {
+    use constant::*;
+    use constant::ConstExpr::*;
+
     match *len {
-        ConstExpr::Call(ref function, ref args) => {
-            visitor.visit_const_expr(function);
+        Call(ConstCall { ref func, ref args }) => {
+            visitor.visit_const_expr(func);
             walk_list!(visitor, visit_const_expr, args);
         }
-        ConstExpr::Binary(_op, ref left, ref right) => {
+        Binary(ConstBinary { ref left, ref right, .. }) => {
             visitor.visit_const_expr(left);
             visitor.visit_const_expr(right);
         }
-        ConstExpr::Unary(_op, ref v) => {
-            visitor.visit_const_expr(v);
-        }
-        ConstExpr::Lit(ref lit) => {
+        Lit(ref lit) => {
             visitor.visit_lit(lit);
         }
-        ConstExpr::Cast(ref expr, ref ty) => {
+        Cast(ConstCast { ref expr, ref ty }) => {
             visitor.visit_const_expr(expr);
             visitor.visit_ty(ty);
         }
-        ConstExpr::Path(ref path) => {
+        Path(ref path) => {
             visitor.visit_path(path);
         }
-        ConstExpr::Index(ref expr, ref index) => {
+        Index(ConstIndex { ref expr, ref index }) => {
             visitor.visit_const_expr(expr);
             visitor.visit_const_expr(index);
         }
-        ConstExpr::Paren(ref expr) => {
+        Unary(ConstUnary { ref expr, .. }) |
+        Paren(ConstParen { ref expr }) => {
             visitor.visit_const_expr(expr);
         }
-        ConstExpr::Other(ref other) => {
+        Other(ref other) => {
             #[cfg(feature = "full")]
             fn walk_other<V: Visitor>(visitor: &mut V, other: &Expr) {
                 visitor.visit_expr(other);
@@ -364,59 +367,72 @@
 
 #[cfg(feature = "full")]
 pub fn walk_item<V: Visitor>(visitor: &mut V, item: &Item) {
+    use item::*;
+
     visitor.visit_ident(&item.ident);
     walk_list!(visitor, visit_attribute, &item.attrs);
     match item.node {
-        ItemKind::ExternCrate(ref ident) => {
-            walk_opt_ident(visitor, ident);
+        ItemKind::ExternCrate(ItemExternCrate { ref original }) => {
+            walk_opt_ident(visitor, original);
         }
-        ItemKind::Use(ref view_path) => {
-            visitor.visit_view_path(view_path);
+        ItemKind::Use(ItemUse { ref path }) => {
+            visitor.visit_view_path(path);
         }
-        ItemKind::Static(ref ty, _, ref expr) |
-        ItemKind::Const(ref ty, ref expr) => {
+        ItemKind::Static(ItemStatic { ref ty, ref expr, .. }) |
+        ItemKind::Const(ItemConst { ref ty, ref expr }) => {
             visitor.visit_ty(ty);
             visitor.visit_expr(expr);
         }
-        ItemKind::Fn(ref decl, _, _, _, ref generics, ref body) => {
+        ItemKind::Fn(ItemFn { ref decl, ref generics, ref block, ..  }) => {
             visitor.visit_fn_decl(decl);
             visitor.visit_generics(generics);
-            walk_list!(visitor, visit_stmt, &body.stmts);
+            walk_list!(visitor, visit_stmt, &block.stmts);
         }
-        ItemKind::Mod(ref maybe_items) => {
-            if let Some(ref items) = *maybe_items {
+        ItemKind::Mod(ItemMod { ref items }) => {
+            if let Some(ref items) = *items {
                 walk_list!(visitor, visit_item, items);
             }
         }
-        ItemKind::ForeignMod(ref foreign_mod) => {
-            walk_list!(visitor, visit_foreign_item, &foreign_mod.items);
+        ItemKind::ForeignMod(ItemForeignMod { ref items, .. }) => {
+            walk_list!(visitor, visit_foreign_item, items);
         }
-        ItemKind::Ty(ref ty, ref generics) => {
+        ItemKind::Ty(ItemTy { ref ty, ref generics }) => {
             visitor.visit_ty(ty);
             visitor.visit_generics(generics);
         }
-        ItemKind::Enum(ref variant, ref generics) => {
-            walk_list!(visitor, visit_variant, variant, generics);
+        ItemKind::Enum(ItemEnum { ref variants, ref generics }) => {
+            walk_list!(visitor, visit_variant, variants, generics);
         }
-        ItemKind::Struct(ref variant_data, ref generics) |
-        ItemKind::Union(ref variant_data, ref generics) => {
-            visitor.visit_variant_data(variant_data, &item.ident, generics);
+        ItemKind::Struct(ItemStruct { ref data, ref generics }) |
+        ItemKind::Union(ItemUnion { ref data, ref generics }) => {
+            visitor.visit_variant_data(data, &item.ident, generics);
         }
-        ItemKind::Trait(_, ref generics, ref bounds, ref trait_items) => {
+        ItemKind::Trait(ItemTrait {
+            ref generics,
+            ref supertraits,
+            ref items,
+            ..
+        }) => {
             visitor.visit_generics(generics);
-            walk_list!(visitor, visit_ty_param_bound, bounds);
-            walk_list!(visitor, visit_trait_item, trait_items);
+            walk_list!(visitor, visit_ty_param_bound, supertraits);
+            walk_list!(visitor, visit_trait_item, items);
         }
-        ItemKind::DefaultImpl(_, ref path) => {
+        ItemKind::DefaultImpl(ItemDefaultImpl { ref path, .. }) => {
             visitor.visit_path(path);
         }
-        ItemKind::Impl(_, _, ref generics, ref maybe_path, ref ty, ref impl_items) => {
+        ItemKind::Impl(ItemImpl {
+            ref generics,
+            ref trait_,
+            ref self_ty,
+            ref items,
+            ..
+        }) => {
             visitor.visit_generics(generics);
-            if let Some(ref path) = *maybe_path {
+            if let Some(ref path) = *trait_ {
                 visitor.visit_path(path);
             }
-            visitor.visit_ty(ty);
-            walk_list!(visitor, visit_impl_item, impl_items);
+            visitor.visit_ty(self_ty);
+            walk_list!(visitor, visit_impl_item, items);
         }
         ItemKind::Mac(ref mac) => visitor.visit_mac(mac),
     }
@@ -425,73 +441,68 @@
 #[cfg(feature = "full")]
 #[cfg_attr(feature = "cargo-clippy", allow(cyclomatic_complexity))]
 pub fn walk_expr<V: Visitor>(visitor: &mut V, expr: &Expr) {
+    use expr::*;
+    use expr::ExprKind::*;
+
     walk_list!(visitor, visit_attribute, &expr.attrs);
     match expr.node {
-        ExprKind::InPlace(ref place, ref value) => {
+        InPlace(ExprInPlace { ref place, ref value }) => {
             visitor.visit_expr(place);
             visitor.visit_expr(value);
         }
-        ExprKind::Call(ref callee, ref args) => {
-            visitor.visit_expr(callee);
+        Call(ExprCall { ref func, ref args }) => {
+            visitor.visit_expr(func);
             walk_list!(visitor, visit_expr, args);
         }
-        ExprKind::MethodCall(ref name, ref ty_args, ref args) => {
-            visitor.visit_ident(name);
-            walk_list!(visitor, visit_ty, ty_args);
+        MethodCall(ExprMethodCall { ref method, ref typarams, ref args }) => {
+            visitor.visit_ident(method);
+            walk_list!(visitor, visit_ty, typarams);
             walk_list!(visitor, visit_expr, args);
         }
-        ExprKind::Array(ref exprs) |
-        ExprKind::Tup(ref exprs) => {
+        Array(ExprArray { ref exprs }) |
+        Tup(ExprTup { args: ref exprs }) => {
             walk_list!(visitor, visit_expr, exprs);
         }
-        ExprKind::Unary(_, ref operand) => {
-            visitor.visit_expr(operand);
-        }
-        ExprKind::Lit(ref lit) => {
+        Lit(ref lit) => {
             visitor.visit_lit(lit);
         }
-        ExprKind::Cast(ref expr, ref ty) |
-        ExprKind::Type(ref expr, ref ty) => {
+        Cast(ExprCast { ref expr, ref ty }) |
+        Type(ExprType { ref expr, ref ty }) => {
             visitor.visit_expr(expr);
             visitor.visit_ty(ty);
         }
-        ExprKind::If(ref cond, ref cons, ref maybe_alt) => {
+        If(ExprIf { ref cond, ref if_true, ref if_false }) => {
             visitor.visit_expr(cond);
-            walk_list!(visitor, visit_stmt, &cons.stmts);
-            if let Some(ref alt) = *maybe_alt {
+            walk_list!(visitor, visit_stmt, &if_true.stmts);
+            if let Some(ref alt) = *if_false {
                 visitor.visit_expr(alt);
             }
         }
-        ExprKind::IfLet(ref pat, ref cond, ref cons, ref maybe_alt) => {
+        IfLet(ExprIfLet { ref pat, ref expr, ref if_true, ref if_false }) => {
             visitor.visit_pat(pat);
-            visitor.visit_expr(cond);
-            walk_list!(visitor, visit_stmt, &cons.stmts);
-            if let Some(ref alt) = *maybe_alt {
+            visitor.visit_expr(expr);
+            walk_list!(visitor, visit_stmt, &if_true.stmts);
+            if let Some(ref alt) = *if_false {
                 visitor.visit_expr(alt);
             }
         }
-        ExprKind::While(ref cond, ref body, ref label) => {
+        While(ExprWhile { ref cond, ref body, ref label }) => {
             visitor.visit_expr(cond);
             walk_list!(visitor, visit_stmt, &body.stmts);
             walk_opt_ident(visitor, label);
         }
-        ExprKind::WhileLet(ref pat, ref cond, ref body, ref label) => {
-            visitor.visit_pat(pat);
-            visitor.visit_expr(cond);
-            walk_list!(visitor, visit_stmt, &body.stmts);
-            walk_opt_ident(visitor, label);
-        }
-        ExprKind::ForLoop(ref pat, ref expr, ref body, ref label) => {
+        WhileLet(ExprWhileLet { ref pat, ref expr, ref body, ref label }) |
+        ForLoop(ExprForLoop { ref pat, ref expr, ref body, ref label }) => {
             visitor.visit_pat(pat);
             visitor.visit_expr(expr);
             walk_list!(visitor, visit_stmt, &body.stmts);
             walk_opt_ident(visitor, label);
         }
-        ExprKind::Loop(ref body, ref label) => {
+        Loop(ExprLoop { ref body, ref label }) => {
             walk_list!(visitor, visit_stmt, &body.stmts);
             walk_opt_ident(visitor, label);
         }
-        ExprKind::Match(ref expr, ref arms) => {
+        Match(ExprMatch { ref expr, ref arms }) => {
             visitor.visit_expr(expr);
             for &Arm { ref attrs, ref pats, ref guard, ref body } in arms {
                 walk_list!(visitor, visit_attribute, attrs);
@@ -502,82 +513,79 @@
                 visitor.visit_expr(body);
             }
         }
-        ExprKind::Catch(ref body) => {
-            walk_list!(visitor, visit_stmt, &body.stmts);
-        }
-        ExprKind::Closure(_, ref decl, ref expr) => {
+        Closure(ExprClosure { ref decl, ref body, .. }) => {
             visitor.visit_fn_decl(decl);
-            visitor.visit_expr(expr);
+            visitor.visit_expr(body);
         }
-        ExprKind::Block(_, ref block) => {
+        Catch(ExprCatch { ref block }) |
+        Block(ExprBlock { ref block, .. }) => {
             walk_list!(visitor, visit_stmt, &block.stmts);
         }
-        ExprKind::Binary(_, ref lhs, ref rhs) |
-        ExprKind::Assign(ref lhs, ref rhs) |
-        ExprKind::AssignOp(_, ref lhs, ref rhs) => {
-            visitor.visit_expr(lhs);
-            visitor.visit_expr(rhs);
+        Binary(ExprBinary { ref left, ref right, .. }) |
+        Assign(ExprAssign { ref left, ref right }) |
+        AssignOp(ExprAssignOp { ref left, ref right, .. }) => {
+            visitor.visit_expr(left);
+            visitor.visit_expr(right);
         }
-        ExprKind::Field(ref obj, ref field) => {
-            visitor.visit_expr(obj);
+        Field(ExprField { ref expr, ref field }) => {
+            visitor.visit_expr(expr);
             visitor.visit_ident(field);
         }
-        ExprKind::TupField(ref obj, _) => {
-            visitor.visit_expr(obj);
+        Index(ExprIndex { ref expr, ref index }) => {
+            visitor.visit_expr(expr);
+            visitor.visit_expr(index);
         }
-        ExprKind::Index(ref obj, ref idx) => {
-            visitor.visit_expr(obj);
-            visitor.visit_expr(idx);
-        }
-        ExprKind::Range(ref maybe_start, ref maybe_end, _) => {
-            if let Some(ref start) = *maybe_start {
+        Range(ExprRange { ref from, ref to, .. }) => {
+            if let Some(ref start) = *from {
                 visitor.visit_expr(start);
             }
-            if let Some(ref end) = *maybe_end {
+            if let Some(ref end) = *to {
                 visitor.visit_expr(end);
             }
         }
-        ExprKind::Path(ref maybe_qself, ref path) => {
-            if let Some(ref qself) = *maybe_qself {
+        Path(ExprPath { ref qself, ref path }) => {
+            if let Some(ref qself) = *qself {
                 visitor.visit_ty(&qself.ty);
             }
             visitor.visit_path(path);
         }
-        ExprKind::Break(ref maybe_label, ref maybe_expr) => {
-            walk_opt_ident(visitor, maybe_label);
-            if let Some(ref expr) = *maybe_expr {
+        Break(ExprBreak { ref label, ref expr }) => {
+            walk_opt_ident(visitor, label);
+            if let Some(ref expr) = *expr {
                 visitor.visit_expr(expr);
             }
         }
-        ExprKind::Continue(ref maybe_label) => {
-            walk_opt_ident(visitor, maybe_label);
+        Continue(ExprContinue { ref label }) => {
+            walk_opt_ident(visitor, label);
         }
-        ExprKind::Ret(ref maybe_expr) => {
-            if let Some(ref expr) = *maybe_expr {
+        Ret(ExprRet { ref expr }) => {
+            if let Some(ref expr) = *expr {
                 visitor.visit_expr(expr);
             }
         }
-        ExprKind::Mac(ref mac) => {
+        Mac(ref mac) => {
             visitor.visit_mac(mac);
         }
-        ExprKind::Struct(ref path, ref fields, ref maybe_base) => {
+        Struct(ExprStruct { ref path, ref fields, ref rest }) => {
             visitor.visit_path(path);
             for &FieldValue { ref ident, ref expr, .. } in fields {
                 visitor.visit_ident(ident);
                 visitor.visit_expr(expr);
             }
-            if let Some(ref base) = *maybe_base {
+            if let Some(ref base) = *rest {
                 visitor.visit_expr(base);
             }
         }
-        ExprKind::Repeat(ref value, ref times) => {
-            visitor.visit_expr(value);
-            visitor.visit_expr(times);
+        Repeat(ExprRepeat { ref expr, ref amt }) => {
+            visitor.visit_expr(expr);
+            visitor.visit_expr(amt);
         }
-        ExprKind::Box(ref expr) |
-        ExprKind::AddrOf(_, ref expr) |
-        ExprKind::Paren(ref expr) |
-        ExprKind::Try(ref expr) => {
+        TupField(ExprTupField { ref expr, .. }) |
+        Unary(ExprUnary { ref expr, .. }) |
+        Box(ExprBox { ref expr }) |
+        AddrOf(ExprAddrOf { ref expr, .. }) |
+        Paren(ExprParen { ref expr }) |
+        Try(ExprTry { ref expr }) => {
             visitor.visit_expr(expr);
         }
     }
@@ -585,14 +593,16 @@
 
 #[cfg(feature = "full")]
 pub fn walk_foreign_item<V: Visitor>(visitor: &mut V, foreign_item: &ForeignItem) {
+    use item::*;
+
     visitor.visit_ident(&foreign_item.ident);
     walk_list!(visitor, visit_attribute, &foreign_item.attrs);
     match foreign_item.node {
-        ForeignItemKind::Fn(ref decl, ref generics) => {
+        ForeignItemKind::Fn(ForeignItemFn { ref decl, ref generics }) => {
             visitor.visit_fn_decl(decl);
             visitor.visit_generics(generics);
         }
-        ForeignItemKind::Static(ref ty, _) => {
+        ForeignItemKind::Static(ForeignItemStatic { ref ty, .. }) => {
             visitor.visit_ty(ty);
         }
     }
@@ -654,11 +664,13 @@
 
 #[cfg(feature = "full")]
 pub fn walk_fn_decl<V: Visitor>(visitor: &mut V, fn_decl: &FnDecl) {
+    use item::*;
+
     for input in &fn_decl.inputs {
         match *input {
-            FnArg::SelfRef(_, _) |
+            FnArg::SelfRef(_) |
             FnArg::SelfValue(_) => {}
-            FnArg::Captured(ref pat, ref ty) => {
+            FnArg::Captured(ArgCaptured { ref pat, ref ty }) => {
                 visitor.visit_pat(pat);
                 visitor.visit_ty(ty);
             }
@@ -672,24 +684,26 @@
 
 #[cfg(feature = "full")]
 pub fn walk_trait_item<V: Visitor>(visitor: &mut V, trait_item: &TraitItem) {
+    use item::*;
+
     visitor.visit_ident(&trait_item.ident);
     walk_list!(visitor, visit_attribute, &trait_item.attrs);
     match trait_item.node {
-        TraitItemKind::Const(ref ty, ref maybe_expr) => {
+        TraitItemKind::Const(TraitItemConst { ref ty, ref default }) => {
             visitor.visit_ty(ty);
-            if let Some(ref expr) = *maybe_expr {
+            if let Some(ref expr) = *default {
                 visitor.visit_expr(expr);
             }
         }
-        TraitItemKind::Method(ref method_sig, ref maybe_block) => {
-            visitor.visit_method_sig(method_sig);
-            if let Some(ref block) = *maybe_block {
+        TraitItemKind::Method(TraitItemMethod { ref sig, ref default }) => {
+            visitor.visit_method_sig(sig);
+            if let Some(ref block) = *default {
                 walk_list!(visitor, visit_stmt, &block.stmts);
             }
         }
-        TraitItemKind::Type(ref bounds, ref maybe_ty) => {
+        TraitItemKind::Type(TraitItemType { ref bounds, ref default }) => {
             walk_list!(visitor, visit_ty_param_bound, bounds);
-            if let Some(ref ty) = *maybe_ty {
+            if let Some(ref ty) = *default {
                 visitor.visit_ty(ty);
             }
         }
@@ -701,18 +715,20 @@
 
 #[cfg(feature = "full")]
 pub fn walk_impl_item<V: Visitor>(visitor: &mut V, impl_item: &ImplItem) {
+    use item::*;
+
     visitor.visit_ident(&impl_item.ident);
     walk_list!(visitor, visit_attribute, &impl_item.attrs);
     match impl_item.node {
-        ImplItemKind::Const(ref ty, ref expr) => {
+        ImplItemKind::Const(ImplItemConst { ref ty, ref expr }) => {
             visitor.visit_ty(ty);
             visitor.visit_expr(expr);
         }
-        ImplItemKind::Method(ref method_sig, ref block) => {
-            visitor.visit_method_sig(method_sig);
+        ImplItemKind::Method(ImplItemMethod { ref sig, ref block }) => {
+            visitor.visit_method_sig(sig);
             walk_list!(visitor, visit_stmt, &block.stmts);
         }
-        ImplItemKind::Type(ref ty) => {
+        ImplItemKind::Type(ImplItemType { ref ty }) => {
             visitor.visit_ty(ty);
         }
         ImplItemKind::Macro(ref mac) => {
@@ -762,15 +778,16 @@
 
 #[cfg(feature = "full")]
 pub fn walk_view_path<V: Visitor>(visitor: &mut V, view_path: &ViewPath) {
+    use item::*;
     match *view_path {
-        ViewPath::Simple(ref path, ref maybe_ident) => {
+        ViewPath::Simple(PathSimple { ref path, ref rename }) => {
             visitor.visit_path(path);
-            walk_opt_ident(visitor, maybe_ident);
+            walk_opt_ident(visitor, rename);
         }
-        ViewPath::Glob(ref path) => {
+        ViewPath::Glob(PathGlob { ref path }) => {
             visitor.visit_path(path);
         }
-        ViewPath::List(ref path, ref items) => {
+        ViewPath::List(PathList { ref path, ref items }) => {
             visitor.visit_path(path);
             for &PathListItem { ref name, ref rename } in items {
                 visitor.visit_ident(name);
diff --git a/synom/src/helper.rs b/synom/src/helper.rs
index a488359..5bae69b 100644
--- a/synom/src/helper.rs
+++ b/synom/src/helper.rs
@@ -227,14 +227,17 @@
 /// extern crate syn;
 /// #[macro_use] extern crate synom;
 ///
-/// use syn::{Expr, ExprKind};
+/// use syn::{Expr, ExprCall};
 /// use syn::parse::expr;
 ///
 /// named!(expr_with_arrow_call -> Expr, do_parse!(
 ///     mut e: expr >>
 ///     many0!(tap!(arg: tuple!(punct!("=>"), expr) => {
 ///         e = Expr {
-///             node: ExprKind::Call(Box::new(e), vec![arg.1]),
+///             node: ExprCall {
+///                 func: Box::new(e),
+///                 args: vec![arg.1],
+///             }.into(),
 ///             attrs: Vec::new(),
 ///         };
 ///     })) >>
diff --git a/tests/test_expr.rs b/tests/test_expr.rs
index dca8603..1fe0180 100644
--- a/tests/test_expr.rs
+++ b/tests/test_expr.rs
@@ -90,45 +90,45 @@
 
     assert_let!(ItemKind::Struct(..) = actual.items[0].node);
 
-    assert_let!(Item { node: ItemKind::Fn(_, _, _, _, _, ref body), .. } = actual.items[1]; {
-        assert_let!(Stmt::Local(ref local) = body.stmts[0]; {
+    assert_let!(Item { node: ItemKind::Fn(ItemFn { ref block, .. }), .. } = actual.items[1]; {
+        assert_let!(Stmt::Local(ref local) = block.stmts[0]; {
             assert_let!(Local { init: Some(ref init_expr), .. } = **local; {
                 assert_let!(Expr { node: ExprKind::Catch(..), .. } = **init_expr);
             });
         });
 
-        assert_let!(Stmt::Local(ref local) = body.stmts[2]; {
+        assert_let!(Stmt::Local(ref local) = block.stmts[2]; {
             assert_let!(Pat::Ident(BindingMode::ByValue(Mutability::Mutable), ref ident, None) = *local.pat; {
                 assert_eq!(ident, "catch");
             });
         });
 
-        assert_let!(Stmt::Expr(ref expr) = body.stmts[3]; {
-            assert_let!(Expr { node: ExprKind::While(ref loop_expr, _, None), .. } = **expr; {
-                assert_let!(Expr { node: ExprKind::Path(None, ref loop_var), .. } = **loop_expr; {
-                    assert_eq!(*loop_var, "catch".into());
+        assert_let!(Stmt::Expr(ref expr) = block.stmts[3]; {
+            assert_let!(Expr { node: ExprKind::While(ExprWhile { ref cond, .. }), .. } = **expr; {
+                assert_let!(Expr { node: ExprKind::Path(ExprPath { qself: None, ref path }), .. } = **cond; {
+                    assert_eq!(*path, "catch".into());
                 });
             });
         });
 
-        assert_let!(Stmt::Semi(ref expr) = body.stmts[5]; {
-            assert_let!(Expr { node: ExprKind::Assign(ref left, ref right), .. } = **expr; {
-                assert_let!(Expr { node: ExprKind::Path(None, ref loop_var), .. } = **left; {
-                    assert_eq!(*loop_var, "catch".into());
+        assert_let!(Stmt::Semi(ref expr) = block.stmts[5]; {
+            assert_let!(Expr { node: ExprKind::Assign(ExprAssign { ref left, ref right }), .. } = **expr; {
+                assert_let!(Expr { node: ExprKind::Path(ExprPath { qself: None, ref path }), .. } = **left; {
+                    assert_eq!(*path, "catch".into());
                 });
 
-                assert_let!(Expr { node: ExprKind::If(ref if_expr, _, _), .. } = **right; {
-                    assert_let!(Expr { node: ExprKind::Path(None, ref if_var), .. } = **if_expr; {
-                        assert_eq!(*if_var, "catch".into());
+                assert_let!(Expr { node: ExprKind::If(ExprIf { ref cond, .. }), .. } = **right; {
+                    assert_let!(Expr { node: ExprKind::Path(ExprPath { qself: None, ref path }), .. } = **cond; {
+                        assert_eq!(*path, "catch".into());
                     });
                 });
             });
         });
 
-        assert_let!(Stmt::Semi(ref expr) = body.stmts[7]; {
-            assert_let!(Expr { node: ExprKind::Match(ref match_expr, _), .. } = **expr; {
-                assert_let!(Expr { node: ExprKind::Path(None, ref match_var), .. } = **match_expr; {
-                    assert_eq!(*match_var, "catch".into());
+        assert_let!(Stmt::Semi(ref expr) = block.stmts[7]; {
+            assert_let!(Expr { node: ExprKind::Match(ExprMatch { ref expr, .. }), .. } = **expr; {
+                assert_let!(Expr { node: ExprKind::Path(ExprPath { qself: None, ref path }), .. } = **expr; {
+                    assert_eq!(*path, "catch".into());
                 });
             });
         });
diff --git a/tests/test_generics.rs b/tests/test_generics.rs
index 800daa7..08886bb 100644
--- a/tests/test_generics.rs
+++ b/tests/test_generics.rs
@@ -27,13 +27,15 @@
                                         }],
                             ident: Ident::new("T"),
                             bounds: vec![TyParamBound::Region(Lifetime::new("'a"))],
-                            default: Some(Ty::Tup(Vec::new())),
+                            default: Some(TyTup { tys: Vec::new() }.into()),
                         }],
         where_clause: WhereClause {
             predicates: vec![WherePredicate::BoundPredicate(WhereBoundPredicate {
                                                                 bound_lifetimes: Vec::new(),
-                                                                bounded_ty:
-                                                                    Ty::Path(None, "T".into()),
+                                                                bounded_ty: TyPath {
+                                                                    qself: None,
+                                                                    path: "T".into(),
+                                                                }.into(),
                                                                 bounds: vec![
                         TyParamBound::Trait(
                             PolyTraitRef {
diff --git a/tests/test_macro_input.rs b/tests/test_macro_input.rs
index d45ead3..4e6bbd8 100644
--- a/tests/test_macro_input.rs
+++ b/tests/test_macro_input.rs
@@ -1,4 +1,4 @@
-//! Test the now-deprecated `parse_macro_input` function. 
+//! Test the now-deprecated `parse_macro_input` function.
 //!
 //! Deprecation warnings are suppressed to keep the output clean.
 #![allow(deprecated)]
@@ -47,43 +47,56 @@
             is_sugared_doc: false,
         }],
         generics: Generics::default(),
-        body: Body::Struct(VariantData::Struct(vec![Field {
-                                                        ident: Some("ident".into()),
-                                                        vis: Visibility::Public,
-                                                        attrs: Vec::new(),
-                                                        ty: Ty::Path(None, "Ident".into()),
-                                                    },
-                                                    Field {
-                                                        ident: Some("attrs".into()),
-                                                        vis: Visibility::Public,
-                                                        attrs: Vec::new(),
-                                                        ty: Ty::Path(None,
-                                                                     Path {
-                                                                         global: false,
-                                                                         segments: vec![
-                        PathSegment {
-                            ident: "Vec".into(),
-                            parameters: PathParameters::AngleBracketed(
-                                AngleBracketedParameterData {
-                                    lifetimes: Vec::new(),
-                                    types: vec![Ty::Path(None, "Attribute".into())],
-                                    bindings: Vec::new(),
-                                },
-                            ),
-                        }
-                    ],
-                                                                     }),
-                                                    }])),
+        body: Body::Struct(VariantData::Struct(vec![
+            Field {
+                ident: Some("ident".into()),
+                vis: Visibility::Public,
+                attrs: Vec::new(),
+                ty: TyPath {
+                    qself: None,
+                    path: "Ident".into(),
+                }.into(),
+            },
+            Field {
+                ident: Some("attrs".into()),
+                vis: Visibility::Public,
+                attrs: Vec::new(),
+                ty: TyPath {
+                    qself: None,
+                    path: Path {
+                        global: false,
+                        segments: vec![
+                            PathSegment {
+                                ident: "Vec".into(),
+                                parameters: PathParameters::AngleBracketed(
+                                    AngleBracketedParameterData {
+                                        lifetimes: Vec::new(),
+                                        types: vec![TyPath {
+                                            qself: None,
+                                            path: "Attribute".into(),
+                                        }.into()],
+                                        bindings: Vec::new(),
+                                    },
+                                ),
+                            }
+                        ],
+                    },
+                }.into(),
+            },
+        ]))
     };
 
     let actual = parse_macro_input(raw).unwrap();
 
     assert_eq!(expected, actual);
 
-    let expected_meta_item = MetaItem::List("derive".into(), vec![
-        NestedMetaItem::MetaItem(MetaItem::Word("Debug".into())),
-        NestedMetaItem::MetaItem(MetaItem::Word("Clone".into())),
-    ]);
+    let expected_meta_item: MetaItem = MetaItemList {
+        ident: "derive".into(),
+        nested: vec![
+            NestedMetaItem::MetaItem(MetaItem::Word("Debug".into())),
+            NestedMetaItem::MetaItem(MetaItem::Word("Clone".into())),
+        ],
+    }.into();
 
     assert_eq!(expected_meta_item, actual.attrs[0].meta_item().unwrap());
 }
@@ -153,7 +166,7 @@
                         ident: None,
                         vis: Visibility::Inherited,
                         attrs: Vec::new(),
-                        ty: Ty::Path(None, "T".into()),
+                        ty: TyPath { qself: None, path: "T".into() }.into(),
                     },
                 ]),
                 discriminant: None,
@@ -166,7 +179,7 @@
                         ident: None,
                         vis: Visibility::Inherited,
                         attrs: Vec::new(),
-                        ty: Ty::Path(None, "E".into()),
+                        ty: TyPath { qself: None, path: "E".into() }.into(),
                     },
                 ]),
                 discriminant: None,
@@ -182,22 +195,24 @@
                 attrs: Vec::new(),
                 data: VariantData::Unit,
                 discriminant: Some(ConstExpr::Other(Expr {
-                    node: ExprKind::TupField(
-                        Box::new(Expr {
-                            node: ExprKind::Tup(vec![
-                                Expr {
-                                    node: ExprKind::Lit(Lit::Int(0, IntTy::Unsuffixed)),
-                                    attrs: Vec::new(),
-                                },
-                                Expr {
-                                    node: ExprKind::Lit(Lit::Str("data".into(), StrStyle::Cooked)),
-                                    attrs: Vec::new(),
-                                },
-                            ]),
+                    node: ExprTupField {
+                        expr: Box::new(Expr {
+                            node: ExprTup {
+                                args: vec![
+                                    Expr {
+                                        node: ExprKind::Lit(Lit::Int(0, IntTy::Unsuffixed)),
+                                        attrs: Vec::new(),
+                                    },
+                                    Expr {
+                                        node: ExprKind::Lit(Lit::Str("data".into(), StrStyle::Cooked)),
+                                        attrs: Vec::new(),
+                                    },
+                                ],
+                            }.into(),
                             attrs: Vec::new(),
                         }),
-                        0
-                    ),
+                        field: 0
+                    }.into(),
                     attrs: Vec::new(),
                 })),
             },
@@ -209,10 +224,13 @@
     assert_eq!(expected, actual);
 
     let expected_meta_items = vec![
-        MetaItem::NameValue("doc".into(), Lit::Str(
-            "/// See the std::result module documentation for details.".into(),
-            StrStyle::Cooked,
-        )),
+        MetaNameValue {
+            ident: "doc".into(),
+            lit: Lit::Str(
+                "/// See the std::result module documentation for details.".into(),
+                StrStyle::Cooked,
+            ),
+        }.into(),
         MetaItem::Word("must_use".into()),
     ];
 
@@ -338,7 +356,7 @@
             ident: None,
             vis: Visibility::Restricted(Box::new(Path { global: false, segments: vec!["m".into(), "n".into()] })),
             attrs: vec![],
-            ty: Ty::Path(None, "u8".into()),
+            ty: TyPath { qself: None, path: "u8".into() }.into(),
         }])),
     };
 
diff --git a/tests/test_meta_item.rs b/tests/test_meta_item.rs
index 58a8eff..735c9a2 100644
--- a/tests/test_meta_item.rs
+++ b/tests/test_meta_item.rs
@@ -3,52 +3,75 @@
 
 #[test]
 fn test_meta_item_word() {
-    run_test("#[foo]", &MetaItem::Word("foo".into()))
+    run_test("#[foo]", MetaItem::Word("foo".into()))
 }
 
 #[test]
 fn test_meta_item_name_value() {
-    run_test("#[foo = 5]", &MetaItem::NameValue("foo".into(),
-        Lit::Int(5, IntTy::Unsuffixed)))
+    run_test("#[foo = 5]", MetaNameValue {
+        ident: "foo".into(),
+        lit: Lit::Int(5, IntTy::Unsuffixed),
+    })
 }
 
 #[test]
 fn test_meta_item_list_lit() {
-    run_test("#[foo(5)]", &MetaItem::List("foo".into(), vec![
-        NestedMetaItem::Literal(Lit::Int(5, IntTy::Unsuffixed)),
-    ]))
+    run_test("#[foo(5)]", MetaItemList {
+        ident: "foo".into(),
+        nested: vec![
+            NestedMetaItem::Literal(Lit::Int(5, IntTy::Unsuffixed)),
+        ],
+    })
 }
 
 #[test]
 fn test_meta_item_list_word() {
-    run_test("#[foo(bar)]", &MetaItem::List("foo".into(), vec![
-        NestedMetaItem::MetaItem(MetaItem::Word("bar".into())),
-    ]))
+    run_test("#[foo(bar)]", MetaItemList {
+        ident: "foo".into(),
+        nested: vec![
+            NestedMetaItem::MetaItem(MetaItem::Word("bar".into())),
+        ],
+    })
 }
 
 #[test]
 fn test_meta_item_list_name_value() {
-    run_test("#[foo(bar = 5)]", &MetaItem::List("foo".into(), vec![
-        NestedMetaItem::MetaItem(MetaItem::NameValue("bar".into(),
-            Lit::Int(5, IntTy::Unsuffixed))),
-    ]))
+    run_test("#[foo(bar = 5)]", MetaItemList {
+        ident: "foo".into(),
+        nested: vec![
+            NestedMetaItem::MetaItem(MetaNameValue {
+                ident: "bar".into(),
+                lit: Lit::Int(5, IntTy::Unsuffixed),
+            }.into()),
+        ],
+    })
 }
 
 #[test]
 fn test_meta_item_multiple() {
-    run_test("#[foo(word, name = 5, list(name2 = 6), word2)]", &MetaItem::List("foo".into(), vec![
-        NestedMetaItem::MetaItem(MetaItem::Word("word".into())),
-        NestedMetaItem::MetaItem(MetaItem::NameValue("name".into(),
-            Lit::Int(5, IntTy::Unsuffixed))),
-        NestedMetaItem::MetaItem(MetaItem::List("list".into(), vec![
-            NestedMetaItem::MetaItem(MetaItem::NameValue("name2".into(),
-                Lit::Int(6, IntTy::Unsuffixed)))
-        ])),
-        NestedMetaItem::MetaItem(MetaItem::Word("word2".into())),
-    ]))
+    run_test("#[foo(word, name = 5, list(name2 = 6), word2)]", MetaItemList {
+        ident: "foo".into(),
+        nested: vec![
+            NestedMetaItem::MetaItem(MetaItem::Word("word".into())),
+            NestedMetaItem::MetaItem(MetaNameValue {
+                ident: "name".into(),
+                lit: Lit::Int(5, IntTy::Unsuffixed),
+            }.into()),
+            NestedMetaItem::MetaItem(MetaItemList {
+                ident: "list".into(),
+                nested: vec![
+                    NestedMetaItem::MetaItem(MetaNameValue {
+                        ident: "name2".into(),
+                        lit: Lit::Int(6, IntTy::Unsuffixed),
+                    }.into())
+                ],
+            }.into()),
+            NestedMetaItem::MetaItem(MetaItem::Word("word2".into())),
+        ],
+    })
 }
 
-fn run_test(input: &str, expected: &MetaItem) {
+fn run_test<T: Into<MetaItem>>(input: &str, expected: T) {
     let attr = parse_outer_attr(input).unwrap();
-    assert_eq!(expected, &attr.meta_item().unwrap());
+    assert_eq!(expected.into(), attr.meta_item().unwrap());
 }