| use syn::{punctuated::Punctuated, token, Attribute, Ident, Member, Path, Token, Type}; |
| |
| ast_enum_of_structs! { |
| /// A pattern in a local binding, function signature, match expression, or |
| /// various other places. |
| /// |
| /// # Syntax tree enum |
| /// |
| /// This type is a [syntax tree enum]. |
| /// |
| /// [syntax tree enum]: https://docs.rs/syn/1/syn/enum.Expr.html#syntax-tree-enums |
| pub enum Pat { |
| /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. |
| Ident(PatIdent), |
| |
| /// A path pattern like `Color::Red`. |
| Path(PatPath), |
| |
| /// A reference pattern: `&mut var`. |
| Reference(PatReference), |
| |
| /// A struct or struct variant pattern: `Variant { x, y, .. }`. |
| Struct(PatStruct), |
| |
| /// A tuple pattern: `(a, b)`. |
| Tuple(PatTuple), |
| |
| /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. |
| TupleStruct(PatTupleStruct), |
| |
| /// A type ascription pattern: `foo: f64`. |
| Type(PatType), |
| |
| /// A pattern that matches any value: `_`. |
| Wild(PatWild), |
| |
| #[doc(hidden)] |
| __Nonexhaustive, |
| } |
| } |
| |
| ast_struct! { |
| /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. |
| pub struct PatIdent { |
| pub attrs: Vec<Attribute>, |
| pub by_ref: Option<Token![ref]>, |
| pub mutability: Option<Token![mut]>, |
| pub ident: Ident, |
| } |
| } |
| |
| ast_struct! { |
| /// A path pattern like `Color::Red`. |
| pub struct PatPath { |
| pub attrs: Vec<Attribute>, |
| pub path: Path, |
| } |
| } |
| |
| ast_struct! { |
| /// A reference pattern: `&mut var`. |
| pub struct PatReference { |
| pub attrs: Vec<Attribute>, |
| pub and_token: Token![&], |
| pub mutability: Option<Token![mut]>, |
| pub pat: Box<Pat>, |
| } |
| } |
| |
| ast_struct! { |
| /// A struct or struct variant pattern: `Variant { x, y, .. }`. |
| pub struct PatStruct { |
| pub attrs: Vec<Attribute>, |
| pub path: Path, |
| pub brace_token: token::Brace, |
| pub fields: Punctuated<FieldPat, Token![,]>, |
| pub dot2_token: Option<Token![..]>, |
| } |
| } |
| |
| ast_struct! { |
| /// A tuple pattern: `(a, b)`. |
| pub struct PatTuple { |
| pub attrs: Vec<Attribute>, |
| pub paren_token: token::Paren, |
| pub elems: Punctuated<Pat, Token![,]>, |
| } |
| } |
| |
| ast_struct! { |
| /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. |
| pub struct PatTupleStruct { |
| pub attrs: Vec<Attribute>, |
| pub path: Path, |
| pub pat: PatTuple, |
| } |
| } |
| |
| ast_struct! { |
| /// A type ascription pattern: `foo: f64`. |
| pub struct PatType { |
| pub attrs: Vec<Attribute>, |
| pub pat: Box<Pat>, |
| pub colon_token: Token![:], |
| pub ty: Box<Type>, |
| } |
| } |
| |
| ast_struct! { |
| /// A pattern that matches any value: `_`. |
| pub struct PatWild { |
| pub attrs: Vec<Attribute>, |
| pub underscore_token: Token![_], |
| } |
| } |
| |
| ast_struct! { |
| /// A single field in a struct pattern. |
| /// |
| /// Patterns like the fields of Foo `{ x, ref y, ref mut z }` are treated |
| /// the same as `x: x, y: ref y, z: ref mut z` but there is no colon token. |
| pub struct FieldPat { |
| pub attrs: Vec<Attribute>, |
| pub member: Member, |
| pub colon_token: Option<Token![:]>, |
| pub pat: Box<Pat>, |
| } |
| } |
| |
| mod parsing { |
| use syn::{ |
| braced, |
| ext::IdentExt, |
| parenthesized, |
| parse::{Parse, ParseStream, Result}, |
| punctuated::Punctuated, |
| token, Attribute, Ident, Member, Path, Token, |
| }; |
| |
| use super::{ |
| FieldPat, Pat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, |
| PatWild, |
| }; |
| use crate::path; |
| |
| impl Parse for Pat { |
| fn parse(input: ParseStream<'_>) -> Result<Self> { |
| let lookahead = input.lookahead1(); |
| if { |
| let ahead = input.fork(); |
| ahead.parse::<Option<Ident>>()?.is_some() |
| && (ahead.peek(Token![::]) |
| || ahead.peek(token::Brace) |
| || ahead.peek(token::Paren)) |
| } || { |
| let ahead = input.fork(); |
| ahead.parse::<Option<Token![self]>>()?.is_some() && ahead.peek(Token![::]) |
| } || lookahead.peek(Token![::]) |
| || lookahead.peek(Token![<]) |
| || input.peek(Token![Self]) |
| || input.peek(Token![super]) |
| || input.peek(Token![extern]) |
| || input.peek(Token![crate]) |
| { |
| pat_path_or_struct(input) |
| } else if lookahead.peek(Token![_]) { |
| input.call(pat_wild).map(Pat::Wild) |
| } else if lookahead.peek(Token![ref]) |
| || lookahead.peek(Token![mut]) |
| || input.peek(Token![self]) |
| || input.peek(Ident) |
| { |
| input.call(pat_ident).map(Pat::Ident) |
| } else if lookahead.peek(Token![&]) { |
| input.call(pat_reference).map(Pat::Reference) |
| } else if lookahead.peek(token::Paren) { |
| input.call(pat_tuple).map(Pat::Tuple) |
| } else { |
| Err(lookahead.error()) |
| } |
| } |
| } |
| |
| fn pat_path_or_struct(input: ParseStream<'_>) -> Result<Pat> { |
| let path = path::parse_path(input)?; |
| |
| if input.peek(token::Brace) { |
| pat_struct(input, path).map(Pat::Struct) |
| } else if input.peek(token::Paren) { |
| pat_tuple_struct(input, path).map(Pat::TupleStruct) |
| } else { |
| Ok(Pat::Path(PatPath { attrs: Vec::new(), path })) |
| } |
| } |
| |
| fn pat_wild(input: ParseStream<'_>) -> Result<PatWild> { |
| Ok(PatWild { attrs: Vec::new(), underscore_token: input.parse()? }) |
| } |
| |
| fn pat_ident(input: ParseStream<'_>) -> Result<PatIdent> { |
| Ok(PatIdent { |
| attrs: Vec::new(), |
| by_ref: input.parse()?, |
| mutability: input.parse()?, |
| ident: input.call(Ident::parse_any)?, |
| }) |
| } |
| |
| fn pat_tuple_struct(input: ParseStream<'_>, path: Path) -> Result<PatTupleStruct> { |
| Ok(PatTupleStruct { attrs: Vec::new(), path, pat: input.call(pat_tuple)? }) |
| } |
| |
| fn pat_struct(input: ParseStream<'_>, path: Path) -> Result<PatStruct> { |
| let content; |
| let brace_token = braced!(content in input); |
| |
| let mut fields = Punctuated::new(); |
| while !content.is_empty() && !content.peek(Token![..]) { |
| let value = content.call(field_pat)?; |
| fields.push_value(value); |
| if content.is_empty() { |
| break; |
| } |
| let punct: Token![,] = content.parse()?; |
| fields.push_punct(punct); |
| } |
| |
| let dot2_token = if fields.empty_or_trailing() && content.peek(Token![..]) { |
| Some(content.parse()?) |
| } else { |
| None |
| }; |
| |
| Ok(PatStruct { attrs: Vec::new(), path, brace_token, fields, dot2_token }) |
| } |
| |
| fn field_pat(input: ParseStream<'_>) -> Result<FieldPat> { |
| let attrs = input.call(Attribute::parse_outer)?; |
| let boxed: Option<Token![box]> = input.parse()?; |
| let by_ref: Option<Token![ref]> = input.parse()?; |
| let mutability: Option<Token![mut]> = input.parse()?; |
| let member: Member = input.parse()?; |
| |
| if boxed.is_none() && by_ref.is_none() && mutability.is_none() && input.peek(Token![:]) |
| || is_unnamed(&member) |
| { |
| return Ok(FieldPat { |
| attrs, |
| member, |
| colon_token: input.parse()?, |
| pat: input.parse()?, |
| }); |
| } |
| |
| let ident = match member { |
| Member::Named(ident) => ident, |
| Member::Unnamed(_) => unreachable!(), |
| }; |
| |
| let pat = |
| Pat::Ident(PatIdent { attrs: Vec::new(), by_ref, mutability, ident: ident.clone() }); |
| |
| Ok(FieldPat { attrs, member: Member::Named(ident), colon_token: None, pat: Box::new(pat) }) |
| } |
| |
| fn pat_tuple(input: ParseStream<'_>) -> Result<PatTuple> { |
| let content; |
| let paren_token = parenthesized!(content in input); |
| |
| let mut elems = Punctuated::new(); |
| while !content.is_empty() { |
| let value: Pat = content.parse()?; |
| elems.push_value(value); |
| if content.is_empty() { |
| break; |
| } |
| let punct = content.parse()?; |
| elems.push_punct(punct); |
| } |
| |
| Ok(PatTuple { attrs: Vec::new(), paren_token, elems }) |
| } |
| |
| fn pat_reference(input: ParseStream<'_>) -> Result<PatReference> { |
| Ok(PatReference { |
| attrs: Vec::new(), |
| and_token: input.parse()?, |
| mutability: input.parse()?, |
| pat: input.parse()?, |
| }) |
| } |
| |
| fn is_unnamed(member: &Member) -> bool { |
| match member { |
| Member::Named(_) => false, |
| Member::Unnamed(_) => true, |
| } |
| } |
| } |
| |
| mod printing { |
| use proc_macro2::TokenStream; |
| use quote::{ToTokens, TokenStreamExt}; |
| use syn::Token; |
| |
| use super::{ |
| FieldPat, PatIdent, PatPath, PatReference, PatStruct, PatTuple, PatTupleStruct, PatType, |
| PatWild, |
| }; |
| |
| impl ToTokens for PatWild { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| self.underscore_token.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for PatIdent { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| self.by_ref.to_tokens(tokens); |
| self.mutability.to_tokens(tokens); |
| self.ident.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for PatStruct { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| self.path.to_tokens(tokens); |
| self.brace_token.surround(tokens, |tokens| { |
| self.fields.to_tokens(tokens); |
| // NOTE: We need a comma before the dot2 token if it is present. |
| if !self.fields.empty_or_trailing() && self.dot2_token.is_some() { |
| <Token![,]>::default().to_tokens(tokens); |
| } |
| self.dot2_token.to_tokens(tokens); |
| }); |
| } |
| } |
| |
| impl ToTokens for PatTupleStruct { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| self.path.to_tokens(tokens); |
| self.pat.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for PatType { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| tokens.append_all(&self.attrs); |
| self.pat.to_tokens(tokens); |
| self.colon_token.to_tokens(tokens); |
| self.ty.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for PatPath { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| self.path.to_tokens(tokens) |
| } |
| } |
| |
| impl ToTokens for PatTuple { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| self.paren_token.surround(tokens, |tokens| { |
| self.elems.to_tokens(tokens); |
| }); |
| } |
| } |
| |
| impl ToTokens for PatReference { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| self.and_token.to_tokens(tokens); |
| self.mutability.to_tokens(tokens); |
| self.pat.to_tokens(tokens); |
| } |
| } |
| |
| impl ToTokens for FieldPat { |
| fn to_tokens(&self, tokens: &mut TokenStream) { |
| if let Some(colon_token) = &self.colon_token { |
| self.member.to_tokens(tokens); |
| colon_token.to_tokens(tokens); |
| } |
| self.pat.to_tokens(tokens); |
| } |
| } |
| } |