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. /// /// # 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")))] #[non_exhaustive] pub enum Pat { /// A const block: `const { ... }`. Const(PatConst), /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. Ident(PatIdent), /// A literal pattern: `0`. Lit(PatLit), /// A macro in pattern position. Macro(PatMacro), /// A pattern that matches any one of a set of cases. Or(PatOr), /// A parenthesized pattern: `(A | B)`. Paren(PatParen), /// 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), // 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. } } 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. #[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 pattern that matches any one of a set of cases. #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatOr { pub attrs: Vec, pub leading_vert: Option, pub cases: Punctuated, } } ast_struct! { /// A parenthesized pattern: `(A | B)`. #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatParen { pub attrs: Vec, pub paren_token: token::Paren, pub pat: Box, } } ast_struct! { /// A reference pattern: `&mut var`. #[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, ..]`. #[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]`. #[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, .. }`. #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatStruct { pub attrs: Vec, pub qself: Option, pub path: Path, pub brace_token: token::Brace, pub fields: Punctuated, pub rest: Option, } } ast_struct! { /// A tuple pattern: `(a, b)`. #[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)`. #[cfg_attr(doc_cfg, doc(cfg(feature = "full")))] pub struct PatTupleStruct { pub attrs: Vec, pub qself: Option, pub path: Path, pub paren_token: token::Paren, pub elems: Punctuated, } } ast_struct! { /// A type ascription pattern: `foo: f64`. #[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: `_`. #[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. #[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(crate) mod parsing { use super::*; use crate::ext::IdentExt as _; use crate::parse::{Parse, ParseBuffer, ParseStream, Result}; use crate::path; #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Pat { /// Parse a pattern that does _not_ involve `|` at the top level. /// /// This parser matches the behavior of the `$:pat_param` macro_rules /// matcher, and on editions prior to Rust 2021, the behavior of /// `$:pat`. /// /// In Rust syntax, some examples of where this syntax would occur are /// in the argument pattern of functions and closures. Patterns using /// `|` are not allowed to occur in these positions. /// /// ```compile_fail /// fn f(Some(_) | None: Option) { /// let _ = |Some(_) | None: Option| {}; /// // ^^^^^^^^^^^^^^^^^^^^^^^^^??? :( /// } /// ``` /// /// ```console /// error: top-level or-patterns are not allowed in function parameters /// --> src/main.rs:1:6 /// | /// 1 | fn f(Some(_) | None: Option) { /// | ^^^^^^^^^^^^^^ help: wrap the pattern in parentheses: `(Some(_) | None)` /// ``` pub fn parse_single(input: ParseStream) -> Result { let begin = input.fork(); let lookahead = input.lookahead1(); if lookahead.peek(Ident) && (input.peek2(Token![::]) || input.peek2(Token![!]) || input.peek2(token::Brace) || input.peek2(token::Paren) || input.peek2(Token![..])) || input.peek(Token![self]) && input.peek2(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]) { pat_box(begin, input) } 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_paren_or_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) } else if lookahead.peek(Token![const]) { input.call(pat_const).map(Pat::Verbatim) } else { Err(lookahead.error()) } } /// Parse a pattern, possibly involving `|`, but not a leading `|`. pub fn parse_multi(input: ParseStream) -> Result { multi_pat_impl(input, None) } /// Parse a pattern, possibly involving `|`, possibly including a /// leading `|`. /// /// This parser matches the behavior of the Rust 2021 edition's `$:pat` /// macro_rules matcher. /// /// In Rust syntax, an example of where this syntax would occur is in /// the pattern of a `match` arm, where the language permits an optional /// leading `|`, although it is not idiomatic to write one there in /// handwritten code. /// /// ``` /// # let wat = None; /// match wat { /// | None | Some(false) => {} /// | Some(true) => {} /// } /// ``` /// /// The compiler accepts it only to facilitate some situations in /// macro-generated code where a macro author might need to write: /// /// ``` /// # macro_rules! doc { /// # ($value:expr, ($($conditions1:pat),*), ($($conditions2:pat),*), $then:expr) => { /// match $value { /// $(| $conditions1)* $(| $conditions2)* => $then /// } /// # }; /// # } /// # /// # doc!(true, (true), (false), {}); /// # doc!(true, (), (true, false), {}); /// # doc!(true, (true, false), (), {}); /// ``` /// /// Expressing the same thing correctly in the case that either one (but /// not both) of `$conditions1` and `$conditions2` might be empty, /// without leading `|`, is complex. /// /// Use [`Pat::parse_multi`] instead if you are not intending to support /// macro-generated macro input. pub fn parse_multi_with_leading_vert(input: ParseStream) -> Result { let leading_vert: Option = input.parse()?; multi_pat_impl(input, leading_vert) } } #[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))] impl Parse for PatType { fn parse(input: ParseStream) -> Result { Ok(PatType { attrs: Vec::new(), pat: Box::new(Pat::parse_single(input)?), colon_token: input.parse()?, ty: input.parse()?, }) } } fn multi_pat_impl(input: ParseStream, leading_vert: Option) -> Result { let mut pat = Pat::parse_single(input)?; 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::parse_single(input)?; cases.push_value(pat); } pat = Pat::Or(PatOr { attrs: Vec::new(), leading_vert, cases, }); } Ok(pat) } fn pat_path_or_macro_or_struct_or_range(input: ParseStream) -> Result { let (qself, path) = path::parsing::qpath(input, true)?; if qself.is_none() && input.peek(Token![!]) && !input.peek(Token![!=]) && path.is_mod_style() { let bang_token: Token![!] = input.parse()?; let (delimiter, tokens) = mac::parse_delimiter(input)?; return Ok(Pat::Macro(ExprMacro { attrs: Vec::new(), mac: Macro { path, bang_token, delimiter, tokens, }, })); } if input.peek(token::Brace) { pat_struct(input, qself, path).map(Pat::Struct) } else if input.peek(token::Paren) { pat_tuple_struct(input, qself, path).map(Pat::TupleStruct) } else if input.peek(Token![..]) { pat_range(input, qself, path) } else { Ok(Pat::Path(ExprPath { attrs: Vec::new(), qself, path, })) } } fn pat_wild(input: ParseStream) -> Result { Ok(PatWild { attrs: Vec::new(), underscore_token: input.parse()?, }) } fn pat_box(begin: ParseBuffer, input: ParseStream) -> Result { input.parse::()?; Pat::parse_single(input)?; Ok(Pat::Verbatim(verbatim::between(&begin, input))) } 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::parse_single(input)?; Some((at_token, Box::new(subpat))) } else { None } }, }) } fn pat_tuple_struct( input: ParseStream, qself: Option, path: Path, ) -> Result { let content; let paren_token = parenthesized!(content in input); let mut elems = Punctuated::new(); while !content.is_empty() { let value = Pat::parse_multi_with_leading_vert(&content)?; elems.push_value(value); if content.is_empty() { break; } let punct = content.parse()?; elems.push_punct(punct); } Ok(PatTupleStruct { attrs: Vec::new(), qself, path, paren_token, elems, }) } fn pat_struct(input: ParseStream, qself: Option, path: Path) -> Result { let content; let brace_token = braced!(content in input); let mut fields = Punctuated::new(); let mut rest = None; while !content.is_empty() { let attrs = content.call(Attribute::parse_outer)?; if content.peek(Token![..]) { rest = Some(PatRest { attrs, dot2_token: content.parse()?, }); 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(PatStruct { attrs: Vec::new(), qself, path, brace_token, fields, rest, }) } fn field_pat(input: ParseStream) -> Result { let begin = input.fork(); let boxed: Option = input.parse()?; let by_ref: Option = input.parse()?; let mutability: Option = input.parse()?; let member = if boxed.is_some() || by_ref.is_some() || mutability.is_some() { input.parse().map(Member::Named) } else { input.parse() }?; if boxed.is_none() && by_ref.is_none() && mutability.is_none() && input.peek(Token![:]) || !member.is_named() { return Ok(FieldPat { attrs: Vec::new(), member, colon_token: Some(input.parse()?), pat: Box::new(Pat::parse_multi_with_leading_vert(input)?), }); } let ident = match member { Member::Named(ident) => ident, Member::Unnamed(_) => unreachable!(), }; let pat = if boxed.is_some() { Pat::Verbatim(verbatim::between(&begin, input)) } else { Pat::Ident(PatIdent { attrs: Vec::new(), by_ref, mutability, ident: ident.clone(), subpat: None, }) }; Ok(FieldPat { attrs: Vec::new(), member: Member::Named(ident), colon_token: None, pat: Box::new(pat), }) } fn pat_range(input: ParseStream, qself: Option, path: Path) -> Result { let limits = RangeLimits::parse_obsolete(input)?; let end = input.call(pat_range_bound)?; if let (RangeLimits::Closed(_), None) = (&limits, &end) { return Err(input.error("expected range upper bound")); } Ok(Pat::Range(ExprRange { attrs: Vec::new(), start: Some(Box::new(Expr::Path(ExprPath { attrs: Vec::new(), qself, path, }))), limits, end: end.map(PatRangeBound::into_expr), })) } fn pat_range_half_open(input: ParseStream) -> Result { let limits: RangeLimits = input.parse()?; let end = input.call(pat_range_bound)?; if end.is_some() { Ok(Pat::Range(ExprRange { attrs: Vec::new(), start: None, limits, end: end.map(PatRangeBound::into_expr), })) } 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_paren_or_tuple(input: ParseStream) -> Result { let content; let paren_token = parenthesized!(content in input); let mut elems = Punctuated::new(); while !content.is_empty() { let value = Pat::parse_multi_with_leading_vert(&content)?; if content.is_empty() { if elems.is_empty() && !matches!(value, Pat::Rest(_)) { return Ok(Pat::Paren(PatParen { attrs: Vec::new(), paren_token, pat: Box::new(value), })); } elems.push_value(value); break; } elems.push_value(value); let punct = content.parse()?; elems.push_punct(punct); } Ok(Pat::Tuple(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: Box::new(Pat::parse_single(input)?), }) } fn pat_lit_or_range(input: ParseStream) -> Result { let start = input.call(pat_range_bound)?.unwrap(); if input.peek(Token![..]) { let limits = RangeLimits::parse_obsolete(input)?; let end = input.call(pat_range_bound)?; if let (RangeLimits::Closed(_), None) = (&limits, &end) { return Err(input.error("expected range upper bound")); } Ok(Pat::Range(ExprRange { attrs: Vec::new(), start: Some(start.into_expr()), limits, end: end.map(PatRangeBound::into_expr), })) } else { Ok(start.into_pat()) } } // Patterns that can appear on either side of a range pattern. enum PatRangeBound { Const(ExprConst), Lit(ExprLit), Path(ExprPath), } impl PatRangeBound { fn into_expr(self) -> Box { Box::new(match self { PatRangeBound::Const(pat) => Expr::Const(pat), PatRangeBound::Lit(pat) => Expr::Lit(pat), PatRangeBound::Path(pat) => Expr::Path(pat), }) } fn into_pat(self) -> Pat { match self { PatRangeBound::Const(pat) => Pat::Const(pat), PatRangeBound::Lit(pat) => Pat::Lit(pat), PatRangeBound::Path(pat) => Pat::Path(pat), } } } fn pat_range_bound(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![;]) || input.peek(Token![if]) { return Ok(None); } let lookahead = input.lookahead1(); let expr = if lookahead.peek(Lit) { PatRangeBound::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]) { PatRangeBound::Path(input.parse()?) } else if lookahead.peek(Token![const]) { PatRangeBound::Const(input.parse()?) } else { return Err(lookahead.error()); }; Ok(Some(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 = Pat::parse_multi_with_leading_vert(&content)?; match value { Pat::Range(pat) if pat.start.is_none() || pat.end.is_none() => { let (start, end) = match pat.limits { RangeLimits::HalfOpen(dot_dot) => (dot_dot.spans[0], dot_dot.spans[1]), RangeLimits::Closed(dot_dot_eq) => { (dot_dot_eq.spans[0], dot_dot_eq.spans[2]) } }; let msg = "range pattern is not allowed unparenthesized inside slice pattern"; return Err(error::new2(start, end, msg)); } _ => {} } 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)) } } #[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 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 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 PatParen { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); self.paren_token.surround(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 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 PatStruct { fn to_tokens(&self, tokens: &mut TokenStream) { tokens.append_all(self.attrs.outer()); path::printing::print_path(tokens, &self.qself, &self.path); 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.rest.is_some() { ::default().to_tokens(tokens); } self.rest.to_tokens(tokens); }); } } #[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); // If there is only one element, a trailing comma is needed to // distinguish PatTuple from PatParen, unless this is `(..)` // which is a tuple pattern even without comma. if self.elems.len() == 1 && !self.elems.trailing_punct() && !matches!(self.elems[0], Pat::Rest { .. }) { ::default().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()); path::printing::print_path(tokens, &self.qself, &self.path); self.paren_token.surround(tokens, |tokens| { self.elems.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 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 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); } } }