Skip to main content

syn_mid/
pat.rs

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