blob: aa8856d20a3c2d81e38cfb3dde2d63b14c763538 [file] [log] [blame]
use super::*;
/// Represents lifetimes and type parameters attached to a declaration
/// of a function, enum, trait, etc.
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct Generics {
pub lifetimes: Vec<LifetimeDef>,
pub ty_params: Vec<TyParam>,
pub where_clause: WhereClause,
}
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::new("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) -> (Generics, Generics, WhereClause) {
// Remove where clause and type parameter defaults.
let impl_generics = Generics {
lifetimes: self.lifetimes.clone(),
ty_params: self.ty_params
.iter()
.map(|ty_param| {
TyParam {
ident: ty_param.ident.clone(),
bounds: ty_param.bounds.clone(),
default: None,
}
})
.collect(),
where_clause: WhereClause::none(),
};
// Remove where clause, type parameter defaults, and bounds.
let ty_generics = Generics {
lifetimes: self.lifetimes
.iter()
.map(|lifetime_def| {
LifetimeDef {
lifetime: lifetime_def.lifetime.clone(),
bounds: Vec::new(),
}
})
.collect(),
ty_params: self.ty_params
.iter()
.map(|ty_param| {
TyParam {
ident: ty_param.ident.clone(),
bounds: Vec::new(),
default: None,
}
})
.collect(),
where_clause: WhereClause::none(),
};
let where_clause = self.where_clause.clone();
(impl_generics, ty_generics, where_clause)
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct Lifetime {
pub ident: Ident,
}
impl Lifetime {
pub fn new<T: Into<Ident>>(t: T) -> Self {
Lifetime { ident: Ident::new(t) }
}
}
/// A lifetime definition, e.g. `'a: 'b+'c+'d`
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct LifetimeDef {
pub lifetime: Lifetime,
pub bounds: Vec<Lifetime>,
}
impl LifetimeDef {
pub fn new<T: Into<Ident>>(t: T) -> Self {
LifetimeDef {
lifetime: Lifetime::new(t),
bounds: Vec::new(),
}
}
}
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct TyParam {
pub ident: Ident,
pub bounds: Vec<TyParamBound>,
pub default: Option<Ty>,
}
/// 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)]
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)]
pub enum TraitBoundModifier {
None,
Maybe,
}
/// A `where` clause in a definition
#[derive(Debug, Clone, Eq, PartialEq, Default)]
pub struct WhereClause {
pub predicates: Vec<WherePredicate>,
}
impl WhereClause {
pub fn none() -> Self {
WhereClause { predicates: Vec::new() }
}
}
/// A single predicate in a `where` clause
#[derive(Debug, Clone, Eq, PartialEq)]
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),
}
/// A type bound.
///
/// E.g. `for<'c> Foo: Send+Clone+'c`
#[derive(Debug, Clone, Eq, PartialEq)]
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`
#[derive(Debug, Clone, Eq, PartialEq)]
pub struct WhereRegionPredicate {
pub lifetime: Lifetime,
pub bounds: Vec<Lifetime>,
}
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
use ident::parsing::ident;
use ty::parsing::{ty, poly_trait_ref};
named!(pub generics -> Generics, map!(
alt!(
do_parse!(
punct!("<") >>
lifetimes: separated_list!(punct!(","), lifetime_def) >>
ty_params: opt_vec!(preceded!(
cond!(!lifetimes.is_empty(), punct!(",")),
separated_nonempty_list!(punct!(","), ty_param)
)) >>
punct!(">") >>
(lifetimes, ty_params)
)
|
epsilon!() => { |_| (Vec::new(), Vec::new()) }
),
|(lifetimes, ty_params)| Generics {
lifetimes: lifetimes,
ty_params: ty_params,
where_clause: Default::default(),
}
));
named!(pub lifetime -> Lifetime, preceded!(
punct!("'"),
map!(ident, |id| Lifetime {
ident: format!("'{}", id).into(),
})
));
named!(pub lifetime_def -> LifetimeDef, do_parse!(
life: lifetime >>
bounds: opt_vec!(preceded!(
punct!(":"),
separated_nonempty_list!(punct!("+"), lifetime)
)) >>
(LifetimeDef {
lifetime: life,
bounds: bounds,
})
));
named!(pub bound_lifetimes -> Vec<LifetimeDef>, opt_vec!(do_parse!(
keyword!("for") >>
punct!("<") >>
lifetimes: separated_list!(punct!(","), lifetime_def) >>
punct!(">") >>
(lifetimes)
)));
named!(ty_param -> TyParam, do_parse!(
id: ident >>
bounds: opt_vec!(preceded!(
punct!(":"),
separated_nonempty_list!(punct!("+"), ty_param_bound)
)) >>
default: option!(preceded!(
punct!("="),
ty
)) >>
(TyParam {
ident: id,
bounds: bounds,
default: default,
})
));
named!(pub ty_param_bound -> TyParamBound, alt!(
preceded!(punct!("?"), poly_trait_ref) => {
|poly| TyParamBound::Trait(poly, TraitBoundModifier::Maybe)
}
|
lifetime => { TyParamBound::Region }
|
poly_trait_ref => {
|poly| TyParamBound::Trait(poly, TraitBoundModifier::None)
}
));
named!(pub where_clause -> WhereClause, alt!(
do_parse!(
keyword!("where") >>
predicates: separated_nonempty_list!(punct!(","), where_predicate) >>
option!(punct!(",")) >>
(WhereClause { predicates: predicates })
)
|
epsilon!() => { |_| Default::default() }
));
named!(where_predicate -> WherePredicate, alt!(
do_parse!(
ident: lifetime >>
punct!(":") >>
bounds: separated_nonempty_list!(punct!("+"), lifetime) >>
(WherePredicate::RegionPredicate(WhereRegionPredicate {
lifetime: ident,
bounds: bounds,
}))
)
|
do_parse!(
bound_lifetimes: bound_lifetimes >>
bounded_ty: ty >>
punct!(":") >>
bounds: separated_nonempty_list!(punct!("+"), ty_param_bound) >>
(WherePredicate::BoundPredicate(WhereBoundPredicate {
bound_lifetimes: bound_lifetimes,
bounded_ty: bounded_ty,
bounds: bounds,
}))
)
));
}
#[cfg(feature = "printing")]
mod printing {
use super::*;
use quote::{Tokens, ToTokens};
impl ToTokens for Generics {
fn to_tokens(&self, tokens: &mut Tokens) {
let has_lifetimes = !self.lifetimes.is_empty();
let has_ty_params = !self.ty_params.is_empty();
if has_lifetimes || has_ty_params {
tokens.append("<");
tokens.append_separated(&self.lifetimes, ",");
if has_lifetimes && has_ty_params {
tokens.append(",");
}
tokens.append_separated(&self.ty_params, ",");
tokens.append(">");
}
}
}
impl ToTokens for Lifetime {
fn to_tokens(&self, tokens: &mut Tokens) {
self.ident.to_tokens(tokens);
}
}
impl ToTokens for LifetimeDef {
fn to_tokens(&self, tokens: &mut Tokens) {
self.lifetime.to_tokens(tokens);
if !self.bounds.is_empty() {
tokens.append(":");
tokens.append_separated(&self.bounds, "+");
}
}
}
impl ToTokens for TyParam {
fn to_tokens(&self, tokens: &mut Tokens) {
self.ident.to_tokens(tokens);
if !self.bounds.is_empty() {
tokens.append(":");
tokens.append_separated(&self.bounds, "+");
}
if let Some(ref default) = self.default {
tokens.append("=");
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, modifier) => {
match modifier {
TraitBoundModifier::None => {}
TraitBoundModifier::Maybe => tokens.append("?"),
}
trait_ref.to_tokens(tokens);
}
}
}
}
impl ToTokens for WhereClause {
fn to_tokens(&self, tokens: &mut Tokens) {
if !self.predicates.is_empty() {
tokens.append("where");
tokens.append_separated(&self.predicates, ",");
}
}
}
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);
}
}
}
}
impl ToTokens for WhereBoundPredicate {
fn to_tokens(&self, tokens: &mut Tokens) {
if !self.bound_lifetimes.is_empty() {
tokens.append("for");
tokens.append("<");
tokens.append_separated(&self.bound_lifetimes, ",");
tokens.append(">");
}
self.bounded_ty.to_tokens(tokens);
if !self.bounds.is_empty() {
tokens.append(":");
tokens.append_separated(&self.bounds, "+");
}
}
}
impl ToTokens for WhereRegionPredicate {
fn to_tokens(&self, tokens: &mut Tokens) {
self.lifetime.to_tokens(tokens);
if !self.bounds.is_empty() {
tokens.append(":");
tokens.append_separated(&self.bounds, "+");
}
}
}
}