| use super::*; |
| use delimited::Delimited; |
| |
| ast_struct! { |
| /// Represents lifetimes and type parameters attached to a declaration |
| /// of a function, enum, trait, etc. |
| #[derive(Default)] |
| pub struct Generics { |
| pub lt_token: Option<tokens::Lt>, |
| pub gt_token: Option<tokens::Gt>, |
| pub lifetimes: Delimited<LifetimeDef, tokens::Comma>, |
| pub ty_params: Delimited<TyParam, tokens::Comma>, |
| pub where_clause: WhereClause, |
| } |
| } |
| |
| #[cfg(feature = "printing")] |
| ast_struct! { |
| /// Returned by `Generics::split_for_impl`. |
| pub struct ImplGenerics<'a>(&'a Generics); |
| } |
| |
| #[cfg(feature = "printing")] |
| ast_struct! { |
| /// Returned by `Generics::split_for_impl`. |
| pub struct TyGenerics<'a>(&'a Generics); |
| } |
| |
| #[cfg(feature = "printing")] |
| ast_struct! { |
| /// Returned by `TyGenerics::as_turbofish`. |
| pub struct Turbofish<'a>(&'a Generics); |
| } |
| |
| #[cfg(feature = "printing")] |
| impl Generics { |
| /// Split a type's generics into the pieces required for impl'ing a trait |
| /// for that type. |
| /// |
| /// ``` |
| /// # extern crate syn; |
| /// # #[macro_use] |
| /// # extern crate quote; |
| /// # fn main() { |
| /// # let generics: syn::Generics = Default::default(); |
| /// # let name = syn::Ident::from("MyType"); |
| /// let (impl_generics, ty_generics, where_clause) = generics.split_for_impl(); |
| /// quote! { |
| /// impl #impl_generics MyTrait for #name #ty_generics #where_clause { |
| /// // ... |
| /// } |
| /// } |
| /// # ; |
| /// # } |
| /// ``` |
| pub fn split_for_impl(&self) -> (ImplGenerics, TyGenerics, &WhereClause) { |
| (ImplGenerics(self), TyGenerics(self), &self.where_clause) |
| } |
| } |
| |
| #[cfg(feature = "printing")] |
| impl<'a> TyGenerics<'a> { |
| /// Turn a type's generics like `<X, Y>` into a turbofish like `::<X, Y>`. |
| pub fn as_turbofish(&self) -> Turbofish { |
| Turbofish(self.0) |
| } |
| } |
| |
| ast_struct! { |
| pub struct Lifetime { |
| pub ident: Ident, |
| } |
| } |
| |
| impl Lifetime { |
| pub fn new<T: Into<Ident>>(t: T) -> Self { |
| let id = t.into(); |
| if !id.as_ref().starts_with('\'') { |
| panic!("lifetime name must start with apostrophe as in \"'a\", \ |
| got {:?}", |
| id.as_ref()); |
| } |
| Lifetime { ident: id } |
| } |
| } |
| |
| ast_struct! { |
| /// A set of bound lifetimes, e.g. `for<'a, 'b, 'c>` |
| #[derive(Default)] |
| pub struct BoundLifetimes { |
| pub for_token: tokens::For, |
| pub lt_token: tokens::Lt, |
| pub lifetimes: Delimited<LifetimeDef, tokens::Comma>, |
| pub gt_token: tokens::Gt, |
| } |
| } |
| |
| 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<tokens::Colon>, |
| pub bounds: Delimited<Lifetime, tokens::Add>, |
| } |
| } |
| |
| impl LifetimeDef { |
| pub fn new<T: Into<Ident>>(t: T) -> Self { |
| LifetimeDef { |
| attrs: Vec::new(), |
| lifetime: Lifetime::new(t), |
| colon_token: None, |
| bounds: Delimited::new(), |
| } |
| } |
| } |
| |
| ast_struct! { |
| /// A generic type parameter, e.g. `T: Into<String>`. |
| pub struct TyParam { |
| pub attrs: Vec<Attribute>, |
| pub ident: Ident, |
| pub colon_token: Option<tokens::Colon>, |
| pub bounds: Delimited<TyParamBound, tokens::Add>, |
| pub eq_token: Option<tokens::Eq>, |
| pub default: Option<Ty>, |
| } |
| } |
| |
| impl From<Ident> for TyParam { |
| fn from(ident: Ident) -> Self { |
| TyParam { |
| attrs: vec![], |
| ident: ident, |
| colon_token: None, |
| bounds: Delimited::new(), |
| eq_token: None, |
| default: None, |
| } |
| } |
| } |
| |
| 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), |
| } |
| } |
| |
| 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. |
| #[cfg_attr(feature = "clone-impls", derive(Copy))] |
| pub enum TraitBoundModifier { |
| None, |
| Maybe(tokens::Question), |
| } |
| } |
| |
| ast_struct! { |
| /// A `where` clause in a definition |
| #[derive(Default)] |
| pub struct WhereClause { |
| pub where_token: Option<tokens::Where>, |
| pub predicates: Delimited<WherePredicate, tokens::Comma>, |
| } |
| } |
| |
| impl WhereClause { |
| pub fn none() -> Self { |
| WhereClause::default() |
| } |
| } |
| |
| 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: Option<BoundLifetimes>, |
| /// The type being bounded |
| pub bounded_ty: Ty, |
| pub colon_token: tokens::Colon, |
| /// Trait and lifetime bounds (`Clone+Send+'static`) |
| pub bounds: Delimited<TyParamBound, tokens::Add>, |
| }), |
| |
| /// A lifetime predicate, e.g. `'a: 'b+'c` |
| pub RegionPredicate(WhereRegionPredicate { |
| pub lifetime: Lifetime, |
| pub colon_token: Option<tokens::Colon>, |
| pub bounds: Delimited<Lifetime, tokens::Add>, |
| }), |
| |
| /// An equality predicate (unsupported) |
| pub EqPredicate(WhereEqPredicate { |
| pub lhs_ty: Ty, |
| pub eq_token: tokens::Eq, |
| pub rhs_ty: Ty, |
| }), |
| } |
| } |
| |
| #[cfg(feature = "parsing")] |
| pub mod parsing { |
| use super::*; |
| |
| use synom::{PResult, Cursor, Synom, parse_error}; |
| use synom::tokens::*; |
| |
| impl Synom for Generics { |
| named!(parse -> Self, map!( |
| alt!( |
| do_parse!( |
| lt: syn!(Lt) >> |
| lifetimes: call!(Delimited::parse_terminated) >> |
| ty_params: cond!( |
| lifetimes.is_empty() || lifetimes.trailing_delim(), |
| call!(Delimited::parse_terminated) |
| ) >> |
| gt: syn!(Gt) >> |
| (lifetimes, ty_params, Some(lt), Some(gt)) |
| ) |
| | |
| epsilon!() => { |_| (Delimited::new(), None, None, None) } |
| ), |
| |(lifetimes, ty_params, lt, gt): (_, Option<_>, _, _)| Generics { |
| lifetimes: lifetimes, |
| ty_params: ty_params.unwrap_or_default(), |
| where_clause: WhereClause::default(), |
| gt_token: gt, |
| lt_token: lt, |
| } |
| )); |
| } |
| |
| impl Synom for Lifetime { |
| fn parse(input: Cursor) -> PResult<Self> { |
| match input.word() { |
| Some((rest, span, sym)) => { |
| if sym.as_str().starts_with('\'') { |
| return Ok((rest, Lifetime { |
| ident: Ident { |
| span: Span(span), |
| sym: sym |
| } |
| })); |
| } |
| } |
| _ => {} |
| } |
| parse_error() |
| } |
| } |
| |
| impl Synom for LifetimeDef { |
| named!(parse -> Self, do_parse!( |
| attrs: many0!(call!(Attribute::parse_outer)) >> |
| life: syn!(Lifetime) >> |
| colon: option!(syn!(Colon)) >> |
| bounds: cond!( |
| colon.is_some(), |
| call!(Delimited::parse_separated_nonempty) |
| ) >> |
| (LifetimeDef { |
| attrs: attrs, |
| lifetime: life, |
| bounds: bounds.unwrap_or_default(), |
| colon_token: colon.map(|_| tokens::Colon::default()), |
| }) |
| )); |
| } |
| |
| impl Synom for BoundLifetimes { |
| named!(parse -> Self, do_parse!( |
| for_: syn!(For) >> |
| lt: syn!(Lt) >> |
| lifetimes: call!(Delimited::parse_terminated) >> |
| gt: syn!(Gt) >> |
| (BoundLifetimes { |
| for_token: for_, |
| lt_token: lt, |
| gt_token: gt, |
| lifetimes: lifetimes, |
| }) |
| )); |
| } |
| |
| impl Synom for TyParam { |
| named!(parse -> Self, do_parse!( |
| attrs: many0!(call!(Attribute::parse_outer)) >> |
| id: syn!(Ident) >> |
| colon: option!(syn!(Colon)) >> |
| bounds: cond!( |
| colon.is_some(), |
| call!(Delimited::parse_separated_nonempty) |
| ) >> |
| default: option!(do_parse!( |
| eq: syn!(Eq) >> |
| ty: syn!(Ty) >> |
| (eq, ty) |
| )) >> |
| (TyParam { |
| attrs: attrs, |
| ident: id, |
| bounds: bounds.unwrap_or_default(), |
| colon_token: colon, |
| eq_token: default.as_ref().map(|d| tokens::Eq((d.0).0)), |
| default: default.map(|d| d.1), |
| }) |
| )); |
| } |
| |
| impl Synom for TyParamBound { |
| named!(parse -> Self, alt!( |
| do_parse!( |
| question: syn!(Question) >> |
| poly: syn!(PolyTraitRef) >> |
| (TyParamBound::Trait(poly, TraitBoundModifier::Maybe(question))) |
| ) |
| | |
| syn!(Lifetime) => { TyParamBound::Region } |
| | |
| syn!(PolyTraitRef) => { |
| |poly| TyParamBound::Trait(poly, TraitBoundModifier::None) |
| } |
| )); |
| |
| fn description() -> Option<&'static str> { |
| Some("type parameter buond") |
| } |
| } |
| |
| impl Synom for WhereClause { |
| named!(parse -> Self, alt!( |
| do_parse!( |
| where_: syn!(Where) >> |
| predicates: call!(Delimited::parse_terminated) >> |
| (WhereClause { |
| predicates: predicates, |
| where_token: Some(where_), |
| }) |
| ) |
| | |
| epsilon!() => { |_| WhereClause::default() } |
| )); |
| |
| fn description() -> Option<&'static str> { |
| Some("where clause") |
| } |
| } |
| |
| impl Synom for WherePredicate { |
| named!(parse -> Self, alt!( |
| do_parse!( |
| ident: syn!(Lifetime) >> |
| colon: option!(syn!(Colon)) >> |
| bounds: cond!( |
| colon.is_some(), |
| call!(Delimited::parse_separated) |
| ) >> |
| (WherePredicate::RegionPredicate(WhereRegionPredicate { |
| lifetime: ident, |
| bounds: bounds.unwrap_or_default(), |
| colon_token: colon, |
| })) |
| ) |
| | |
| do_parse!( |
| bound_lifetimes: option!(syn!(BoundLifetimes)) >> |
| bounded_ty: syn!(Ty) >> |
| colon: syn!(Colon) >> |
| bounds: call!(Delimited::parse_separated_nonempty) >> |
| (WherePredicate::BoundPredicate(WhereBoundPredicate { |
| bound_lifetimes: bound_lifetimes, |
| bounded_ty: bounded_ty, |
| bounds: bounds, |
| colon_token: colon, |
| })) |
| ) |
| )); |
| } |
| } |
| |
| #[cfg(feature = "printing")] |
| mod printing { |
| use super::*; |
| use attr::FilterAttrs; |
| use quote::{Tokens, ToTokens}; |
| |
| impl ToTokens for Generics { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| self.lt_token.to_tokens(tokens); |
| self.lifetimes.to_tokens(tokens); |
| self.ty_params.to_tokens(tokens); |
| self.gt_token.to_tokens(tokens); |
| } |
| } |
| |
| impl<'a> ToTokens for ImplGenerics<'a> { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| self.0.lt_token.to_tokens(tokens); |
| self.0.lifetimes.to_tokens(tokens); |
| 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); |
| item.colon_token.to_tokens(tokens); |
| item.bounds.to_tokens(tokens); |
| param.delimiter().to_tokens(tokens); |
| } |
| self.0.gt_token.to_tokens(tokens); |
| } |
| } |
| |
| impl<'a> ToTokens for TyGenerics<'a> { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| 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); |
| } |
| // Leave off the type parameter defaults |
| for param in self.0.ty_params.iter() { |
| param.item().ident.to_tokens(tokens); |
| param.delimiter().to_tokens(tokens); |
| } |
| self.0.gt_token.to_tokens(tokens); |
| } |
| } |
| |
| impl<'a> ToTokens for Turbofish<'a> { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| let has_lifetimes = !self.0.lifetimes.is_empty(); |
| let has_ty_params = !self.0.ty_params.is_empty(); |
| if has_lifetimes || has_ty_params { |
| tokens::Colon2::default().to_tokens(tokens); |
| TyGenerics(self.0).to_tokens(tokens); |
| } |
| } |
| } |
| |
| impl ToTokens for Lifetime { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| self.ident.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for BoundLifetimes { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| self.for_token.to_tokens(tokens); |
| self.lt_token.to_tokens(tokens); |
| self.lifetimes.to_tokens(tokens); |
| self.gt_token.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for LifetimeDef { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| tokens.append_all(self.attrs.outer()); |
| self.lifetime.to_tokens(tokens); |
| self.colon_token.to_tokens(tokens); |
| self.bounds.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for TyParam { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| tokens.append_all(self.attrs.outer()); |
| self.ident.to_tokens(tokens); |
| self.colon_token.to_tokens(tokens); |
| self.bounds.to_tokens(tokens); |
| self.eq_token.to_tokens(tokens); |
| self.default.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for TyParamBound { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| match *self { |
| TyParamBound::Region(ref lifetime) => lifetime.to_tokens(tokens), |
| TyParamBound::Trait(ref trait_ref, ref modifier) => { |
| modifier.to_tokens(tokens); |
| trait_ref.to_tokens(tokens); |
| } |
| } |
| } |
| } |
| |
| impl ToTokens for TraitBoundModifier { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| match *self { |
| TraitBoundModifier::None => {} |
| TraitBoundModifier::Maybe(ref t) => t.to_tokens(tokens), |
| } |
| } |
| } |
| |
| impl ToTokens for WhereClause { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| self.where_token.to_tokens(tokens); |
| self.predicates.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for WhereBoundPredicate { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| self.bound_lifetimes.to_tokens(tokens); |
| self.bounded_ty.to_tokens(tokens); |
| self.colon_token.to_tokens(tokens); |
| self.bounds.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for WhereRegionPredicate { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| self.lifetime.to_tokens(tokens); |
| self.colon_token.to_tokens(tokens); |
| self.bounds.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for WhereEqPredicate { |
| fn to_tokens(&self, tokens: &mut Tokens) { |
| self.lhs_ty.to_tokens(tokens); |
| self.eq_token.to_tokens(tokens); |
| self.rhs_ty.to_tokens(tokens); |
| } |
| } |
| } |