| // SPDX-License-Identifier: Apache-2.0 OR MIT | |
| use crate::attr::Attribute; | |
| use crate::expr::Expr; | |
| use crate::generics::{BoundLifetimes, TypeParamBound}; | |
| use crate::ident::Ident; | |
| use crate::lifetime::Lifetime; | |
| use crate::lit::LitStr; | |
| use crate::mac::Macro; | |
| use crate::path::{Path, QSelf}; | |
| use crate::punctuated::Punctuated; | |
| use crate::token; | |
| use proc_macro2::TokenStream; | |
| ast_enum_of_structs! { | |
| /// The possible types that a Rust value could have. | |
| /// | |
| /// # Syntax tree enum | |
| /// | |
| /// This type is a [syntax tree enum]. | |
| /// | |
| /// [syntax tree enum]: crate::expr::Expr#syntax-tree-enums | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| #[non_exhaustive] | |
| pub enum Type { | |
| /// A fixed size array type: `[T; n]`. | |
| Array(TypeArray), | |
| /// A bare function type: `fn(usize) -> bool`. | |
| BareFn(TypeBareFn), | |
| /// A type contained within invisible delimiters. | |
| Group(TypeGroup), | |
| /// An `impl Bound1 + Bound2 + Bound3` type where `Bound` is a trait or | |
| /// a lifetime. | |
| ImplTrait(TypeImplTrait), | |
| /// Indication that a type should be inferred by the compiler: `_`. | |
| Infer(TypeInfer), | |
| /// A macro in the type position. | |
| Macro(TypeMacro), | |
| /// The never type: `!`. | |
| Never(TypeNever), | |
| /// A parenthesized type equivalent to the inner type. | |
| Paren(TypeParen), | |
| /// A path like `std::slice::Iter`, optionally qualified with a | |
| /// self-type as in `<Vec<T> as SomeTrait>::Associated`. | |
| Path(TypePath), | |
| /// A raw pointer type: `*const T` or `*mut T`. | |
| Ptr(TypePtr), | |
| /// A reference type: `&'a T` or `&'a mut T`. | |
| Reference(TypeReference), | |
| /// A dynamically sized slice type: `[T]`. | |
| Slice(TypeSlice), | |
| /// A trait object type `dyn Bound1 + Bound2 + Bound3` where `Bound` is a | |
| /// trait or a lifetime. | |
| TraitObject(TypeTraitObject), | |
| /// A tuple type: `(A, B, C, String)`. | |
| Tuple(TypeTuple), | |
| /// Tokens in type position not interpreted by Syn. | |
| Verbatim(TokenStream), | |
| // For testing exhaustiveness in downstream code, use the following idiom: | |
| // | |
| // match ty { | |
| // #![cfg_attr(test, deny(non_exhaustive_omitted_patterns))] | |
| // | |
| // Type::Array(ty) => {...} | |
| // Type::BareFn(ty) => {...} | |
| // ... | |
| // Type::Verbatim(ty) => {...} | |
| // | |
| // _ => { /* some sane fallback */ } | |
| // } | |
| // | |
| // This way we fail your tests but don't break your library when adding | |
| // a variant. You will be notified by a test failure when a variant is | |
| // added, so that you can add code to handle it, but your library will | |
| // continue to compile and work for downstream users in the interim. | |
| } | |
| } | |
| ast_struct! { | |
| /// A fixed size array type: `[T; n]`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeArray { | |
| pub bracket_token: token::Bracket, | |
| pub elem: Box<Type>, | |
| pub semi_token: Token![;], | |
| pub len: Expr, | |
| } | |
| } | |
| ast_struct! { | |
| /// A bare function type: `fn(usize) -> bool`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeBareFn { | |
| pub lifetimes: Option<BoundLifetimes>, | |
| pub unsafety: Option<Token![unsafe]>, | |
| pub abi: Option<Abi>, | |
| pub fn_token: Token![fn], | |
| pub paren_token: token::Paren, | |
| pub inputs: Punctuated<BareFnArg, Token![,]>, | |
| pub variadic: Option<BareVariadic>, | |
| pub output: ReturnType, | |
| } | |
| } | |
| ast_struct! { | |
| /// A type contained within invisible delimiters. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeGroup { | |
| pub group_token: token::Group, | |
| pub elem: Box<Type>, | |
| } | |
| } | |
| ast_struct! { | |
| /// An `impl Bound1 + Bound2 + Bound3` type where `Bound` is a trait or | |
| /// a lifetime. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeImplTrait { | |
| pub impl_token: Token![impl], | |
| pub bounds: Punctuated<TypeParamBound, Token![+]>, | |
| } | |
| } | |
| ast_struct! { | |
| /// Indication that a type should be inferred by the compiler: `_`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeInfer { | |
| pub underscore_token: Token![_], | |
| } | |
| } | |
| ast_struct! { | |
| /// A macro in the type position. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeMacro { | |
| pub mac: Macro, | |
| } | |
| } | |
| ast_struct! { | |
| /// The never type: `!`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeNever { | |
| pub bang_token: Token![!], | |
| } | |
| } | |
| ast_struct! { | |
| /// A parenthesized type equivalent to the inner type. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeParen { | |
| pub paren_token: token::Paren, | |
| pub elem: Box<Type>, | |
| } | |
| } | |
| ast_struct! { | |
| /// A path like `std::slice::Iter`, optionally qualified with a | |
| /// self-type as in `<Vec<T> as SomeTrait>::Associated`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypePath { | |
| pub qself: Option<QSelf>, | |
| pub path: Path, | |
| } | |
| } | |
| ast_struct! { | |
| /// A raw pointer type: `*const T` or `*mut T`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypePtr { | |
| pub star_token: Token![*], | |
| pub const_token: Option<Token![const]>, | |
| pub mutability: Option<Token![mut]>, | |
| pub elem: Box<Type>, | |
| } | |
| } | |
| ast_struct! { | |
| /// A reference type: `&'a T` or `&'a mut T`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeReference { | |
| pub and_token: Token![&], | |
| pub lifetime: Option<Lifetime>, | |
| pub mutability: Option<Token![mut]>, | |
| pub elem: Box<Type>, | |
| } | |
| } | |
| ast_struct! { | |
| /// A dynamically sized slice type: `[T]`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeSlice { | |
| pub bracket_token: token::Bracket, | |
| pub elem: Box<Type>, | |
| } | |
| } | |
| ast_struct! { | |
| /// A trait object type `dyn Bound1 + Bound2 + Bound3` where `Bound` is a | |
| /// trait or a lifetime. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeTraitObject { | |
| pub dyn_token: Option<Token![dyn]>, | |
| pub bounds: Punctuated<TypeParamBound, Token![+]>, | |
| } | |
| } | |
| ast_struct! { | |
| /// A tuple type: `(A, B, C, String)`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct TypeTuple { | |
| pub paren_token: token::Paren, | |
| pub elems: Punctuated<Type, Token![,]>, | |
| } | |
| } | |
| ast_struct! { | |
| /// The binary interface of a function: `extern "C"`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct Abi { | |
| pub extern_token: Token![extern], | |
| pub name: Option<LitStr>, | |
| } | |
| } | |
| ast_struct! { | |
| /// An argument in a function type: the `usize` in `fn(usize) -> bool`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct BareFnArg { | |
| pub attrs: Vec<Attribute>, | |
| pub name: Option<(Ident, Token![:])>, | |
| pub ty: Type, | |
| } | |
| } | |
| ast_struct! { | |
| /// The variadic argument of a function pointer like `fn(usize, ...)`. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| pub struct BareVariadic { | |
| pub attrs: Vec<Attribute>, | |
| pub name: Option<(Ident, Token![:])>, | |
| pub dots: Token![...], | |
| pub comma: Option<Token![,]>, | |
| } | |
| } | |
| ast_enum! { | |
| /// Return type of a function signature. | |
| #[cfg_attr(docsrs, doc(cfg(any(feature = "full", feature = "derive"))))] | |
| 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<Type>), | |
| } | |
| } | |
| #[cfg(feature = "parsing")] | |
| pub(crate) mod parsing { | |
| use crate::attr::Attribute; | |
| use crate::error::{self, Result}; | |
| use crate::ext::IdentExt as _; | |
| use crate::generics::{BoundLifetimes, TraitBound, TraitBoundModifier, TypeParamBound}; | |
| use crate::ident::Ident; | |
| use crate::lifetime::Lifetime; | |
| use crate::mac::{self, Macro}; | |
| use crate::parse::{Parse, ParseStream}; | |
| use crate::path; | |
| use crate::path::{Path, PathArguments, QSelf}; | |
| use crate::punctuated::Punctuated; | |
| use crate::token; | |
| use crate::ty::{ | |
| Abi, BareFnArg, BareVariadic, ReturnType, Type, TypeArray, TypeBareFn, TypeGroup, | |
| TypeImplTrait, TypeInfer, TypeMacro, TypeNever, TypeParen, TypePath, TypePtr, | |
| TypeReference, TypeSlice, TypeTraitObject, TypeTuple, | |
| }; | |
| use crate::verbatim; | |
| use proc_macro2::Span; | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for Type { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let allow_plus = true; | |
| let allow_group_generic = true; | |
| ambig_ty(input, allow_plus, allow_group_generic) | |
| } | |
| } | |
| 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. | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn without_plus(input: ParseStream) -> Result<Self> { | |
| let allow_plus = false; | |
| let allow_group_generic = true; | |
| ambig_ty(input, allow_plus, allow_group_generic) | |
| } | |
| } | |
| pub(crate) fn ambig_ty( | |
| input: ParseStream, | |
| allow_plus: bool, | |
| allow_group_generic: bool, | |
| ) -> Result<Type> { | |
| let begin = input.fork(); | |
| if input.peek(token::Group) { | |
| let mut group: TypeGroup = input.parse()?; | |
| if input.peek(Token![::]) && input.peek3(Ident::peek_any) { | |
| if let Type::Path(mut ty) = *group.elem { | |
| Path::parse_rest(input, &mut ty.path, false)?; | |
| return Ok(Type::Path(ty)); | |
| } else { | |
| return Ok(Type::Path(TypePath { | |
| qself: Some(QSelf { | |
| lt_token: Token, | |
| position: 0, | |
| as_token: None, | |
| gt_token: Token, | |
| ty: group.elem, | |
| }), | |
| path: Path::parse_helper(input, false)?, | |
| })); | |
| } | |
| } else if input.peek(Token![<]) && allow_group_generic | |
| || input.peek(Token![::]) && input.peek3(Token![<]) | |
| { | |
| if let Type::Path(mut ty) = *group.elem { | |
| let arguments = &mut ty.path.segments.last_mut().unwrap().arguments; | |
| if arguments.is_none() { | |
| *arguments = PathArguments::AngleBracketed(input.parse()?); | |
| Path::parse_rest(input, &mut ty.path, false)?; | |
| return Ok(Type::Path(ty)); | |
| } else { | |
| group.elem = Box::new(Type::Path(ty)); | |
| } | |
| } | |
| } | |
| return Ok(Type::Group(group)); | |
| } | |
| let mut lifetimes = None::<BoundLifetimes>; | |
| 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]) | |
| || input.peek(Token![dyn]) | |
| { | |
| 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, | |
| elems: Punctuated::new(), | |
| })); | |
| } | |
| if content.peek(Lifetime) { | |
| return Ok(Type::Paren(TypeParen { | |
| 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({ | |
| let allow_precise_capture = false; | |
| let allow_const = false; | |
| TypeParamBound::parse_single( | |
| input, | |
| allow_precise_capture, | |
| allow_const, | |
| )? | |
| }); | |
| } | |
| bounds | |
| }, | |
| })); | |
| } | |
| let mut first: Type = content.parse()?; | |
| if content.peek(Token![,]) { | |
| return Ok(Type::Tuple(TypeTuple { | |
| paren_token, | |
| elems: { | |
| let mut elems = Punctuated::new(); | |
| elems.push_value(first); | |
| elems.push_punct(content.parse()?); | |
| while !content.is_empty() { | |
| elems.push_value(content.parse()?); | |
| if content.is_empty() { | |
| break; | |
| } | |
| elems.push_punct(content.parse()?); | |
| } | |
| 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, | |
| }) | |
| } | |
| Type::TraitObject(TypeTraitObject { | |
| dyn_token: None, | |
| bounds, | |
| }) => { | |
| if bounds.len() > 1 || bounds.trailing_punct() { | |
| first = Type::TraitObject(TypeTraitObject { | |
| dyn_token: None, | |
| bounds, | |
| }); | |
| break; | |
| } | |
| match bounds.into_iter().next().unwrap() { | |
| TypeParamBound::Trait(trait_bound) => { | |
| TypeParamBound::Trait(TraitBound { | |
| paren_token: Some(paren_token), | |
| ..trait_bound | |
| }) | |
| } | |
| other @ (TypeParamBound::Lifetime(_) | |
| | TypeParamBound::PreciseCapture(_) | |
| | TypeParamBound::Verbatim(_)) => other, | |
| } | |
| } | |
| _ => 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({ | |
| let allow_precise_capture = false; | |
| let allow_const = false; | |
| TypeParamBound::parse_single( | |
| input, | |
| allow_precise_capture, | |
| allow_const, | |
| )? | |
| }); | |
| } | |
| bounds | |
| }, | |
| })); | |
| } | |
| } | |
| Ok(Type::Paren(TypeParen { | |
| paren_token, | |
| elem: Box::new(first), | |
| })) | |
| } else if lookahead.peek(Token![fn]) | |
| || lookahead.peek(Token![unsafe]) | |
| || lookahead.peek(Token![extern]) | |
| { | |
| 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]) | |
| || lookahead.peek(Token![::]) | |
| || lookahead.peek(Token![<]) | |
| { | |
| let ty: TypePath = input.parse()?; | |
| if ty.qself.is_some() { | |
| return Ok(Type::Path(ty)); | |
| } | |
| if input.peek(Token![!]) && !input.peek(Token![!=]) && ty.path.is_mod_style() { | |
| let bang_token: Token![!] = input.parse()?; | |
| let (delimiter, tokens) = mac::parse_delimiter(input)?; | |
| return Ok(Type::Macro(TypeMacro { | |
| mac: Macro { | |
| path: ty.path, | |
| bang_token, | |
| delimiter, | |
| tokens, | |
| }, | |
| })); | |
| } | |
| 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, | |
| path: ty.path, | |
| })); | |
| if allow_plus { | |
| while input.peek(Token![+]) { | |
| bounds.push_punct(input.parse()?); | |
| if !(input.peek(Ident::peek_any) | |
| || input.peek(Token![::]) | |
| || input.peek(Token![?]) | |
| || input.peek(Lifetime) | |
| || input.peek(token::Paren)) | |
| { | |
| break; | |
| } | |
| bounds.push_value({ | |
| let allow_precise_capture = false; | |
| let allow_const = false; | |
| TypeParamBound::parse_single(input, allow_precise_capture, allow_const)? | |
| }); | |
| } | |
| } | |
| return Ok(Type::TraitObject(TypeTraitObject { | |
| dyn_token: None, | |
| bounds, | |
| })); | |
| } | |
| Ok(Type::Path(ty)) | |
| } else if lookahead.peek(Token![dyn]) { | |
| let dyn_token: Token![dyn] = input.parse()?; | |
| let dyn_span = dyn_token.span; | |
| let star_token: Option<Token![*]> = input.parse()?; | |
| let bounds = TypeTraitObject::parse_bounds(dyn_span, input, allow_plus)?; | |
| Ok(if star_token.is_some() { | |
| Type::Verbatim(verbatim::between(&begin, input)) | |
| } else { | |
| Type::TraitObject(TypeTraitObject { | |
| dyn_token: Some(dyn_token), | |
| bounds, | |
| }) | |
| }) | |
| } 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, | |
| elem: Box::new(elem), | |
| semi_token: content.parse()?, | |
| len: content.parse()?, | |
| })) | |
| } else { | |
| Ok(Type::Slice(TypeSlice { | |
| 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]) { | |
| TypeImplTrait::parse(input, allow_plus).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()) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeSlice { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let content; | |
| Ok(TypeSlice { | |
| bracket_token: bracketed!(content in input), | |
| elem: content.parse()?, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeArray { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let content; | |
| Ok(TypeArray { | |
| bracket_token: bracketed!(content in input), | |
| elem: content.parse()?, | |
| semi_token: content.parse()?, | |
| len: content.parse()?, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypePtr { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| 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, | |
| const_token, | |
| mutability, | |
| elem: Box::new(input.call(Type::without_plus)?), | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeReference { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| 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)?), | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeBareFn { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let args; | |
| let mut variadic = None; | |
| 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() { | |
| let attrs = args.call(Attribute::parse_outer)?; | |
| if inputs.empty_or_trailing() | |
| && (args.peek(Token![...]) | |
| || (args.peek(Ident) || args.peek(Token![_])) | |
| && args.peek2(Token![:]) | |
| && args.peek3(Token![...])) | |
| { | |
| variadic = Some(parse_bare_variadic(&args, attrs)?); | |
| break; | |
| } | |
| let allow_self = inputs.is_empty(); | |
| let arg = parse_bare_fn_arg(&args, allow_self)?; | |
| inputs.push_value(BareFnArg { attrs, ..arg }); | |
| if args.is_empty() { | |
| break; | |
| } | |
| let comma = args.parse()?; | |
| inputs.push_punct(comma); | |
| } | |
| inputs | |
| }, | |
| variadic, | |
| output: input.call(ReturnType::without_plus)?, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeNever { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| Ok(TypeNever { | |
| bang_token: input.parse()?, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeInfer { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| Ok(TypeInfer { | |
| underscore_token: input.parse()?, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeTuple { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let content; | |
| let paren_token = parenthesized!(content in input); | |
| if content.is_empty() { | |
| return Ok(TypeTuple { | |
| paren_token, | |
| elems: Punctuated::new(), | |
| }); | |
| } | |
| let first: Type = content.parse()?; | |
| Ok(TypeTuple { | |
| paren_token, | |
| elems: { | |
| let mut elems = Punctuated::new(); | |
| elems.push_value(first); | |
| elems.push_punct(content.parse()?); | |
| while !content.is_empty() { | |
| elems.push_value(content.parse()?); | |
| if content.is_empty() { | |
| break; | |
| } | |
| elems.push_punct(content.parse()?); | |
| } | |
| elems | |
| }, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeMacro { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| Ok(TypeMacro { | |
| mac: input.parse()?, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypePath { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let expr_style = false; | |
| let (qself, path) = path::parsing::qpath(input, expr_style)?; | |
| Ok(TypePath { qself, path }) | |
| } | |
| } | |
| impl ReturnType { | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn without_plus(input: ParseStream) -> Result<Self> { | |
| let allow_plus = false; | |
| Self::parse(input, allow_plus) | |
| } | |
| pub(crate) fn parse(input: ParseStream, allow_plus: bool) -> Result<Self> { | |
| if input.peek(Token![->]) { | |
| let arrow = input.parse()?; | |
| let allow_group_generic = true; | |
| let ty = ambig_ty(input, allow_plus, allow_group_generic)?; | |
| Ok(ReturnType::Type(arrow, Box::new(ty))) | |
| } else { | |
| Ok(ReturnType::Default) | |
| } | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for ReturnType { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let allow_plus = true; | |
| Self::parse(input, allow_plus) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeTraitObject { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let allow_plus = true; | |
| Self::parse(input, allow_plus) | |
| } | |
| } | |
| impl TypeTraitObject { | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn without_plus(input: ParseStream) -> Result<Self> { | |
| let allow_plus = false; | |
| Self::parse(input, allow_plus) | |
| } | |
| // Only allow multiple trait references if allow_plus is true. | |
| pub(crate) fn parse(input: ParseStream, allow_plus: bool) -> Result<Self> { | |
| let dyn_token: Option<Token![dyn]> = input.parse()?; | |
| let dyn_span = match &dyn_token { | |
| Some(token) => token.span, | |
| None => input.span(), | |
| }; | |
| let bounds = Self::parse_bounds(dyn_span, input, allow_plus)?; | |
| Ok(TypeTraitObject { dyn_token, bounds }) | |
| } | |
| fn parse_bounds( | |
| dyn_span: Span, | |
| input: ParseStream, | |
| allow_plus: bool, | |
| ) -> Result<Punctuated<TypeParamBound, Token![+]>> { | |
| let allow_precise_capture = false; | |
| let allow_const = false; | |
| let bounds = TypeParamBound::parse_multiple( | |
| input, | |
| allow_plus, | |
| allow_precise_capture, | |
| allow_const, | |
| )?; | |
| let mut last_lifetime_span = None; | |
| let mut at_least_one_trait = false; | |
| for bound in &bounds { | |
| match bound { | |
| TypeParamBound::Trait(_) => { | |
| at_least_one_trait = true; | |
| break; | |
| } | |
| TypeParamBound::Lifetime(lifetime) => { | |
| last_lifetime_span = Some(lifetime.ident.span()); | |
| } | |
| TypeParamBound::PreciseCapture(_) | TypeParamBound::Verbatim(_) => { | |
| unreachable!() | |
| } | |
| } | |
| } | |
| // Just lifetimes like `'a + 'b` is not a TraitObject. | |
| if !at_least_one_trait { | |
| let msg = "at least one trait is required for an object type"; | |
| return Err(error::new2(dyn_span, last_lifetime_span.unwrap(), msg)); | |
| } | |
| Ok(bounds) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeImplTrait { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let allow_plus = true; | |
| Self::parse(input, allow_plus) | |
| } | |
| } | |
| impl TypeImplTrait { | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| pub fn without_plus(input: ParseStream) -> Result<Self> { | |
| let allow_plus = false; | |
| Self::parse(input, allow_plus) | |
| } | |
| pub(crate) fn parse(input: ParseStream, allow_plus: bool) -> Result<Self> { | |
| let impl_token: Token![impl] = input.parse()?; | |
| let allow_precise_capture = true; | |
| let allow_const = true; | |
| let bounds = TypeParamBound::parse_multiple( | |
| input, | |
| allow_plus, | |
| allow_precise_capture, | |
| allow_const, | |
| )?; | |
| let mut last_nontrait_span = None; | |
| let mut at_least_one_trait = false; | |
| for bound in &bounds { | |
| match bound { | |
| TypeParamBound::Trait(_) => { | |
| at_least_one_trait = true; | |
| break; | |
| } | |
| TypeParamBound::Lifetime(lifetime) => { | |
| last_nontrait_span = Some(lifetime.ident.span()); | |
| } | |
| TypeParamBound::PreciseCapture(precise_capture) => { | |
| #[cfg(feature = "full")] | |
| { | |
| last_nontrait_span = Some(precise_capture.gt_token.span); | |
| } | |
| #[cfg(not(feature = "full"))] | |
| { | |
| _ = precise_capture; | |
| unreachable!(); | |
| } | |
| } | |
| TypeParamBound::Verbatim(_) => { | |
| // `[const] Trait` | |
| at_least_one_trait = true; | |
| break; | |
| } | |
| } | |
| } | |
| if !at_least_one_trait { | |
| let msg = "at least one trait must be specified"; | |
| return Err(error::new2( | |
| impl_token.span, | |
| last_nontrait_span.unwrap(), | |
| msg, | |
| )); | |
| } | |
| Ok(TypeImplTrait { impl_token, bounds }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeGroup { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let group = crate::group::parse_group(input)?; | |
| Ok(TypeGroup { | |
| group_token: group.token, | |
| elem: group.content.parse()?, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for TypeParen { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let allow_plus = false; | |
| Self::parse(input, allow_plus) | |
| } | |
| } | |
| impl TypeParen { | |
| fn parse(input: ParseStream, allow_plus: bool) -> Result<Self> { | |
| let content; | |
| Ok(TypeParen { | |
| paren_token: parenthesized!(content in input), | |
| elem: Box::new({ | |
| let allow_group_generic = true; | |
| ambig_ty(&content, allow_plus, allow_group_generic)? | |
| }), | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for BareFnArg { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| let allow_self = false; | |
| parse_bare_fn_arg(input, allow_self) | |
| } | |
| } | |
| fn parse_bare_fn_arg(input: ParseStream, allow_self: bool) -> Result<BareFnArg> { | |
| let attrs = input.call(Attribute::parse_outer)?; | |
| let begin = input.fork(); | |
| let has_mut_self = allow_self && input.peek(Token![mut]) && input.peek2(Token![self]); | |
| if has_mut_self { | |
| input.parse::<Token![mut]>()?; | |
| } | |
| let mut has_self = false; | |
| let mut name = if (input.peek(Ident) || input.peek(Token![_]) || { | |
| has_self = allow_self && input.peek(Token![self]); | |
| has_self | |
| }) && input.peek2(Token![:]) | |
| && !input.peek2(Token![::]) | |
| { | |
| let name = input.call(Ident::parse_any)?; | |
| let colon: Token![:] = input.parse()?; | |
| Some((name, colon)) | |
| } else { | |
| has_self = false; | |
| None | |
| }; | |
| let ty = if allow_self && !has_self && input.peek(Token![mut]) && input.peek2(Token![self]) | |
| { | |
| input.parse::<Token![mut]>()?; | |
| input.parse::<Token![self]>()?; | |
| None | |
| } else if has_mut_self && name.is_none() { | |
| input.parse::<Token![self]>()?; | |
| None | |
| } else { | |
| Some(input.parse()?) | |
| }; | |
| let ty = match ty { | |
| Some(ty) if !has_mut_self => ty, | |
| _ => { | |
| name = None; | |
| Type::Verbatim(verbatim::between(&begin, input)) | |
| } | |
| }; | |
| Ok(BareFnArg { attrs, name, ty }) | |
| } | |
| fn parse_bare_variadic(input: ParseStream, attrs: Vec<Attribute>) -> Result<BareVariadic> { | |
| Ok(BareVariadic { | |
| attrs, | |
| name: if input.peek(Ident) || input.peek(Token![_]) { | |
| let name = input.call(Ident::parse_any)?; | |
| let colon: Token![:] = input.parse()?; | |
| Some((name, colon)) | |
| } else { | |
| None | |
| }, | |
| dots: input.parse()?, | |
| comma: input.parse()?, | |
| }) | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for Abi { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| Ok(Abi { | |
| extern_token: input.parse()?, | |
| name: input.parse()?, | |
| }) | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "parsing")))] | |
| impl Parse for Option<Abi> { | |
| fn parse(input: ParseStream) -> Result<Self> { | |
| if input.peek(Token![extern]) { | |
| input.parse().map(Some) | |
| } else { | |
| Ok(None) | |
| } | |
| } | |
| } | |
| } | |
| #[cfg(feature = "printing")] | |
| mod printing { | |
| use crate::attr::FilterAttrs; | |
| use crate::path; | |
| use crate::path::printing::PathStyle; | |
| use crate::print::TokensOrDefault; | |
| use crate::ty::{ | |
| Abi, BareFnArg, BareVariadic, ReturnType, TypeArray, TypeBareFn, TypeGroup, TypeImplTrait, | |
| TypeInfer, TypeMacro, TypeNever, TypeParen, TypePath, TypePtr, TypeReference, TypeSlice, | |
| TypeTraitObject, TypeTuple, | |
| }; | |
| use proc_macro2::TokenStream; | |
| use quote::{ToTokens, TokenStreamExt}; | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeSlice { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.bracket_token.surround(tokens, |tokens| { | |
| self.elem.to_tokens(tokens); | |
| }); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| 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); | |
| }); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypePtr { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.star_token.to_tokens(tokens); | |
| match &self.mutability { | |
| Some(tok) => tok.to_tokens(tokens), | |
| None => { | |
| TokensOrDefault(&self.const_token).to_tokens(tokens); | |
| } | |
| } | |
| self.elem.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| 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); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| 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(variadic) = &self.variadic { | |
| if !self.inputs.empty_or_trailing() { | |
| let span = variadic.dots.spans[0]; | |
| Token.to_tokens(tokens); | |
| } | |
| variadic.to_tokens(tokens); | |
| } | |
| }); | |
| self.output.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeNever { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.bang_token.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeTuple { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.paren_token.surround(tokens, |tokens| { | |
| self.elems.to_tokens(tokens); | |
| // If we only have one argument, we need a trailing comma to | |
| // distinguish TypeTuple from TypeParen. | |
| if self.elems.len() == 1 && !self.elems.trailing_punct() { | |
| <Token![,]>::default().to_tokens(tokens); | |
| } | |
| }); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypePath { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| path::printing::print_qpath(tokens, &self.qself, &self.path, PathStyle::AsWritten); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeTraitObject { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.dyn_token.to_tokens(tokens); | |
| self.bounds.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeImplTrait { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.impl_token.to_tokens(tokens); | |
| self.bounds.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeGroup { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.group_token.surround(tokens, |tokens| { | |
| self.elem.to_tokens(tokens); | |
| }); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeParen { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.paren_token.surround(tokens, |tokens| { | |
| self.elem.to_tokens(tokens); | |
| }); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeInfer { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.underscore_token.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for TypeMacro { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.mac.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for ReturnType { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| match self { | |
| ReturnType::Default => {} | |
| ReturnType::Type(arrow, ty) => { | |
| arrow.to_tokens(tokens); | |
| ty.to_tokens(tokens); | |
| } | |
| } | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for BareFnArg { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| tokens.append_all(self.attrs.outer()); | |
| if let Some((name, colon)) = &self.name { | |
| name.to_tokens(tokens); | |
| colon.to_tokens(tokens); | |
| } | |
| self.ty.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for BareVariadic { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| tokens.append_all(self.attrs.outer()); | |
| if let Some((name, colon)) = &self.name { | |
| name.to_tokens(tokens); | |
| colon.to_tokens(tokens); | |
| } | |
| self.dots.to_tokens(tokens); | |
| self.comma.to_tokens(tokens); | |
| } | |
| } | |
| #[cfg_attr(docsrs, doc(cfg(feature = "printing")))] | |
| impl ToTokens for Abi { | |
| fn to_tokens(&self, tokens: &mut TokenStream) { | |
| self.extern_token.to_tokens(tokens); | |
| self.name.to_tokens(tokens); | |
| } | |
| } | |
| } |