use super::*; use crate::punctuated::Punctuated; use proc_macro2::TokenStream; ast_enum_of_structs! { /// A pattern in a local binding, function signature, match expression, or /// various other places. /// /// *This type is available only if Syn is built with the `"full"` feature.* /// /// # Syntax tree enum /// /// This type is a [syntax tree enum]. /// /// [syntax tree enum]: Expr#syntax-tree-enums #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] #[cfg_attr(not(syn_no_non_exhaustive), non_exhaustive)] pub enum Pat { /// A box pattern: `box v`. Box(PatBox), /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. Ident(PatIdent), /// A literal pattern: `0`. /// /// This holds an `Expr` rather than a `Lit` because negative numbers /// are represented as an `Expr::Unary`. Lit(PatLit), /// A macro in pattern position. Macro(PatMacro), /// A pattern that matches any one of a set of cases. Or(PatOr), /// A path pattern like `Color::Red`, optionally qualified with a /// self-type. /// /// Unqualified path patterns can legally refer to variants, structs, /// constants or associated constants. Qualified path patterns like /// `::B::C` and `::B::C` can only legally refer to /// associated constants. Path(PatPath), /// A range pattern: `1..=2`. Range(PatRange), /// A reference pattern: `&mut var`. Reference(PatReference), /// The dots in a tuple or slice pattern: `[0, 1, ..]` Rest(PatRest), /// A dynamically sized slice pattern: `[a, b, ref i @ .., y, z]`. Slice(PatSlice), /// 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), /// Tokens in pattern position not interpreted by Syn. Verbatim(TokenStream), /// A pattern that matches any value: `_`. Wild(PatWild), // Not public API. // // For testing exhaustiveness in downstream code, use the following idiom: // // match pat { // Pat::Box(pat) => {...} // Pat::Ident(pat) => {...} // ... // Pat::Wild(pat) => {...} // // #[cfg_attr(test, deny(non_exhaustive_omitted_patterns))] // _ => { /* 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. #[cfg(syn_no_non_exhaustive)] #[doc(hidden)] __NonExhaustive, } } ast_struct! { /// A box pattern: `box v`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatBox { pub attrs: Vec, pub box_token: Token![box], pub pat: Box, } } ast_struct! { /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. /// /// It may also be a unit struct or struct variant (e.g. `None`), or a /// constant; these cannot be distinguished syntactically. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatIdent { pub attrs: Vec, pub by_ref: Option, pub mutability: Option, pub ident: Ident, pub subpat: Option<(Token![@], Box)>, } } ast_struct! { /// A literal pattern: `0`. /// /// This holds an `Expr` rather than a `Lit` because negative numbers /// are represented as an `Expr::Unary`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatLit { pub attrs: Vec, pub expr: Box, } } ast_struct! { /// A macro in pattern position. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatMacro { pub attrs: Vec, pub mac: Macro, } } ast_struct! { /// A pattern that matches any one of a set of cases. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatOr { pub attrs: Vec, pub leading_vert: Option, pub cases: Punctuated, } } ast_struct! { /// A path pattern like `Color::Red`, optionally qualified with a /// self-type. /// /// Unqualified path patterns can legally refer to variants, structs, /// constants or associated constants. Qualified path patterns like /// `::B::C` and `::B::C` can only legally refer to /// associated constants. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatPath { pub attrs: Vec, pub qself: Option, pub path: Path, } } ast_struct! { /// A range pattern: `1..=2`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatRange { pub attrs: Vec, pub lo: Box, pub limits: RangeLimits, pub hi: Box, } } ast_struct! { /// A reference pattern: `&mut var`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatReference { pub attrs: Vec, pub and_token: Token![&], pub mutability: Option, pub pat: Box, } } ast_struct! { /// The dots in a tuple or slice pattern: `[0, 1, ..]` /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatRest { pub attrs: Vec, pub dot2_token: Token![..], } } ast_struct! { /// A dynamically sized slice pattern: `[a, b, ref i @ .., y, z]`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatSlice { pub attrs: Vec, pub bracket_token: token::Bracket, pub elems: Punctuated, } } ast_struct! { /// A struct or struct variant pattern: `Variant { x, y, .. }`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatStruct { pub attrs: Vec, pub path: Path, pub brace_token: token::Brace, pub fields: Punctuated, pub dot2_token: Option, } } ast_struct! { /// A tuple pattern: `(a, b)`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatTuple { pub attrs: Vec, pub paren_token: token::Paren, pub elems: Punctuated, } } ast_struct! { /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatTupleStruct { pub attrs: Vec, pub path: Path, pub pat: PatTuple, } } ast_struct! { /// A type ascription pattern: `foo: f64`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatType { pub attrs: Vec, pub pat: Box, pub colon_token: Token![:], pub ty: Box, } } ast_struct! { /// A pattern that matches any value: `_`. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatWild { pub attrs: Vec, 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. /// /// *This type is available only if Syn is built with the `"full"` feature.* #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct FieldPat { pub attrs: Vec, pub member: Member, pub colon_token: Option, pub pat: Box, } } #[cfg(feature = "parsing")] pub mod parsing { use super::*; use crate::ext::IdentExt; use crate::parse::{Parse, ParseBuffer, ParseStream, Result}; use crate::path; #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for Pat { fn parse(input: ParseStream) -> Result { let begin = input.fork(); let lookahead = input.lookahead1(); if { let ahead = input.fork(); ahead.parse::>()?.is_some() && (ahead.peek(Token![::]) || ahead.peek(Token![!]) || ahead.peek(token::Brace) || ahead.peek(token::Paren) || ahead.peek(Token![..]) && ahead.parse::().is_ok() && !(ahead.is_empty() || ahead.peek(Token![,]))) } || { let ahead = input.fork(); ahead.parse::>()?.is_some() && ahead.peek(Token![::]) } || lookahead.peek(Token![::]) || lookahead.peek(Token![<]) || input.peek(Token![Self]) || input.peek(Token![super]) || input.peek(Token![crate]) { pat_path_or_macro_or_struct_or_range(input) } else if lookahead.peek(Token![_]) { input.call(pat_wild).map(Pat::Wild) } else if input.peek(Token![box]) { input.call(pat_box).map(Pat::Box) } else if input.peek(Token![-]) || lookahead.peek(Lit) || lookahead.peek(Token![const]) { pat_lit_or_range(input) } 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 if lookahead.peek(token::Bracket) { input.call(pat_slice).map(Pat::Slice) } else if lookahead.peek(Token![..]) && !input.peek(Token![...]) { pat_range_half_open(input, begin) } else if lookahead.peek(Token![const]) { input.call(pat_const).map(Pat::Verbatim) } else { Err(lookahead.error()) } } } fn pat_path_or_macro_or_struct_or_range(input: ParseStream) -> Result { let begin = input.fork(); let (qself, path) = path::parsing::qpath(input, true)?; if qself.is_none() && input.peek(Token![!]) && !input.peek(Token![!=]) { let mut contains_arguments = false; for segment in &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, tokens) = mac::parse_delimiter(input)?; return Ok(Pat::Macro(PatMacro { attrs: Vec::new(), mac: Macro { path, bang_token, delimiter, tokens, }, })); } } if input.peek(token::Brace) { let pat = pat_struct(begin.fork(), input, path)?; if qself.is_some() { Ok(Pat::Verbatim(verbatim::between(begin, input))) } else { Ok(pat) } } else if input.peek(token::Paren) { let pat = pat_tuple_struct(input, path)?; if qself.is_some() { Ok(Pat::Verbatim(verbatim::between(begin, input))) } else { Ok(Pat::TupleStruct(pat)) } } else if input.peek(Token![..]) { pat_range(input, begin, qself, path) } else { Ok(Pat::Path(PatPath { attrs: Vec::new(), qself, path, })) } } fn pat_wild(input: ParseStream) -> Result { Ok(PatWild { attrs: Vec::new(), underscore_token: input.parse()?, }) } fn pat_box(input: ParseStream) -> Result { Ok(PatBox { attrs: Vec::new(), box_token: input.parse()?, pat: input.parse()?, }) } fn pat_ident(input: ParseStream) -> Result { Ok(PatIdent { attrs: Vec::new(), by_ref: input.parse()?, mutability: input.parse()?, ident: input.call(Ident::parse_any)?, subpat: { if input.peek(Token![@]) { let at_token: Token![@] = input.parse()?; let subpat: Pat = input.parse()?; Some((at_token, Box::new(subpat))) } else { None } }, }) } fn pat_tuple_struct(input: ParseStream, path: Path) -> Result { Ok(PatTupleStruct { attrs: Vec::new(), path, pat: input.call(pat_tuple)?, }) } fn pat_struct(begin: ParseBuffer, input: ParseStream, path: Path) -> Result { let content; let brace_token = braced!(content in input); let mut fields = Punctuated::new(); let mut dot2_token = None; while !content.is_empty() { let attrs = content.call(Attribute::parse_outer)?; if content.peek(Token![..]) { dot2_token = Some(content.parse()?); if !attrs.is_empty() { return Ok(Pat::Verbatim(verbatim::between(begin, input))); } break; } let mut value = content.call(field_pat)?; value.attrs = attrs; fields.push_value(value); if content.is_empty() { break; } let punct: Token![,] = content.parse()?; fields.push_punct(punct); } Ok(Pat::Struct(PatStruct { attrs: Vec::new(), path, brace_token, fields, dot2_token, })) } impl Member { fn is_unnamed(&self) -> bool { match *self { Member::Named(_) => false, Member::Unnamed(_) => true, } } } fn field_pat(input: ParseStream) -> Result { let boxed: Option = input.parse()?; let by_ref: Option = input.parse()?; let mutability: Option = input.parse()?; let member: Member = input.parse()?; if boxed.is_none() && by_ref.is_none() && mutability.is_none() && input.peek(Token![:]) || member.is_unnamed() { return Ok(FieldPat { attrs: Vec::new(), member, colon_token: input.parse()?, pat: Box::new(multi_pat_with_leading_vert(input)?), }); } let ident = match member { Member::Named(ident) => ident, Member::Unnamed(_) => unreachable!(), }; let mut pat = Pat::Ident(PatIdent { attrs: Vec::new(), by_ref, mutability, ident: ident.clone(), subpat: None, }); if let Some(boxed) = boxed { pat = Pat::Box(PatBox { attrs: Vec::new(), box_token: boxed, pat: Box::new(pat), }); } Ok(FieldPat { attrs: Vec::new(), member: Member::Named(ident), colon_token: None, pat: Box::new(pat), }) } fn pat_range( input: ParseStream, begin: ParseBuffer, qself: Option, path: Path, ) -> Result { let limits: RangeLimits = input.parse()?; let hi = input.call(pat_lit_expr)?; if let Some(hi) = hi { Ok(Pat::Range(PatRange { attrs: Vec::new(), lo: Box::new(Expr::Path(ExprPath { attrs: Vec::new(), qself, path, })), limits, hi, })) } else { Ok(Pat::Verbatim(verbatim::between(begin, input))) } } fn pat_range_half_open(input: ParseStream, begin: ParseBuffer) -> Result { let limits: RangeLimits = input.parse()?; let hi = input.call(pat_lit_expr)?; if hi.is_some() { Ok(Pat::Verbatim(verbatim::between(begin, input))) } else { match limits { RangeLimits::HalfOpen(dot2_token) => Ok(Pat::Rest(PatRest { attrs: Vec::new(), dot2_token, })), RangeLimits::Closed(_) => Err(input.error("expected range upper bound")), } } } fn pat_tuple(input: ParseStream) -> Result { let content; let paren_token = parenthesized!(content in input); let mut elems = Punctuated::new(); while !content.is_empty() { let value = multi_pat_with_leading_vert(&content)?; 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 { Ok(PatReference { attrs: Vec::new(), and_token: input.parse()?, mutability: input.parse()?, pat: input.parse()?, }) } fn pat_lit_or_range(input: ParseStream) -> Result { let begin = input.fork(); let lo = input.call(pat_lit_expr)?.unwrap(); if input.peek(Token![..]) { let limits: RangeLimits = input.parse()?; let hi = input.call(pat_lit_expr)?; if let Some(hi) = hi { Ok(Pat::Range(PatRange { attrs: Vec::new(), lo, limits, hi, })) } else { Ok(Pat::Verbatim(verbatim::between(begin, input))) } } else if let Expr::Verbatim(verbatim) = *lo { Ok(Pat::Verbatim(verbatim)) } else { Ok(Pat::Lit(PatLit { attrs: Vec::new(), expr: lo, })) } } fn pat_lit_expr(input: ParseStream) -> Result>> { if input.is_empty() || input.peek(Token![|]) || input.peek(Token![=]) || input.peek(Token![:]) && !input.peek(Token![::]) || input.peek(Token![,]) || input.peek(Token![;]) { return Ok(None); } let neg: Option = input.parse()?; let lookahead = input.lookahead1(); let expr = if lookahead.peek(Lit) { Expr::Lit(input.parse()?) } else if lookahead.peek(Ident) || lookahead.peek(Token![::]) || lookahead.peek(Token![<]) || lookahead.peek(Token![self]) || lookahead.peek(Token![Self]) || lookahead.peek(Token![super]) || lookahead.peek(Token![crate]) { Expr::Path(input.parse()?) } else if lookahead.peek(Token![const]) { Expr::Verbatim(input.call(expr::parsing::expr_const)?) } else { return Err(lookahead.error()); }; Ok(Some(Box::new(if let Some(neg) = neg { Expr::Unary(ExprUnary { attrs: Vec::new(), op: UnOp::Neg(neg), expr: Box::new(expr), }) } else { expr }))) } fn pat_slice(input: ParseStream) -> Result { let content; let bracket_token = bracketed!(content in input); let mut elems = Punctuated::new(); while !content.is_empty() { let value = multi_pat_with_leading_vert(&content)?; elems.push_value(value); if content.is_empty() { break; } let punct = content.parse()?; elems.push_punct(punct); } Ok(PatSlice { attrs: Vec::new(), bracket_token, elems, }) } fn pat_const(input: ParseStream) -> Result { let begin = input.fork(); input.parse::()?; let content; braced!(content in input); content.call(Attribute::parse_inner)?; content.call(Block::parse_within)?; Ok(verbatim::between(begin, input)) } pub fn multi_pat(input: ParseStream) -> Result { multi_pat_impl(input, None) } pub fn multi_pat_with_leading_vert(input: ParseStream) -> Result { let leading_vert: Option = input.parse()?; multi_pat_impl(input, leading_vert) } fn multi_pat_impl(input: ParseStream, leading_vert: Option) -> Result { let mut pat: Pat = input.parse()?; if leading_vert.is_some() || input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=]) { let mut cases = Punctuated::new(); cases.push_value(pat); while input.peek(Token![|]) && !input.peek(Token![||]) && !input.peek(Token![|=]) { let punct = input.parse()?; cases.push_punct(punct); let pat: Pat = input.parse()?; cases.push_value(pat); } pat = Pat::Or(PatOr { attrs: Vec::new(), leading_vert, cases, }); } Ok(pat) } } #[cfg(feature = "printing")] mod printing { use super::*; use crate::attr::FilterAttrs; use proc_macro2::TokenStream; use quote::{ToTokens, TokenStreamExt}; #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatWild { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.underscore_token.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatIdent { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.by_ref.to_tokens(tokens); self.mutability.to_tokens(tokens); self.ident.to_tokens(tokens); if let Some((at_token, subpat)) = &self.subpat { at_token.to_tokens(tokens); subpat.to_tokens(tokens); } } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatStruct { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); 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() { ::default().to_tokens(tokens); } self.dot2_token.to_tokens(tokens); }); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatTupleStruct { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.path.to_tokens(tokens); self.pat.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatType { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.pat.to_tokens(tokens); self.colon_token.to_tokens(tokens); self.ty.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatPath { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); path::printing::print_path(tokens, &self.qself, &self.path); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatTuple { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.paren_token.surround(tokens, |tokens| { self.elems.to_tokens(tokens); }); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatBox { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.box_token.to_tokens(tokens); self.pat.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatReference { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.and_token.to_tokens(tokens); self.mutability.to_tokens(tokens); self.pat.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatRest { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.dot2_token.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatLit { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.expr.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatRange { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.lo.to_tokens(tokens); self.limits.to_tokens(tokens); self.hi.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatSlice { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.bracket_token.surround(tokens, |tokens| { self.elems.to_tokens(tokens); }); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatMacro { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.mac.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for PatOr { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.leading_vert.to_tokens(tokens); self.cases.to_tokens(tokens); } } #[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))] impl ToTokens for FieldPat { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); if let Some(colon_token) = &self.colon_token { self.member.to_tokens(tokens); colon_token.to_tokens(tokens); } self.pat.to_tokens(tokens); } } }