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 syn::{punctuated::Punctuated, token, Attribute, Ident, Member, Path, Token, Type};
6
7use super::PatPath;
8
9ast_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
40ast_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
50ast_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
60ast_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
68ast_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
79ast_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
88ast_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
98ast_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
108ast_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
116ast_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
129mod 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.
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
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
193    fn pat_wild(input: ParseStream<'_>) -> Result<PatWild> {
194        Ok(PatWild { attrs: Vec::new(), underscore_token: input.parse()? })
195    }
196
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
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
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
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
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
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
315    fn is_unnamed(member: &Member) -> bool {
316        match member {
317            Member::Named(_) => false,
318            Member::Unnamed(_) => true,
319        }
320    }
321}
322
323mod 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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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 {
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}