use super::*;
use crate::punctuated::Punctuated;
#[cfg(feature = "extra-traits")]
use crate::tt::TokenStreamHelper;
use proc_macro2::TokenStream;
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
ast_enum_of_structs! {
/// A pattern in a local binding, function signature, match expression, or
/// various other places.
///
/// *This type is available if Syn is built with the `"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 Pat #manual_extra_traits {
/// 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),
#[doc(hidden)]
__Nonexhaustive,
}
}
ast_struct! {
/// A box pattern: `box v`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
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`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
pub struct PatLit {
pub attrs: Vec,
pub expr: Box,
}
}
ast_struct! {
/// A macro in pattern position.
///
/// *This type is available if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
pub struct PatPath {
pub attrs: Vec,
pub qself: Option,
pub path: Path,
}
}
ast_struct! {
/// A range pattern: `1..=2`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
pub struct PatTupleStruct {
pub attrs: Vec,
pub path: Path,
pub pat: PatTuple,
}
}
ast_struct! {
/// A type ascription pattern: `foo: f64`.
///
/// *This type is available if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
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 if Syn is built with the `"full"` feature.*
pub struct FieldPat {
pub attrs: Vec,
pub member: Member,
pub colon_token: Option,
pub pat: Box,
}
}
#[cfg(feature = "extra-traits")]
impl Eq for Pat {}
#[cfg(feature = "extra-traits")]
impl PartialEq for Pat {
fn eq(&self, other: &Self) -> bool {
match (self, other) {
(Pat::Box(this), Pat::Box(other)) => this == other,
(Pat::Ident(this), Pat::Ident(other)) => this == other,
(Pat::Lit(this), Pat::Lit(other)) => this == other,
(Pat::Macro(this), Pat::Macro(other)) => this == other,
(Pat::Or(this), Pat::Or(other)) => this == other,
(Pat::Path(this), Pat::Path(other)) => this == other,
(Pat::Range(this), Pat::Range(other)) => this == other,
(Pat::Reference(this), Pat::Reference(other)) => this == other,
(Pat::Rest(this), Pat::Rest(other)) => this == other,
(Pat::Slice(this), Pat::Slice(other)) => this == other,
(Pat::Struct(this), Pat::Struct(other)) => this == other,
(Pat::Tuple(this), Pat::Tuple(other)) => this == other,
(Pat::TupleStruct(this), Pat::TupleStruct(other)) => this == other,
(Pat::Type(this), Pat::Type(other)) => this == other,
(Pat::Verbatim(this), Pat::Verbatim(other)) => {
TokenStreamHelper(this) == TokenStreamHelper(other)
}
(Pat::Wild(this), Pat::Wild(other)) => this == other,
_ => false,
}
}
}
#[cfg(feature = "extra-traits")]
impl Hash for Pat {
fn hash(&self, hash: &mut H)
where
H: Hasher,
{
match self {
Pat::Box(pat) => {
hash.write_u8(0);
pat.hash(hash);
}
Pat::Ident(pat) => {
hash.write_u8(1);
pat.hash(hash);
}
Pat::Lit(pat) => {
hash.write_u8(2);
pat.hash(hash);
}
Pat::Macro(pat) => {
hash.write_u8(3);
pat.hash(hash);
}
Pat::Or(pat) => {
hash.write_u8(4);
pat.hash(hash);
}
Pat::Path(pat) => {
hash.write_u8(5);
pat.hash(hash);
}
Pat::Range(pat) => {
hash.write_u8(6);
pat.hash(hash);
}
Pat::Reference(pat) => {
hash.write_u8(7);
pat.hash(hash);
}
Pat::Rest(pat) => {
hash.write_u8(8);
pat.hash(hash);
}
Pat::Slice(pat) => {
hash.write_u8(9);
pat.hash(hash);
}
Pat::Struct(pat) => {
hash.write_u8(10);
pat.hash(hash);
}
Pat::Tuple(pat) => {
hash.write_u8(11);
pat.hash(hash);
}
Pat::TupleStruct(pat) => {
hash.write_u8(12);
pat.hash(hash);
}
Pat::Type(pat) => {
hash.write_u8(13);
pat.hash(hash);
}
Pat::Verbatim(pat) => {
hash.write_u8(14);
TokenStreamHelper(pat).hash(hash);
}
Pat::Wild(pat) => {
hash.write_u8(15);
pat.hash(hash);
}
Pat::__Nonexhaustive => unreachable!(),
}
}
}
#[cfg(feature = "parsing")]
mod parsing {
use super::*;
use crate::ext::IdentExt;
use crate::parse::{Parse, ParseStream, Result};
use crate::path;
impl Parse for Pat {
fn parse(input: ParseStream) -> Result {
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![..])
&& !{
let ahead = input.fork();
ahead.parse::()?;
ahead.parse::()?;
ahead.is_empty() || ahead.peek(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![extern])
|| 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) {
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![...]) {
input.call(pat_rest).map(Pat::Rest)
} else {
Err(lookahead.error())
}
}
}
fn pat_path_or_macro_or_struct_or_range(input: ParseStream) -> Result {
let (qself, path) = path::parsing::qpath(input, true)?;
if input.peek(Token![..]) {
return pat_range(input, qself, path).map(Pat::Range);
}
if qself.is_some() {
return Ok(Pat::Path(PatPath {
attrs: Vec::new(),
qself,
path,
}));
}
if 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) {
pat_struct(input, path).map(Pat::Struct)
} else if input.peek(token::Paren) {
pat_tuple_struct(input, path).map(Pat::TupleStruct)
} else if input.peek(Token![..]) {
pat_range(input, qself, path).map(Pat::Range)
} 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(input: ParseStream, path: Path) -> Result {
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.peek(Token![,]) {
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,
})
}
impl Member {
fn is_unnamed(&self) -> bool {
match *self {
Member::Named(_) => false,
Member::Unnamed(_) => true,
}
}
}
fn field_pat(input: ParseStream) -> Result {
let attrs = input.call(Attribute::parse_outer)?;
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,
member,
colon_token: input.parse()?,
pat: input.parse()?,
});
}
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,
member: Member::Named(ident),
colon_token: None,
pat: Box::new(pat),
})
}
fn pat_range(input: ParseStream, qself: Option, path: Path) -> Result {
Ok(PatRange {
attrs: Vec::new(),
lo: Box::new(Expr::Path(ExprPath {
attrs: Vec::new(),
qself,
path,
})),
limits: input.parse()?,
hi: input.call(pat_lit_expr)?,
})
}
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: 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 {
Ok(PatReference {
attrs: Vec::new(),
and_token: input.parse()?,
mutability: input.parse()?,
pat: input.parse()?,
})
}
fn pat_lit_or_range(input: ParseStream) -> Result {
let lo = input.call(pat_lit_expr)?;
if input.peek(Token![..]) {
Ok(Pat::Range(PatRange {
attrs: Vec::new(),
lo,
limits: input.parse()?,
hi: input.call(pat_lit_expr)?,
}))
} else {
Ok(Pat::Lit(PatLit {
attrs: Vec::new(),
expr: lo,
}))
}
}
fn pat_lit_expr(input: ParseStream) -> Result> {
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![extern])
|| lookahead.peek(Token![crate])
{
Expr::Path(input.parse()?)
} else {
return Err(lookahead.error());
};
Ok(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: Pat = content.parse()?;
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_rest(input: ParseStream) -> Result {
Ok(PatRest {
attrs: Vec::new(),
dot2_token: input.parse()?,
})
}
}
#[cfg(feature = "printing")]
mod printing {
use super::*;
use proc_macro2::TokenStream;
use quote::{ToTokens, TokenStreamExt};
use crate::attr::FilterAttrs;
impl ToTokens for PatWild {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(self.attrs.outer());
self.underscore_token.to_tokens(tokens);
}
}
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);
}
}
}
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);
});
}
}
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);
}
}
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);
}
}
impl ToTokens for PatPath {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(self.attrs.outer());
private::print_path(tokens, &self.qself, &self.path);
}
}
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);
});
}
}
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);
}
}
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);
}
}
impl ToTokens for PatRest {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(self.attrs.outer());
self.dot2_token.to_tokens(tokens);
}
}
impl ToTokens for PatLit {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(self.attrs.outer());
self.expr.to_tokens(tokens);
}
}
impl ToTokens for PatRange {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(self.attrs.outer());
self.lo.to_tokens(tokens);
match &self.limits {
RangeLimits::HalfOpen(t) => t.to_tokens(tokens),
RangeLimits::Closed(t) => t.to_tokens(tokens),
}
self.hi.to_tokens(tokens);
}
}
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);
});
}
}
impl ToTokens for PatMacro {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append_all(self.attrs.outer());
self.mac.to_tokens(tokens);
}
}
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);
}
}
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);
}
}
}