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(¶m.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! {