1 // SPDX-License-Identifier: Apache-2.0 OR MIT 2 3 // Based on https://github.com/dtolnay/syn/blob/2.0.37/src/item.rs. 4 5 use syn::{punctuated::Punctuated, token, Attribute, Ident, Member, Path, Token, Type}; 6 7 use super::PatPath; 8 9 ast_enum_of_structs! { 10 /// A pattern in a local binding, function signature, match expression, or 11 /// various other places. 12 #[non_exhaustive] 13 pub enum Pat { 14 /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. 15 Ident(PatIdent), 16 17 /// A path pattern like `Color::Red`. 18 Path(PatPath), 19 20 /// A reference pattern: `&mut var`. 21 Reference(PatReference), 22 23 /// A struct or struct variant pattern: `Variant { x, y, .. }`. 24 Struct(PatStruct), 25 26 /// A tuple pattern: `(a, b)`. 27 Tuple(PatTuple), 28 29 /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. 30 TupleStruct(PatTupleStruct), 31 32 /// A type ascription pattern: `foo: f64`. 33 Type(PatType), 34 35 /// A pattern that matches any value: `_`. 36 Wild(PatWild), 37 } 38 } 39 40 ast_struct! { 41 /// A pattern that binds a new variable: `ref mut binding @ SUBPATTERN`. 42 pub struct PatIdent { 43 pub attrs: Vec<Attribute>, 44 pub by_ref: Option<Token![ref]>, 45 pub mutability: Option<Token![mut]>, 46 pub ident: Ident, 47 } 48 } 49 50 ast_struct! { 51 /// A reference pattern: `&mut var`. 52 pub struct PatReference { 53 pub attrs: Vec<Attribute>, 54 pub and_token: Token![&], 55 pub mutability: Option<Token![mut]>, 56 pub pat: Box<Pat>, 57 } 58 } 59 60 ast_struct! { 61 /// The dots in a tuple pattern: `[0, 1, ..]`. 62 pub struct PatRest { 63 pub attrs: Vec<Attribute>, 64 pub dot2_token: Token![..], 65 } 66 } 67 68 ast_struct! { 69 /// A struct or struct variant pattern: `Variant { x, y, .. }`. 70 pub struct PatStruct { 71 pub attrs: Vec<Attribute>, 72 pub path: Path, 73 pub brace_token: token::Brace, 74 pub fields: Punctuated<FieldPat, Token![,]>, 75 pub rest: Option<PatRest>, 76 } 77 } 78 79 ast_struct! { 80 /// A tuple pattern: `(a, b)`. 81 pub struct PatTuple { 82 pub attrs: Vec<Attribute>, 83 pub paren_token: token::Paren, 84 pub elems: Punctuated<Pat, Token![,]>, 85 } 86 } 87 88 ast_struct! { 89 /// A tuple struct or tuple variant pattern: `Variant(x, y, .., z)`. 90 pub struct PatTupleStruct { 91 pub attrs: Vec<Attribute>, 92 pub path: Path, 93 pub paren_token: token::Paren, 94 pub elems: Punctuated<Pat, Token![,]>, 95 } 96 } 97 98 ast_struct! { 99 /// A type ascription pattern: `foo: f64`. 100 pub struct PatType { 101 pub attrs: Vec<Attribute>, 102 pub pat: Box<Pat>, 103 pub colon_token: Token![:], 104 pub ty: Box<Type>, 105 } 106 } 107 108 ast_struct! { 109 /// A pattern that matches any value: `_`. 110 pub struct PatWild { 111 pub attrs: Vec<Attribute>, 112 pub underscore_token: Token![_], 113 } 114 } 115 116 ast_struct! { 117 /// A single field in a struct pattern. 118 /// 119 /// Patterns like the fields of Foo `{ x, ref y, ref mut z }` are treated 120 /// the same as `x: x, y: ref y, z: ref mut z` but there is no colon token. 121 pub struct FieldPat { 122 pub attrs: Vec<Attribute>, 123 pub member: Member, 124 pub colon_token: Option<Token![:]>, 125 pub pat: Box<Pat>, 126 } 127 } 128 129 mod parsing { 130 use syn::{ 131 braced, 132 ext::IdentExt, 133 parenthesized, 134 parse::{ParseStream, Result}, 135 punctuated::Punctuated, 136 token, Attribute, ExprPath, Ident, Member, Path, Token, 137 }; 138 139 use super::{ 140 FieldPat, Pat, PatIdent, PatReference, PatRest, PatStruct, PatTuple, PatTupleStruct, 141 PatWild, 142 }; 143 use crate::path; 144 145 impl Pat { 146 /// Parse a pattern that does _not_ involve `|` at the top level. parse_single(input: ParseStream<'_>) -> Result<Self>147 pub fn parse_single(input: ParseStream<'_>) -> Result<Self> { 148 let lookahead = input.lookahead1(); 149 if lookahead.peek(Ident) 150 && (input.peek2(Token![::]) 151 || input.peek2(Token![!]) 152 || input.peek2(token::Brace) 153 || input.peek2(token::Paren) 154 || input.peek2(Token![..])) 155 || input.peek(Token![self]) && input.peek2(Token![::]) 156 || lookahead.peek(Token![::]) 157 || lookahead.peek(Token![<]) 158 || input.peek(Token![Self]) 159 || input.peek(Token![super]) 160 || input.peek(Token![crate]) 161 { 162 pat_path_or_struct(input) 163 } else if lookahead.peek(Token![_]) { 164 input.call(pat_wild).map(Pat::Wild) 165 } else if lookahead.peek(Token![ref]) 166 || lookahead.peek(Token![mut]) 167 || input.peek(Token![self]) 168 || input.peek(Ident) 169 { 170 input.call(pat_ident).map(Pat::Ident) 171 } else if lookahead.peek(Token![&]) { 172 input.call(pat_reference).map(Pat::Reference) 173 } else if lookahead.peek(token::Paren) { 174 input.call(pat_paren_or_tuple) 175 } else { 176 Err(lookahead.error()) 177 } 178 } 179 } 180 pat_path_or_struct(input: ParseStream<'_>) -> Result<Pat>181 fn pat_path_or_struct(input: ParseStream<'_>) -> Result<Pat> { 182 let path = path::parse_path(input)?; 183 184 if input.peek(token::Brace) { 185 pat_struct(input, path).map(Pat::Struct) 186 } else if input.peek(token::Paren) { 187 pat_tuple_struct(input, path).map(Pat::TupleStruct) 188 } else { 189 Ok(Pat::Path(ExprPath { attrs: Vec::new(), qself: None, path })) 190 } 191 } 192 pat_wild(input: ParseStream<'_>) -> Result<PatWild>193 fn pat_wild(input: ParseStream<'_>) -> Result<PatWild> { 194 Ok(PatWild { attrs: Vec::new(), underscore_token: input.parse()? }) 195 } 196 pat_ident(input: ParseStream<'_>) -> Result<PatIdent>197 fn pat_ident(input: ParseStream<'_>) -> Result<PatIdent> { 198 Ok(PatIdent { 199 attrs: Vec::new(), 200 by_ref: input.parse()?, 201 mutability: input.parse()?, 202 ident: input.call(Ident::parse_any)?, 203 }) 204 } 205 pat_tuple_struct(input: ParseStream<'_>, path: Path) -> Result<PatTupleStruct>206 fn pat_tuple_struct(input: ParseStream<'_>, path: Path) -> Result<PatTupleStruct> { 207 let content; 208 let paren_token = parenthesized!(content in input); 209 210 let mut elems = Punctuated::new(); 211 while !content.is_empty() { 212 let value = Pat::parse_single(&content)?; 213 elems.push_value(value); 214 if content.is_empty() { 215 break; 216 } 217 let punct = content.parse()?; 218 elems.push_punct(punct); 219 } 220 221 Ok(PatTupleStruct { attrs: Vec::new(), path, paren_token, elems }) 222 } 223 pat_struct(input: ParseStream<'_>, path: Path) -> Result<PatStruct>224 fn pat_struct(input: ParseStream<'_>, path: Path) -> Result<PatStruct> { 225 let content; 226 let brace_token = braced!(content in input); 227 228 let mut fields = Punctuated::new(); 229 let mut rest = None; 230 while !content.is_empty() { 231 let attrs = content.call(Attribute::parse_outer)?; 232 if content.peek(Token![..]) { 233 rest = Some(PatRest { attrs, dot2_token: content.parse()? }); 234 break; 235 } 236 let mut value = content.call(field_pat)?; 237 value.attrs = attrs; 238 fields.push_value(value); 239 if content.is_empty() { 240 break; 241 } 242 let punct: Token![,] = content.parse()?; 243 fields.push_punct(punct); 244 } 245 246 Ok(PatStruct { attrs: Vec::new(), path, brace_token, fields, rest }) 247 } 248 field_pat(input: ParseStream<'_>) -> Result<FieldPat>249 fn field_pat(input: ParseStream<'_>) -> Result<FieldPat> { 250 let boxed: Option<Token![box]> = input.parse()?; 251 let by_ref: Option<Token![ref]> = input.parse()?; 252 let mutability: Option<Token![mut]> = input.parse()?; 253 254 let member = if boxed.is_some() || by_ref.is_some() || mutability.is_some() { 255 input.parse().map(Member::Named) 256 } else { 257 input.parse() 258 }?; 259 260 if boxed.is_none() && by_ref.is_none() && mutability.is_none() && input.peek(Token![:]) 261 || is_unnamed(&member) 262 { 263 return Ok(FieldPat { 264 attrs: Vec::new(), 265 member, 266 colon_token: Some(input.parse()?), 267 pat: Box::new(Pat::parse_single(input)?), 268 }); 269 } 270 271 let ident = match member { 272 Member::Named(ident) => ident, 273 Member::Unnamed(_) => unreachable!(), 274 }; 275 276 let pat = 277 Pat::Ident(PatIdent { attrs: Vec::new(), by_ref, mutability, ident: ident.clone() }); 278 279 Ok(FieldPat { 280 attrs: Vec::new(), 281 member: Member::Named(ident), 282 colon_token: None, 283 pat: Box::new(pat), 284 }) 285 } 286 pat_paren_or_tuple(input: ParseStream<'_>) -> Result<Pat>287 fn pat_paren_or_tuple(input: ParseStream<'_>) -> Result<Pat> { 288 let content; 289 let paren_token = parenthesized!(content in input); 290 291 let mut elems = Punctuated::new(); 292 while !content.is_empty() { 293 let value = Pat::parse_single(&content)?; 294 if content.is_empty() { 295 elems.push_value(value); 296 break; 297 } 298 elems.push_value(value); 299 let punct = content.parse()?; 300 elems.push_punct(punct); 301 } 302 303 Ok(Pat::Tuple(PatTuple { attrs: Vec::new(), paren_token, elems })) 304 } 305 pat_reference(input: ParseStream<'_>) -> Result<PatReference>306 fn pat_reference(input: ParseStream<'_>) -> Result<PatReference> { 307 Ok(PatReference { 308 attrs: Vec::new(), 309 and_token: input.parse()?, 310 mutability: input.parse()?, 311 pat: Box::new(Pat::parse_single(input)?), 312 }) 313 } 314 is_unnamed(member: &Member) -> bool315 fn is_unnamed(member: &Member) -> bool { 316 match member { 317 Member::Named(_) => false, 318 Member::Unnamed(_) => true, 319 } 320 } 321 } 322 323 mod printing { 324 use proc_macro2::TokenStream; 325 use quote::{ToTokens, TokenStreamExt}; 326 use syn::Token; 327 328 use super::{ 329 FieldPat, PatIdent, PatReference, PatRest, PatStruct, PatTuple, PatTupleStruct, PatType, 330 PatWild, 331 }; 332 333 impl ToTokens for PatIdent { to_tokens(&self, tokens: &mut TokenStream)334 fn to_tokens(&self, tokens: &mut TokenStream) { 335 tokens.append_all(&self.attrs); 336 self.by_ref.to_tokens(tokens); 337 self.mutability.to_tokens(tokens); 338 self.ident.to_tokens(tokens); 339 } 340 } 341 342 impl ToTokens for PatReference { to_tokens(&self, tokens: &mut TokenStream)343 fn to_tokens(&self, tokens: &mut TokenStream) { 344 tokens.append_all(&self.attrs); 345 self.and_token.to_tokens(tokens); 346 self.mutability.to_tokens(tokens); 347 self.pat.to_tokens(tokens); 348 } 349 } 350 351 impl ToTokens for PatRest { to_tokens(&self, tokens: &mut TokenStream)352 fn to_tokens(&self, tokens: &mut TokenStream) { 353 tokens.append_all(&self.attrs); 354 self.dot2_token.to_tokens(tokens); 355 } 356 } 357 358 impl ToTokens for PatStruct { to_tokens(&self, tokens: &mut TokenStream)359 fn to_tokens(&self, tokens: &mut TokenStream) { 360 tokens.append_all(&self.attrs); 361 self.path.to_tokens(tokens); 362 self.brace_token.surround(tokens, |tokens| { 363 self.fields.to_tokens(tokens); 364 // Note: We need a comma before the dot2 token if it is present. 365 if !self.fields.empty_or_trailing() && self.rest.is_some() { 366 <Token![,]>::default().to_tokens(tokens); 367 } 368 self.rest.to_tokens(tokens); 369 }); 370 } 371 } 372 373 impl ToTokens for PatTuple { to_tokens(&self, tokens: &mut TokenStream)374 fn to_tokens(&self, tokens: &mut TokenStream) { 375 tokens.append_all(&self.attrs); 376 self.paren_token.surround(tokens, |tokens| { 377 self.elems.to_tokens(tokens); 378 }); 379 } 380 } 381 382 impl ToTokens for PatTupleStruct { to_tokens(&self, tokens: &mut TokenStream)383 fn to_tokens(&self, tokens: &mut TokenStream) { 384 tokens.append_all(&self.attrs); 385 self.path.to_tokens(tokens); 386 self.paren_token.surround(tokens, |tokens| { 387 self.elems.to_tokens(tokens); 388 }); 389 } 390 } 391 392 impl ToTokens for PatType { to_tokens(&self, tokens: &mut TokenStream)393 fn to_tokens(&self, tokens: &mut TokenStream) { 394 tokens.append_all(&self.attrs); 395 self.pat.to_tokens(tokens); 396 self.colon_token.to_tokens(tokens); 397 self.ty.to_tokens(tokens); 398 } 399 } 400 401 impl ToTokens for PatWild { to_tokens(&self, tokens: &mut TokenStream)402 fn to_tokens(&self, tokens: &mut TokenStream) { 403 tokens.append_all(&self.attrs); 404 self.underscore_token.to_tokens(tokens); 405 } 406 } 407 408 impl ToTokens for FieldPat { to_tokens(&self, tokens: &mut TokenStream)409 fn to_tokens(&self, tokens: &mut TokenStream) { 410 tokens.append_all(&self.attrs); 411 if let Some(colon_token) = &self.colon_token { 412 self.member.to_tokens(tokens); 413 colon_token.to_tokens(tokens); 414 } 415 self.pat.to_tokens(tokens); 416 } 417 } 418 } 419