use super::*; use proc_macro2::TokenStream; use punctuated::Punctuated; #[cfg(feature = "extra-traits")] use std::hash::{Hash, Hasher}; #[cfg(feature = "extra-traits")] use tt::TokenStreamHelper; ast_enum_of_structs! { /// The possible types that a Rust value could have. /// /// *This type is available if Syn is built with the `"derive"` or `"full"` /// feature.* /// /// # Syntax tree enum /// /// This type is a [syntax tree enum]. /// /// [syntax tree enum]: enum.Expr.html#syntax-tree-enums // // TODO: change syntax-tree-enum link to an intra rustdoc link, currently // blocked on https://github.com/rust-lang/rust/issues/62833 pub enum Type { /// A dynamically sized slice type: `[T]`. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Slice(TypeSlice { pub bracket_token: token::Bracket, pub elem: Box, }), /// A fixed size array type: `[T; n]`. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Array(TypeArray { pub bracket_token: token::Bracket, pub elem: Box, pub semi_token: Token![;], pub len: Expr, }), /// A raw pointer type: `*const T` or `*mut T`. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Ptr(TypePtr { pub star_token: Token![*], pub const_token: Option, pub mutability: Option, pub elem: Box, }), /// A reference type: `&'a T` or `&'a mut T`. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Reference(TypeReference { pub and_token: Token![&], pub lifetime: Option, pub mutability: Option, pub elem: Box, }), /// A bare function type: `fn(usize) -> bool`. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub BareFn(TypeBareFn { pub lifetimes: Option, pub unsafety: Option, pub abi: Option, pub fn_token: Token![fn], pub paren_token: token::Paren, pub inputs: Punctuated, pub variadic: Option, pub output: ReturnType, }), /// The never type: `!`. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Never(TypeNever { pub bang_token: Token![!], }), /// A tuple type: `(A, B, C, String)`. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Tuple(TypeTuple { pub paren_token: token::Paren, pub elems: Punctuated, }), /// A path like `std::slice::Iter`, optionally qualified with a /// self-type as in ` as SomeTrait>::Associated`. /// /// Type arguments are stored in the Path itself. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Path(TypePath { pub qself: Option, pub path: Path, }), /// A trait object type `Bound1 + Bound2 + Bound3` where `Bound` is a /// trait or a lifetime. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub TraitObject(TypeTraitObject { pub dyn_token: Option, pub bounds: Punctuated, }), /// An `impl Bound1 + Bound2 + Bound3` type where `Bound` is a trait or /// a lifetime. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub ImplTrait(TypeImplTrait { pub impl_token: Token![impl], pub bounds: Punctuated, }), /// A parenthesized type equivalent to the inner type. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Paren(TypeParen { pub paren_token: token::Paren, pub elem: Box, }), /// A type contained within invisible delimiters. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Group(TypeGroup { pub group_token: token::Group, pub elem: Box, }), /// Indication that a type should be inferred by the compiler: `_`. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Infer(TypeInfer { pub underscore_token: Token![_], }), /// A macro in the type position. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Macro(TypeMacro { pub mac: Macro, }), /// Tokens in type position not interpreted by Syn. /// /// *This type is available if Syn is built with the `"derive"` or /// `"full"` feature.* pub Verbatim(TypeVerbatim #manual_extra_traits { pub tts: TokenStream, }), } } #[cfg(feature = "extra-traits")] impl Eq for TypeVerbatim {} #[cfg(feature = "extra-traits")] impl PartialEq for TypeVerbatim { fn eq(&self, other: &Self) -> bool { TokenStreamHelper(&self.tts) == TokenStreamHelper(&other.tts) } } #[cfg(feature = "extra-traits")] impl Hash for TypeVerbatim { fn hash(&self, state: &mut H) where H: Hasher, { TokenStreamHelper(&self.tts).hash(state); } } ast_struct! { /// The binary interface of a function: `extern "C"`. /// /// *This type is available if Syn is built with the `"derive"` or `"full"` /// feature.* pub struct Abi { pub extern_token: Token![extern], pub name: Option, } } ast_struct! { /// An argument in a function type: the `usize` in `fn(usize) -> bool`. /// /// *This type is available if Syn is built with the `"derive"` or `"full"` /// feature.* pub struct BareFnArg { pub name: Option<(BareFnArgName, Token![:])>, pub ty: Type, } } ast_enum! { /// Name of an argument in a function type: the `n` in `fn(n: usize)`. /// /// *This type is available if Syn is built with the `"derive"` or `"full"` /// feature.* pub enum BareFnArgName { /// Argument given a name. Named(Ident), /// Argument not given a name, matched with `_`. Wild(Token![_]), } } ast_enum! { /// Return type of a function signature. /// /// *This type is available if Syn is built with the `"derive"` or `"full"` /// feature.* pub enum ReturnType { /// Return type is not specified. /// /// Functions default to `()` and closures default to type inference. Default, /// A particular type is returned. Type(Token![->], Box), } } #[cfg(feature = "parsing")] pub mod parsing { use super::*; use parse::{Parse, ParseStream, Result}; use path; impl Parse for Type { fn parse(input: ParseStream) -> Result { ambig_ty(input, true) } } impl Type { /// In some positions, types may not contain the `+` character, to /// disambiguate them. For example in the expression `1 as T`, T may not /// contain a `+` character. /// /// This parser does not allow a `+`, while the default parser does. pub fn without_plus(input: ParseStream) -> Result { ambig_ty(input, false) } } fn ambig_ty(input: ParseStream, allow_plus: bool) -> Result { if input.peek(token::Group) { return input.parse().map(Type::Group); } let mut lifetimes = None::; let mut lookahead = input.lookahead1(); if lookahead.peek(Token![for]) { lifetimes = input.parse()?; lookahead = input.lookahead1(); if !lookahead.peek(Ident) && !lookahead.peek(Token![fn]) && !lookahead.peek(Token![unsafe]) && !lookahead.peek(Token![extern]) && !lookahead.peek(Token![super]) && !lookahead.peek(Token![self]) && !lookahead.peek(Token![Self]) && !lookahead.peek(Token![crate]) { return Err(lookahead.error()); } } if lookahead.peek(token::Paren) { let content; let paren_token = parenthesized!(content in input); if content.is_empty() { return Ok(Type::Tuple(TypeTuple { paren_token: paren_token, elems: Punctuated::new(), })); } if content.peek(Lifetime) { return Ok(Type::Paren(TypeParen { paren_token: paren_token, elem: Box::new(Type::TraitObject(content.parse()?)), })); } if content.peek(Token![?]) { return Ok(Type::TraitObject(TypeTraitObject { dyn_token: None, bounds: { let mut bounds = Punctuated::new(); bounds.push_value(TypeParamBound::Trait(TraitBound { paren_token: Some(paren_token), ..content.parse()? })); while let Some(plus) = input.parse()? { bounds.push_punct(plus); bounds.push_value(input.parse()?); } bounds }, })); } let first: Type = content.parse()?; if content.peek(Token![,]) { return Ok(Type::Tuple(TypeTuple { paren_token: paren_token, elems: { let mut elems = Punctuated::new(); elems.push_value(first); elems.push_punct(content.parse()?); let rest: Punctuated = content.parse_terminated(Parse::parse)?; elems.extend(rest); elems }, })); } if allow_plus && input.peek(Token![+]) { loop { let first = match first { Type::Path(TypePath { qself: None, path }) => { TypeParamBound::Trait(TraitBound { paren_token: Some(paren_token), modifier: TraitBoundModifier::None, lifetimes: None, path: path, }) } Type::TraitObject(TypeTraitObject { dyn_token: None, ref bounds, }) => { if bounds.len() > 1 || bounds.trailing_punct() { break; } match first { Type::TraitObject(TypeTraitObject { bounds, .. }) => { match bounds.into_iter().next().unwrap() { TypeParamBound::Trait(trait_bound) => { TypeParamBound::Trait(TraitBound { paren_token: Some(paren_token), ..trait_bound }) } other => other, } } _ => unreachable!(), } } _ => break, }; return Ok(Type::TraitObject(TypeTraitObject { dyn_token: None, bounds: { let mut bounds = Punctuated::new(); bounds.push_value(first); while let Some(plus) = input.parse()? { bounds.push_punct(plus); bounds.push_value(input.parse()?); } bounds }, })); } } Ok(Type::Paren(TypeParen { paren_token: paren_token, elem: Box::new(first), })) } else if lookahead.peek(Token![fn]) || lookahead.peek(Token![unsafe]) || lookahead.peek(Token![extern]) && !input.peek2(Token![::]) { let mut bare_fn: TypeBareFn = input.parse()?; bare_fn.lifetimes = lifetimes; Ok(Type::BareFn(bare_fn)) } else if lookahead.peek(Ident) || input.peek(Token![super]) || input.peek(Token![self]) || input.peek(Token![Self]) || input.peek(Token![crate]) || input.peek(Token![extern]) || lookahead.peek(Token![::]) || lookahead.peek(Token![<]) { if input.peek(Token![dyn]) { let mut trait_object: TypeTraitObject = input.parse()?; if lifetimes.is_some() { match *trait_object.bounds.iter_mut().next().unwrap() { TypeParamBound::Trait(ref mut trait_bound) => { trait_bound.lifetimes = lifetimes; } TypeParamBound::Lifetime(_) => unreachable!(), } } return Ok(Type::TraitObject(trait_object)); } let ty: TypePath = input.parse()?; if ty.qself.is_some() { return Ok(Type::Path(ty)); } if input.peek(Token![!]) && !input.peek(Token![!=]) { let mut contains_arguments = false; for segment in &ty.path.segments { match segment.arguments { PathArguments::None => {} PathArguments::AngleBracketed(_) | PathArguments::Parenthesized(_) => { contains_arguments = true; } } } if !contains_arguments { let bang_token: Token![!] = input.parse()?; let (delimiter, tts) = mac::parse_delimiter(input)?; return Ok(Type::Macro(TypeMacro { mac: Macro { path: ty.path, bang_token: bang_token, delimiter: delimiter, tts: tts, }, })); } } if lifetimes.is_some() || allow_plus && input.peek(Token![+]) { let mut bounds = Punctuated::new(); bounds.push_value(TypeParamBound::Trait(TraitBound { paren_token: None, modifier: TraitBoundModifier::None, lifetimes: lifetimes, path: ty.path, })); if allow_plus { while input.peek(Token![+]) { bounds.push_punct(input.parse()?); if input.peek(Token![>]) { break; } bounds.push_value(input.parse()?); } } return Ok(Type::TraitObject(TypeTraitObject { dyn_token: None, bounds: bounds, })); } Ok(Type::Path(ty)) } else if lookahead.peek(token::Bracket) { let content; let bracket_token = bracketed!(content in input); let elem: Type = content.parse()?; if content.peek(Token![;]) { Ok(Type::Array(TypeArray { bracket_token: bracket_token, elem: Box::new(elem), semi_token: content.parse()?, len: content.parse()?, })) } else { Ok(Type::Slice(TypeSlice { bracket_token: bracket_token, elem: Box::new(elem), })) } } else if lookahead.peek(Token![*]) { input.parse().map(Type::Ptr) } else if lookahead.peek(Token![&]) { input.parse().map(Type::Reference) } else if lookahead.peek(Token![!]) && !input.peek(Token![=]) { input.parse().map(Type::Never) } else if lookahead.peek(Token![impl]) { input.parse().map(Type::ImplTrait) } else if lookahead.peek(Token![_]) { input.parse().map(Type::Infer) } else if lookahead.peek(Lifetime) { input.parse().map(Type::TraitObject) } else { Err(lookahead.error()) } } impl Parse for TypeSlice { fn parse(input: ParseStream) -> Result { let content; Ok(TypeSlice { bracket_token: bracketed!(content in input), elem: content.parse()?, }) } } impl Parse for TypeArray { fn parse(input: ParseStream) -> Result { let content; Ok(TypeArray { bracket_token: bracketed!(content in input), elem: content.parse()?, semi_token: content.parse()?, len: content.parse()?, }) } } impl Parse for TypePtr { fn parse(input: ParseStream) -> Result { let star_token: Token![*] = input.parse()?; let lookahead = input.lookahead1(); let (const_token, mutability) = if lookahead.peek(Token![const]) { (Some(input.parse()?), None) } else if lookahead.peek(Token![mut]) { (None, Some(input.parse()?)) } else { return Err(lookahead.error()); }; Ok(TypePtr { star_token: star_token, const_token: const_token, mutability: mutability, elem: Box::new(input.call(Type::without_plus)?), }) } } impl Parse for TypeReference { fn parse(input: ParseStream) -> Result { Ok(TypeReference { and_token: input.parse()?, lifetime: input.parse()?, mutability: input.parse()?, // & binds tighter than +, so we don't allow + here. elem: Box::new(input.call(Type::without_plus)?), }) } } impl Parse for TypeBareFn { fn parse(input: ParseStream) -> Result { let args; let allow_variadic; Ok(TypeBareFn { lifetimes: input.parse()?, unsafety: input.parse()?, abi: input.parse()?, fn_token: input.parse()?, paren_token: parenthesized!(args in input), inputs: { let mut inputs = Punctuated::new(); while !args.is_empty() && !args.peek(Token![...]) { inputs.push_value(args.parse()?); if args.is_empty() { break; } inputs.push_punct(args.parse()?); } allow_variadic = inputs.empty_or_trailing(); inputs }, variadic: { if allow_variadic && args.peek(Token![...]) { Some(args.parse()?) } else { None } }, output: input.call(ReturnType::without_plus)?, }) } } impl Parse for TypeNever { fn parse(input: ParseStream) -> Result { Ok(TypeNever { bang_token: input.parse()?, }) } } impl Parse for TypeInfer { fn parse(input: ParseStream) -> Result { Ok(TypeInfer { underscore_token: input.parse()?, }) } } impl Parse for TypeTuple { fn parse(input: ParseStream) -> Result { let content; Ok(TypeTuple { paren_token: parenthesized!(content in input), elems: content.parse_terminated(Type::parse)?, }) } } impl Parse for TypeMacro { fn parse(input: ParseStream) -> Result { Ok(TypeMacro { mac: input.parse()?, }) } } impl Parse for TypePath { fn parse(input: ParseStream) -> Result { let (qself, mut path) = path::parsing::qpath(input, false)?; if path.segments.last().unwrap().value().arguments.is_empty() && input.peek(token::Paren) { let args: ParenthesizedGenericArguments = input.parse()?; let parenthesized = PathArguments::Parenthesized(args); path.segments.last_mut().unwrap().value_mut().arguments = parenthesized; } Ok(TypePath { qself: qself, path: path, }) } } impl ReturnType { pub fn without_plus(input: ParseStream) -> Result { Self::parse(input, false) } pub fn parse(input: ParseStream, allow_plus: bool) -> Result { if input.peek(Token![->]) { let arrow = input.parse()?; let ty = ambig_ty(input, allow_plus)?; Ok(ReturnType::Type(arrow, Box::new(ty))) } else { Ok(ReturnType::Default) } } } impl Parse for ReturnType { fn parse(input: ParseStream) -> Result { Self::parse(input, true) } } impl Parse for TypeTraitObject { fn parse(input: ParseStream) -> Result { Self::parse(input, true) } } fn at_least_one_type(bounds: &Punctuated) -> bool { for bound in bounds { if let TypeParamBound::Trait(_) = *bound { return true; } } false } impl TypeTraitObject { pub fn without_plus(input: ParseStream) -> Result { Self::parse(input, false) } // Only allow multiple trait references if allow_plus is true. pub fn parse(input: ParseStream, allow_plus: bool) -> Result { Ok(TypeTraitObject { dyn_token: input.parse()?, bounds: { let mut bounds = Punctuated::new(); if allow_plus { loop { bounds.push_value(input.parse()?); if !input.peek(Token![+]) { break; } bounds.push_punct(input.parse()?); if input.peek(Token![>]) { break; } } } else { bounds.push_value(input.parse()?); } // Just lifetimes like `'a + 'b` is not a TraitObject. if !at_least_one_type(&bounds) { return Err(input.error("expected at least one type")); } bounds }, }) } } impl Parse for TypeImplTrait { fn parse(input: ParseStream) -> Result { Ok(TypeImplTrait { impl_token: input.parse()?, // NOTE: rust-lang/rust#34511 includes discussion about whether // or not + should be allowed in ImplTrait directly without (). bounds: { let mut bounds = Punctuated::new(); loop { bounds.push_value(input.parse()?); if !input.peek(Token![+]) { break; } bounds.push_punct(input.parse()?); } bounds }, }) } } impl Parse for TypeGroup { fn parse(input: ParseStream) -> Result { let group = private::parse_group(input)?; Ok(TypeGroup { group_token: group.token, elem: group.content.parse()?, }) } } impl Parse for TypeParen { fn parse(input: ParseStream) -> Result { Self::parse(input, false) } } impl TypeParen { fn parse(input: ParseStream, allow_plus: bool) -> Result { let content; Ok(TypeParen { paren_token: parenthesized!(content in input), elem: Box::new(ambig_ty(&content, allow_plus)?), }) } } impl Parse for BareFnArg { fn parse(input: ParseStream) -> Result { Ok(BareFnArg { name: { if (input.peek(Ident) || input.peek(Token![_])) && !input.peek2(Token![::]) && input.peek2(Token![:]) { let name: BareFnArgName = input.parse()?; let colon: Token![:] = input.parse()?; Some((name, colon)) } else { None } }, ty: input.parse()?, }) } } impl Parse for BareFnArgName { fn parse(input: ParseStream) -> Result { let lookahead = input.lookahead1(); if lookahead.peek(Ident) { input.parse().map(BareFnArgName::Named) } else if lookahead.peek(Token![_]) { input.parse().map(BareFnArgName::Wild) } else { Err(lookahead.error()) } } } impl Parse for Abi { fn parse(input: ParseStream) -> Result { Ok(Abi { extern_token: input.parse()?, name: input.parse()?, }) } } impl Parse for Option { fn parse(input: ParseStream) -> Result { if input.peek(Token![extern]) { input.parse().map(Some) } else { Ok(None) } } } } #[cfg(feature = "printing")] mod printing { use super::*; use proc_macro2::TokenStream; use quote::ToTokens; use print::TokensOrDefault; impl ToTokens for TypeSlice { fn to_tokens(&self, tokens: &mut TokenStream) { self.bracket_token.surround(tokens, |tokens| { self.elem.to_tokens(tokens); }); } } impl ToTokens for TypeArray { fn to_tokens(&self, tokens: &mut TokenStream) { self.bracket_token.surround(tokens, |tokens| { self.elem.to_tokens(tokens); self.semi_token.to_tokens(tokens); self.len.to_tokens(tokens); }); } } impl ToTokens for TypePtr { fn to_tokens(&self, tokens: &mut TokenStream) { self.star_token.to_tokens(tokens); match self.mutability { Some(ref tok) => tok.to_tokens(tokens), None => { TokensOrDefault(&self.const_token).to_tokens(tokens); } } self.elem.to_tokens(tokens); } } impl ToTokens for TypeReference { fn to_tokens(&self, tokens: &mut TokenStream) { self.and_token.to_tokens(tokens); self.lifetime.to_tokens(tokens); self.mutability.to_tokens(tokens); self.elem.to_tokens(tokens); } } impl ToTokens for TypeBareFn { fn to_tokens(&self, tokens: &mut TokenStream) { self.lifetimes.to_tokens(tokens); self.unsafety.to_tokens(tokens); self.abi.to_tokens(tokens); self.fn_token.to_tokens(tokens); self.paren_token.surround(tokens, |tokens| { self.inputs.to_tokens(tokens); if let Some(ref variadic) = self.variadic { if !self.inputs.empty_or_trailing() { let span = variadic.spans[0]; Token![,](span).to_tokens(tokens); } variadic.to_tokens(tokens); } }); self.output.to_tokens(tokens); } } impl ToTokens for TypeNever { fn to_tokens(&self, tokens: &mut TokenStream) { self.bang_token.to_tokens(tokens); } } impl ToTokens for TypeTuple { fn to_tokens(&self, tokens: &mut TokenStream) { self.paren_token.surround(tokens, |tokens| { self.elems.to_tokens(tokens); }); } } impl ToTokens for TypePath { fn to_tokens(&self, tokens: &mut TokenStream) { private::print_path(tokens, &self.qself, &self.path); } } impl ToTokens for TypeTraitObject { fn to_tokens(&self, tokens: &mut TokenStream) { self.dyn_token.to_tokens(tokens); self.bounds.to_tokens(tokens); } } impl ToTokens for TypeImplTrait { fn to_tokens(&self, tokens: &mut TokenStream) { self.impl_token.to_tokens(tokens); self.bounds.to_tokens(tokens); } } impl ToTokens for TypeGroup { fn to_tokens(&self, tokens: &mut TokenStream) { self.group_token.surround(tokens, |tokens| { self.elem.to_tokens(tokens); }); } } impl ToTokens for TypeParen { fn to_tokens(&self, tokens: &mut TokenStream) { self.paren_token.surround(tokens, |tokens| { self.elem.to_tokens(tokens); }); } } impl ToTokens for TypeInfer { fn to_tokens(&self, tokens: &mut TokenStream) { self.underscore_token.to_tokens(tokens); } } impl ToTokens for TypeMacro { fn to_tokens(&self, tokens: &mut TokenStream) { self.mac.to_tokens(tokens); } } impl ToTokens for TypeVerbatim { fn to_tokens(&self, tokens: &mut TokenStream) { self.tts.to_tokens(tokens); } } impl ToTokens for ReturnType { fn to_tokens(&self, tokens: &mut TokenStream) { match *self { ReturnType::Default => {} ReturnType::Type(ref arrow, ref ty) => { arrow.to_tokens(tokens); ty.to_tokens(tokens); } } } } impl ToTokens for BareFnArg { fn to_tokens(&self, tokens: &mut TokenStream) { if let Some((ref name, ref colon)) = self.name { name.to_tokens(tokens); colon.to_tokens(tokens); } self.ty.to_tokens(tokens); } } impl ToTokens for BareFnArgName { fn to_tokens(&self, tokens: &mut TokenStream) { match *self { BareFnArgName::Named(ref t) => t.to_tokens(tokens), BareFnArgName::Wild(ref t) => t.to_tokens(tokens), } } } impl ToTokens for Abi { fn to_tokens(&self, tokens: &mut TokenStream) { self.extern_token.to_tokens(tokens); self.name.to_tokens(tokens); } } }