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