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