blob: fbb89b11cfc1fa5422f02040189954b0a8960e8b [file] [log] [blame] [edit]
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,
}
#[cfg(feature = "printing")]
/// Returned by `Generics::split_for_impl`.
#[derive(Debug)]
pub struct ImplGenerics<'a>(&'a Generics);
#[cfg(feature = "printing")]
/// Returned by `Generics::split_for_impl`.
#[derive(Debug)]
pub struct TyGenerics<'a>(&'a Generics);
#[cfg(feature = "printing")]
/// Returned by `TyGenerics::as_turbofish`.
#[derive(Debug)]
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::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) -> (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)
}
}
#[derive(Debug, Clone, Eq, PartialEq, Hash, Ord, PartialOrd)]
pub struct Lifetime {
pub ident: Ident,
}
impl Lifetime {
pub fn new<T: Into<Ident>>(t: T) -> Self {
let id = Ident::new(t);
if !id.as_ref().starts_with('\'') {
panic!("lifetime name must start with apostrophe as in \"'a\", \
got {:?}",
id.as_ref());
}
Lifetime { ident: id }
}
}
/// 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>,
}
impl LifetimeDef {
pub fn new<T: Into<Ident>>(t: T) -> Self {
LifetimeDef {
attrs: Vec::new(),
lifetime: Lifetime::new(t),
bounds: Vec::new(),
}
}
}
/// 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>,
}
impl From<Ident> for TyParam {
fn from(ident: Ident) -> Self {
TyParam {
attrs: vec![],
ident: ident,
bounds: vec![],
default: None,
}
}
}
/// 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),
}
/// 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,
}
/// A `where` clause in a definition
#[derive(Debug, Clone, Eq, PartialEq, Default, Hash)]
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, 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),
}
/// 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`
#[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,
}
#[cfg(feature = "parsing")]
pub mod parsing {
use super::*;
use attr::parsing::outer_attr;
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)
)) >>
cond!(!lifetimes.is_empty() || !ty_params.is_empty(), option!(punct!(","))) >>
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!("'"),
alt!(
map!(ident, |id| Lifetime {
ident: format!("'{}", id).into(),
})
|
map!(keyword!("static"), |_| Lifetime {
ident: "'static".into(),
})
)
));
named!(pub lifetime_def -> LifetimeDef, do_parse!(
attrs: many0!(outer_attr) >>
life: lifetime >>
bounds: opt_vec!(preceded!(
punct!(":"),
separated_list!(punct!("+"), lifetime)
)) >>
(LifetimeDef {
attrs: attrs,
lifetime: life,
bounds: bounds,
})
));
named!(pub bound_lifetimes -> Vec<LifetimeDef>, opt_vec!(do_parse!(
keyword!("for") >>
punct!("<") >>
lifetimes: terminated_list!(punct!(","), lifetime_def) >>
punct!(">") >>
(lifetimes)
)));
named!(ty_param -> TyParam, do_parse!(
attrs: many0!(outer_attr) >>
id: ident >>
bounds: opt_vec!(preceded!(
punct!(":"),
separated_nonempty_list!(punct!("+"), ty_param_bound)
)) >>
default: option!(preceded!(
punct!("="),
ty
)) >>
(TyParam {
attrs: attrs,
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 >>
bounds: opt_vec!(preceded!(
punct!(":"),
separated_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 attr::FilterAttrs;
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<'a> ToTokens for ImplGenerics<'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.append("<");
tokens.append_separated(&self.0.lifetimes, ",");
// Leave off the type parameter defaults
for (i, ty_param) in self.0
.ty_params
.iter()
.enumerate() {
if i > 0 || has_lifetimes {
tokens.append(",");
}
tokens.append_all(ty_param.attrs.outer());
ty_param.ident.to_tokens(tokens);
if !ty_param.bounds.is_empty() {
tokens.append(":");
tokens.append_separated(&ty_param.bounds, "+");
}
}
tokens.append(">");
}
}
}
impl<'a> ToTokens for TyGenerics<'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.append("<");
// Leave off the lifetime bounds and attributes
let lifetimes = self.0
.lifetimes
.iter()
.map(|ld| &ld.lifetime);
tokens.append_separated(lifetimes, ",");
if has_lifetimes && has_ty_params {
tokens.append(",");
}
// Leave off the type parameter bounds, defaults, and attributes
let ty_params = self.0
.ty_params
.iter()
.map(|tp| &tp.ident);
tokens.append_separated(ty_params, ",");
tokens.append(">");
}
}
}
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.append("::");
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 LifetimeDef {
fn to_tokens(&self, tokens: &mut Tokens) {
tokens.append_all(self.attrs.outer());
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) {
tokens.append_all(self.attrs.outer());
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);
}
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() {
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, "+");
}
}
}
impl ToTokens for WhereEqPredicate {
fn to_tokens(&self, tokens: &mut Tokens) {
self.lhs_ty.to_tokens(tokens);
tokens.append("=");
self.rhs_ty.to_tokens(tokens);
}
}
}