blob: 2c30a5f1c596aac944112a564b5cb84af7a373c3 [file] [log] [blame]
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);
}
}
}