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)`.
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;
use crate::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)
}
}
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,
})
}
impl Member {
fn is_unnamed(&self) -> bool {
match self {
Member::Named(_) => false,
Member::Unnamed(_) => true,
}
}
}
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_unnamed()
{
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