Merge pull request #258 from dtolnay/generic-param

Groundwork for const generics
diff --git a/src/gen/fold.rs b/src/gen/fold.rs
index a03ed31..1b676e2 100644
--- a/src/gen/fold.rs
+++ b/src/gen/fold.rs
@@ -202,6 +202,8 @@
 # [ cfg ( feature = "full" ) ]
 fn fold_foreign_item_type(&mut self, i: ForeignItemType) -> ForeignItemType { fold_foreign_item_type(self, i) }
 
+fn fold_generic_param(&mut self, i: GenericParam) -> GenericParam { fold_generic_param(self, i) }
+
 fn fold_generics(&mut self, i: Generics) -> Generics { fold_generics(self, i) }
 # [ cfg ( feature = "full" ) ]
 fn fold_impl_item(&mut self, i: ImplItem) -> ImplItem { fold_impl_item(self, i) }
@@ -1437,12 +1439,27 @@
     }
 }
 
+pub fn fold_generic_param<V: Folder + ?Sized>(_visitor: &mut V, _i: GenericParam) -> GenericParam {
+    use ::GenericParam::*;
+    match _i {
+        Lifetime(_binding_0, ) => {
+            Lifetime (
+                _visitor.fold_lifetime_def(_binding_0),
+            )
+        }
+        Type(_binding_0, ) => {
+            Type (
+                _visitor.fold_type_param(_binding_0),
+            )
+        }
+    }
+}
+
 pub fn fold_generics<V: Folder + ?Sized>(_visitor: &mut V, _i: Generics) -> Generics {
     Generics {
         lt_token: _i . lt_token,
+        params: FoldHelper::lift(_i . params, |it| { _visitor.fold_generic_param(it) }),
         gt_token: _i . gt_token,
-        lifetimes: FoldHelper::lift(_i . lifetimes, |it| { _visitor.fold_lifetime_def(it) }),
-        ty_params: FoldHelper::lift(_i . ty_params, |it| { _visitor.fold_type_param(it) }),
         where_clause: _visitor.fold_where_clause(_i . where_clause),
     }
 }
diff --git a/src/gen/visit.rs b/src/gen/visit.rs
index c422455..da068a1 100644
--- a/src/gen/visit.rs
+++ b/src/gen/visit.rs
@@ -175,6 +175,8 @@
 # [ cfg ( feature = "full" ) ]
 fn visit_foreign_item_type(&mut self, i: &'ast ForeignItemType) { visit_foreign_item_type(self, i) }
 
+fn visit_generic_param(&mut self, i: &'ast GenericParam) { visit_generic_param(self, i) }
+
 fn visit_generics(&mut self, i: &'ast Generics) { visit_generics(self, i) }
 # [ cfg ( feature = "full" ) ]
 fn visit_impl_item(&mut self, i: &'ast ImplItem) { visit_impl_item(self, i) }
@@ -1120,11 +1122,22 @@
     // Skipped field _i . semi_token;
 }
 
+pub fn visit_generic_param<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast GenericParam) {
+    use ::GenericParam::*;
+    match *_i {
+        Lifetime(ref _binding_0, ) => {
+            _visitor.visit_lifetime_def(&* _binding_0);
+        }
+        Type(ref _binding_0, ) => {
+            _visitor.visit_type_param(&* _binding_0);
+        }
+    }
+}
+
 pub fn visit_generics<'ast, V: Visitor<'ast> + ?Sized>(_visitor: &mut V, _i: &'ast Generics) {
     // Skipped field _i . lt_token;
+    for el in (_i . params).iter() { let it = el.item(); _visitor.visit_generic_param(&it) };
     // Skipped field _i . gt_token;
-    for el in (_i . lifetimes).iter() { let it = el.item(); _visitor.visit_lifetime_def(&it) };
-    for el in (_i . ty_params).iter() { let it = el.item(); _visitor.visit_type_param(&it) };
     _visitor.visit_where_clause(&_i . where_clause);
 }
 # [ cfg ( feature = "full" ) ]
diff --git a/src/gen/visit_mut.rs b/src/gen/visit_mut.rs
index 0d75bc7..7d5c109 100644
--- a/src/gen/visit_mut.rs
+++ b/src/gen/visit_mut.rs
@@ -175,6 +175,8 @@
 # [ cfg ( feature = "full" ) ]
 fn visit_foreign_item_type_mut(&mut self, i: &mut ForeignItemType) { visit_foreign_item_type_mut(self, i) }
 
+fn visit_generic_param_mut(&mut self, i: &mut GenericParam) { visit_generic_param_mut(self, i) }
+
 fn visit_generics_mut(&mut self, i: &mut Generics) { visit_generics_mut(self, i) }
 # [ cfg ( feature = "full" ) ]
 fn visit_impl_item_mut(&mut self, i: &mut ImplItem) { visit_impl_item_mut(self, i) }
@@ -1120,11 +1122,22 @@
     // Skipped field _i . semi_token;
 }
 
+pub fn visit_generic_param_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut GenericParam) {
+    use ::GenericParam::*;
+    match *_i {
+        Lifetime(ref mut _binding_0, ) => {
+            _visitor.visit_lifetime_def_mut(&mut * _binding_0);
+        }
+        Type(ref mut _binding_0, ) => {
+            _visitor.visit_type_param_mut(&mut * _binding_0);
+        }
+    }
+}
+
 pub fn visit_generics_mut<V: VisitorMut + ?Sized>(_visitor: &mut V, _i: &mut Generics) {
     // Skipped field _i . lt_token;
+    for mut el in (_i . params).iter_mut() { let mut it = el.item_mut(); _visitor.visit_generic_param_mut(&mut it) };
     // Skipped field _i . gt_token;
-    for mut el in (_i . lifetimes).iter_mut() { let mut it = el.item_mut(); _visitor.visit_lifetime_def_mut(&mut it) };
-    for mut el in (_i . ty_params).iter_mut() { let mut it = el.item_mut(); _visitor.visit_type_param_mut(&mut it) };
     _visitor.visit_where_clause_mut(&mut _i . where_clause);
 }
 # [ cfg ( feature = "full" ) ]
diff --git a/src/generics.rs b/src/generics.rs
index 950d5fb..08dba39 100644
--- a/src/generics.rs
+++ b/src/generics.rs
@@ -7,13 +7,33 @@
     #[derive(Default)]
     pub struct Generics {
         pub lt_token: Option<Token![<]>,
+        pub params: Delimited<GenericParam, Token![,]>,
         pub gt_token: Option<Token![>]>,
-        pub lifetimes: Delimited<LifetimeDef, Token![,]>,
-        pub ty_params: Delimited<TypeParam, Token![,]>,
         pub where_clause: WhereClause,
     }
 }
 
+ast_enum_of_structs! {
+    pub enum GenericParam {
+        /// A lifetime definition, e.g. `'a: 'b+'c+'d`
+        pub Lifetime(LifetimeDef {
+            pub attrs: Vec<Attribute>,
+            pub lifetime: Lifetime,
+            pub colon_token: Option<Token![:]>,
+            pub bounds: Delimited<Lifetime, Token![+]>,
+        }),
+        /// A generic type parameter, e.g. `T: Into<String>`.
+        pub Type(TypeParam {
+            pub attrs: Vec<Attribute>,
+            pub ident: Ident,
+            pub colon_token: Option<Token![:]>,
+            pub bounds: Delimited<TypeParamBound, Token![+]>,
+            pub eq_token: Option<Token![=]>,
+            pub default: Option<Type>,
+        }),
+    }
+}
+
 #[cfg(feature = "printing")]
 #[cfg_attr(feature = "extra-traits", derive(Debug, Eq, PartialEq, Hash))]
 #[cfg_attr(feature = "clone-impls", derive(Clone))]
@@ -77,16 +97,6 @@
     }
 }
 
-ast_struct! {
-    /// A lifetime definition, e.g. `'a: 'b+'c+'d`
-    pub struct LifetimeDef {
-        pub attrs: Vec<Attribute>,
-        pub lifetime: Lifetime,
-        pub colon_token: Option<Token![:]>,
-        pub bounds: Delimited<Lifetime, Token![+]>,
-    }
-}
-
 impl LifetimeDef {
     pub fn new(lifetime: Lifetime) -> Self {
         LifetimeDef {
@@ -98,18 +108,6 @@
     }
 }
 
-ast_struct! {
-    /// A generic type parameter, e.g. `T: Into<String>`.
-    pub struct TypeParam {
-        pub attrs: Vec<Attribute>,
-        pub ident: Ident,
-        pub colon_token: Option<Token![:]>,
-        pub bounds: Delimited<TypeParamBound, Token![+]>,
-        pub eq_token: Option<Token![=]>,
-        pub default: Option<Type>,
-    }
-}
-
 impl From<Ident> for TypeParam {
     fn from(ident: Ident) -> Self {
         TypeParam {
@@ -194,16 +192,17 @@
     use super::*;
 
     use synom::Synom;
+    use synom::delimited::Element;
 
     impl Synom for Generics {
         named!(parse -> Self, map!(
             alt!(
                 do_parse!(
                     lt: punct!(<) >>
-                    lifetimes: call!(Delimited::parse_terminated) >>
+                    lifetimes: call!(Delimited::<LifetimeDef, Token![,]>::parse_terminated) >>
                     ty_params: cond!(
-                        lifetimes.is_empty() || lifetimes.trailing_delim(),
-                        call!(Delimited::parse_terminated)
+                        lifetimes.empty_or_trailing(),
+                        call!(Delimited::<TypeParam, Token![,]>::parse_terminated)
                     ) >>
                     gt: punct!(>) >>
                     (lifetimes, ty_params, Some(lt), Some(gt))
@@ -212,11 +211,18 @@
                 epsilon!() => { |_| (Delimited::new(), None, None, None) }
             ),
             |(lifetimes, ty_params, lt, gt)| Generics {
-                lifetimes: lifetimes,
-                ty_params: ty_params.unwrap_or_default(),
-                where_clause: WhereClause::default(),
-                gt_token: gt,
                 lt_token: lt,
+                params: lifetimes.into_iter()
+                    .map(Element::into_tuple)
+                    .map(|(lifetime, comma)| (GenericParam::Lifetime(lifetime), comma))
+                    .chain(ty_params.unwrap_or_default()
+                        .into_iter()
+                        .map(Element::into_tuple)
+                        .map(|(ty_param, comma)| (GenericParam::Type(ty_param), comma)))
+                    .collect::<Vec<_>>()
+                    .into(),
+                gt_token: gt,
+                where_clause: WhereClause::default(),
             }
         ));
     }
@@ -358,18 +364,7 @@
 
     /// Returns true if the generics object has no lifetimes or ty_params.
     fn empty_normal_generics(generics: &Generics) -> bool {
-        generics.lifetimes.is_empty() && generics.ty_params.is_empty()
-    }
-
-    /// We need a comma between the lifetimes list and the ty_params list if
-    /// there are more than 0 lifetimes, the lifetimes list didn't have a
-    /// trailing delimiter, and there are more than 0 type parameters. This is a
-    /// helper method for adding that comma.
-    fn maybe_add_lifetime_params_comma(tokens: &mut Tokens, generics: &Generics) {
-        // We may need to require a trailing comma if we have any ty_params.
-        if !generics.lifetimes.empty_or_trailing() && !generics.ty_params.is_empty() {
-            <Token![,]>::default().to_tokens(tokens);
-        }
+        generics.params.is_empty()
     }
 
     impl ToTokens for Generics {
@@ -379,9 +374,7 @@
             }
 
             TokensOrDefault(&self.lt_token).to_tokens(tokens);
-            self.lifetimes.to_tokens(tokens);
-            maybe_add_lifetime_params_comma(tokens, self);
-            self.ty_params.to_tokens(tokens);
+            self.params.to_tokens(tokens);
             TokensOrDefault(&self.gt_token).to_tokens(tokens);
         }
     }
@@ -393,16 +386,20 @@
             }
 
             TokensOrDefault(&self.0.lt_token).to_tokens(tokens);
-            self.0.lifetimes.to_tokens(tokens);
-            maybe_add_lifetime_params_comma(tokens, &self.0);
-            for param in self.0.ty_params.iter() {
-                 // Leave off the type parameter defaults
-                let item = param.item();
-                tokens.append_all(item.attrs.outer());
-                item.ident.to_tokens(tokens);
-                if !item.bounds.is_empty() {
-                    TokensOrDefault(&item.colon_token).to_tokens(tokens);
-                    item.bounds.to_tokens(tokens);
+            for param in self.0.params.iter() {
+                match **param.item() {
+                    GenericParam::Lifetime(ref param) => {
+                        param.to_tokens(tokens);
+                    }
+                    GenericParam::Type(ref param) => {
+                        // Leave off the type parameter defaults
+                        tokens.append_all(param.attrs.outer());
+                        param.ident.to_tokens(tokens);
+                        if !param.bounds.is_empty() {
+                            TokensOrDefault(&param.colon_token).to_tokens(tokens);
+                            param.bounds.to_tokens(tokens);
+                        }
+                    }
                 }
                 param.delimiter().to_tokens(tokens);
             }
@@ -417,15 +414,17 @@
             }
 
             TokensOrDefault(&self.0.lt_token).to_tokens(tokens);
-            // Leave off the lifetime bounds and attributes
-            for param in self.0.lifetimes.iter() {
-                param.item().lifetime.to_tokens(tokens);
-                param.delimiter().to_tokens(tokens);
-            }
-            maybe_add_lifetime_params_comma(tokens, &self.0);
-            // Leave off the type parameter defaults
-            for param in self.0.ty_params.iter() {
-                param.item().ident.to_tokens(tokens);
+            for param in self.0.params.iter() {
+                match **param.item() {
+                    GenericParam::Lifetime(ref param) => {
+                        // Leave off the lifetime bounds and attributes
+                        param.lifetime.to_tokens(tokens);
+                    }
+                    GenericParam::Type(ref param) => {
+                        // Leave off the type parameter defaults
+                        param.ident.to_tokens(tokens);
+                    }
+                }
                 param.delimiter().to_tokens(tokens);
             }
             TokensOrDefault(&self.0.gt_token).to_tokens(tokens);
diff --git a/src/lib.rs b/src/lib.rs
index d3bd2f5..54f89e9 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -40,7 +40,7 @@
                PatLit, PatRange, PatSlice, InPlaceKind};
 
 mod generics;
-pub use generics::{Generics, LifetimeDef, TraitBoundModifier, TypeParam, TypeParamBound,
+pub use generics::{Generics, GenericParam, LifetimeDef, TraitBoundModifier, TypeParam, TypeParamBound,
                    WhereBoundPredicate, WhereClause, WhereEqPredicate, WherePredicate,
                    WhereRegionPredicate, BoundLifetimes};
 #[cfg(feature = "printing")]
diff --git a/tests/test_derive_input.rs b/tests/test_derive_input.rs
index d4a48ee..d4e0ada 100644
--- a/tests/test_derive_input.rs
+++ b/tests/test_derive_input.rs
@@ -196,27 +196,26 @@
             },
         ],
         generics: Generics {
-            lifetimes: Default::default(),
             lt_token: Some(Default::default()),
-            gt_token: Some(Default::default()),
-            ty_params: vec![
-                TypeParam {
+            params: vec![
+                GenericParam::Type(TypeParam {
                     attrs: Vec::new(),
                     ident: "T".into(),
                     bounds: Default::default(),
                     default: None,
                     colon_token: None,
                     eq_token: None,
-                },
-                TypeParam {
+                }),
+                GenericParam::Type(TypeParam {
                     attrs: Vec::new(),
                     ident: "E".into(),
                     bounds: Default::default(),
                     colon_token: None,
                     eq_token: None,
                     default: None,
-                },
+                }),
             ].into(),
+            gt_token: Some(Default::default()),
             where_clause: WhereClause::default(),
         },
         body: Body::Enum(BodyEnum {
diff --git a/tests/test_generics.rs b/tests/test_generics.rs
index 69e648f..1823a25 100644
--- a/tests/test_generics.rs
+++ b/tests/test_generics.rs
@@ -16,25 +16,23 @@
 #[test]
 fn test_split_for_impl() {
     // <'a, 'b: 'a, #[may_dangle] T: 'a = ()> where T: Debug
-    let mut generics = Generics {
+    let generics = Generics {
         gt_token: Some(Default::default()),
         lt_token: Some(Default::default()),
-        lifetimes: vec![
-            LifetimeDef {
+        params: vec![
+            GenericParam::Lifetime(LifetimeDef {
                 attrs: Default::default(),
                 lifetime: Lifetime::new(Term::intern("'a"), Span::default()),
                 bounds: Default::default(),
                 colon_token: None,
-            },
-            LifetimeDef {
+            }),
+            GenericParam::Lifetime(LifetimeDef {
                 attrs: Default::default(),
                 lifetime: Lifetime::new(Term::intern("'b"), Span::default()),
                 bounds: vec![Lifetime::new(Term::intern("'a"), Span::default())].into(),
                 colon_token: Some(tokens::Colon::default()),
-            },
-        ].into(),
-        ty_params: vec![
-            TypeParam {
+            }),
+            GenericParam::Type(TypeParam {
                 attrs: vec![Attribute {
                     bracket_token: Default::default(),
                     pound_token: Default::default(),
@@ -52,7 +50,7 @@
                 }.into()),
                 colon_token: Some(Default::default()),
                 eq_token: Default::default(),
-            },
+            }),
         ].into(),
         where_clause: WhereClause {
             where_token: Some(Default::default()),
@@ -77,7 +75,6 @@
             ].into(),
         },
     };
-    generics.lifetimes.push_trailing(Default::default());
 
     let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
     let tokens = quote! {