parser/grammar/patterns.rs
1use super::*;
2
3pub(super) const PATTERN_FIRST: TokenSet =
4 expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[
5 T![box],
6 T![ref],
7 T![mut],
8 T![const],
9 T!['('],
10 T!['['],
11 T![&],
12 T![_],
13 T![-],
14 T![.],
15 ]));
16
17const PAT_TOP_FIRST: TokenSet = PATTERN_FIRST.union(TokenSet::new(&[T![|]]));
18
19/// Set of possible tokens at the start of a range pattern's end bound.
20const RANGE_PAT_END_FIRST: TokenSet =
21 expressions::LITERAL_FIRST.union(paths::PATH_FIRST).union(TokenSet::new(&[T![-], T![const]]));
22
23/// Parses a pattern list separated by pipes `|`.
24pub(crate) fn pattern(p: &mut Parser<'_>) {
25 pattern_r(p, PAT_RECOVERY_SET);
26}
27
28pub(crate) fn pattern_single(p: &mut Parser<'_>) {
29 pattern_single_r(p, PAT_RECOVERY_SET);
30}
31
32/// Parses a pattern list separated by pipes `|`
33/// using the given `recovery_set`.
34pub(super) fn pattern_top_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
35 pattern_r(p, recovery_set);
36}
37
38// test or_pattern
39// fn main() {
40// match () {
41// (_ | _) => (),
42// &(_ | _) => (),
43// (_ | _,) => (),
44// [_ | _,] => (),
45// }
46// }
47/// Parses a pattern list separated by pipes `|`, with no leading `|`,using the
48/// given `recovery_set`.
49fn pattern_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
50 let m = p.start();
51 let has_leading_pipe = p.eat(T![|]);
52
53 pattern_single_r(p, recovery_set);
54
55 if !p.at(T![|]) && !has_leading_pipe {
56 m.abandon(p);
57 return;
58 }
59 while p.eat(T![|]) {
60 pattern_single_r(p, recovery_set);
61 }
62 m.complete(p, OR_PAT);
63}
64
65fn pattern_single_r(p: &mut Parser<'_>, recovery_set: TokenSet) {
66 // test range_pat
67 // fn main() {
68 // match 92 {
69 // 0 ... 100 => (),
70 // 101 ..= 200 => (),
71 // 200 .. 301 => (),
72 // 302 .. => (),
73 // ..= 303 => (),
74 // }
75 //
76 // match Some(10 as u8) {
77 // Some(0) | None => (),
78 // Some(1..) => (),
79 // Some(..=2) => (),
80 // }
81 //
82 // match () {
83 // S { a: 0 } => (),
84 // S { a: 1.. } => (),
85 // S { a: ..=2 } => (),
86 // }
87 //
88 // match () {
89 // [0] => (),
90 // [1..] => (),
91 // [..=2] => (),
92 // }
93 //
94 // match (10 as u8, 5 as u8) {
95 // (0, _) => (),
96 // (1.., _) => (),
97 // (..=2, _) => (),
98 // }
99 // }
100
101 if p.at(T![..=]) {
102 let m = p.start();
103 p.bump(T![..=]);
104 atom_pat(p, recovery_set);
105 m.complete(p, RANGE_PAT);
106 return;
107 }
108
109 // test exclusive_range_pat
110 // fn main() {
111 // match 42 {
112 // ..0 => {}
113 // 1..2 => {}
114 // }
115 // }
116
117 // test dot_dot_pat
118 // fn main() {
119 // let .. = ();
120 // //
121 // // Tuples
122 // //
123 // let (a, ..) = ();
124 // let (a, ..,) = ();
125 // let Tuple(a, ..) = ();
126 // let Tuple(a, ..,) = ();
127 // let (.., ..) = ();
128 // let Tuple(.., ..) = ();
129 // let (.., a, ..) = ();
130 // let Tuple(.., a, ..) = ();
131 // //
132 // // Slices
133 // //
134 // let [..] = ();
135 // let [head, ..] = ();
136 // let [head, tail @ ..] = ();
137 // let [head, .., cons] = ();
138 // let [head, mid @ .., cons] = ();
139 // let [head, .., .., cons] = ();
140 // let [head, .., mid, tail @ ..] = ();
141 // let [head, .., mid, .., cons] = ();
142 // }
143 if p.at(T![..]) {
144 let m = p.start();
145 p.bump(T![..]);
146 if p.at_ts(RANGE_PAT_END_FIRST) {
147 atom_pat(p, recovery_set);
148 m.complete(p, RANGE_PAT);
149 } else {
150 m.complete(p, REST_PAT);
151 }
152 return;
153 }
154
155 if let Some(lhs) = atom_pat(p, recovery_set) {
156 for range_op in [T![...], T![..=], T![..]] {
157 if p.at(range_op) {
158 let m = lhs.precede(p);
159 p.bump(range_op);
160
161 // testing if we're at one of the following positions:
162 // `0 .. =>`
163 // ^
164 // `let 0 .. =`
165 // ^
166 // `let 0..: _ =`
167 // ^
168 // (1.., _)
169 // ^
170 // `Some(0 .. )`
171 // ^
172 // `S { t: 0.. }`
173 // ^
174 // `[0..]`
175 // ^
176 // `0 .. if`
177 // ^
178 if matches!(
179 p.current(),
180 T![=] | T![,] | T![:] | T![')'] | T!['}'] | T![']'] | T![if] | EOF
181 ) {
182 // test half_open_range_pat
183 // fn f() {
184 // let 0 .. = 1u32;
185 // let 0..: _ = 1u32;
186 //
187 // match 42 {
188 // 0 .. if true => (),
189 // _ => (),
190 // }
191 // }
192 } else {
193 atom_pat(p, recovery_set);
194 }
195 m.complete(p, RANGE_PAT);
196 return;
197 }
198 }
199 }
200}
201
202const PAT_RECOVERY_SET: TokenSet = TokenSet::new(&[
203 T![let],
204 T![if],
205 T![while],
206 T![loop],
207 T![match],
208 T![')'],
209 T![']'],
210 T!['}'],
211 T![,],
212 T![=],
213 T![&],
214]);
215
216fn atom_pat(p: &mut Parser<'_>, recovery_set: TokenSet) -> Option<CompletedMarker> {
217 let m = match p.current() {
218 T![box] => box_pat(p),
219 T![ref] | T![mut] => ident_pat(p, true),
220 T![const] => const_block_pat(p),
221 IDENT => match p.nth(1) {
222 // Checks the token after an IDENT to see if a pattern is a path (Struct { .. }) or macro
223 // (T![x]).
224 T!['('] | T!['{'] | T![!] => path_or_macro_pat(p),
225 T![:] if p.nth_at(1, T![::]) => path_or_macro_pat(p),
226 _ => ident_pat(p, true),
227 },
228
229 // test type_path_in_pattern
230 // fn main() { let <_>::Foo = (); }
231 _ if paths::is_path_start(p) => path_or_macro_pat(p),
232 _ if is_literal_pat_start(p) => literal_pat(p),
233
234 T![_] => wildcard_pat(p),
235 T![&] => ref_pat(p),
236 T!['('] => tuple_pat(p),
237 T!['['] => slice_pat(p),
238
239 _ => {
240 p.err_recover("expected pattern", recovery_set);
241 return None;
242 }
243 };
244
245 Some(m)
246}
247
248fn is_literal_pat_start(p: &Parser<'_>) -> bool {
249 p.at(T![-]) && (p.nth(1) == INT_NUMBER || p.nth(1) == FLOAT_NUMBER)
250 || p.at_ts(expressions::LITERAL_FIRST)
251}
252
253// test literal_pattern
254// fn main() {
255// match () {
256// -1 => (),
257// 92 => (),
258// 'c' => (),
259// "hello" => (),
260// }
261// }
262fn literal_pat(p: &mut Parser<'_>) -> CompletedMarker {
263 assert!(is_literal_pat_start(p));
264 let m = p.start();
265 p.eat(T![-]);
266 expressions::literal(p);
267 m.complete(p, LITERAL_PAT)
268}
269
270// test path_part
271// fn foo() {
272// let foo::Bar = ();
273// let ::Bar = ();
274// let Bar { .. } = ();
275// let Bar(..) = ();
276// }
277fn path_or_macro_pat(p: &mut Parser<'_>) -> CompletedMarker {
278 assert!(paths::is_path_start(p));
279 let m = p.start();
280 paths::expr_path(p);
281 let kind = match p.current() {
282 T!['('] => {
283 tuple_pat_fields(p);
284 TUPLE_STRUCT_PAT
285 }
286 T!['{'] => {
287 record_pat_field_list(p);
288 RECORD_PAT
289 }
290 // test marco_pat
291 // fn main() {
292 // let m!(x) = 0;
293 // }
294 T![!] => {
295 items::macro_call_after_excl(p);
296 return m.complete(p, MACRO_CALL).precede(p).complete(p, MACRO_PAT);
297 }
298 _ => PATH_PAT,
299 };
300 m.complete(p, kind)
301}
302
303// test tuple_pat_fields
304// fn foo() {
305// let S() = ();
306// let S(_) = ();
307// let S(_,) = ();
308// let S(_, .. , x) = ();
309// let S(| a) = ();
310// }
311fn tuple_pat_fields(p: &mut Parser<'_>) {
312 assert!(p.at(T!['(']));
313 p.bump(T!['(']);
314 pat_list(p, T![')']);
315 p.expect(T![')']);
316}
317
318// test record_pat_field
319// fn foo() {
320// let S { 0: 1 } = ();
321// let S { x: 1 } = ();
322// let S { #[cfg(any())] x: 1 } = ();
323// }
324fn record_pat_field(p: &mut Parser<'_>) {
325 match p.current() {
326 IDENT | INT_NUMBER if p.nth(1) == T![:] => {
327 name_ref_or_index(p);
328 p.bump(T![:]);
329 // test record_field_pat_leading_or
330 // fn foo() { let R { a: | 1 | 2 } = 0; }
331 pattern(p);
332 }
333 // test_err record_pat_field_eq_recovery
334 // fn main() {
335 // let S { field = foo };
336 // }
337 IDENT | INT_NUMBER if p.nth(1) == T![=] => {
338 name_ref_or_index(p);
339 p.err_and_bump("expected `:`");
340 pattern(p);
341 }
342 T![box] => {
343 // FIXME: not all box patterns should be allowed
344 box_pat(p);
345 }
346 T![ref] | T![mut] | IDENT => {
347 ident_pat(p, false);
348 }
349 _ => {
350 p.err_and_bump("expected identifier");
351 }
352 }
353}
354
355// test record_pat_field_list
356// fn foo() {
357// let S {} = ();
358// let S { f, ref mut g } = ();
359// let S { h: _, ..} = ();
360// let S { h: _, } = ();
361// let S { #[cfg(any())] .. } = ();
362// }
363fn record_pat_field_list(p: &mut Parser<'_>) {
364 assert!(p.at(T!['{']));
365 let m = p.start();
366 p.bump(T!['{']);
367 while !p.at(EOF) && !p.at(T!['}']) {
368 let m = p.start();
369 attributes::outer_attrs(p);
370
371 match p.current() {
372 // A trailing `..` is *not* treated as a REST_PAT.
373 T![.] if p.at(T![..]) => {
374 p.bump(T![..]);
375 m.complete(p, REST_PAT);
376 }
377 T!['{'] => {
378 error_block(p, "expected ident");
379 m.abandon(p);
380 }
381 _ => {
382 record_pat_field(p);
383 m.complete(p, RECORD_PAT_FIELD);
384 }
385 }
386 if !p.at(T!['}']) {
387 p.expect(T![,]);
388 }
389 }
390 p.expect(T!['}']);
391 m.complete(p, RECORD_PAT_FIELD_LIST);
392}
393
394// test placeholder_pat
395// fn main() { let _ = (); }
396fn wildcard_pat(p: &mut Parser<'_>) -> CompletedMarker {
397 assert!(p.at(T![_]));
398 let m = p.start();
399 p.bump(T![_]);
400 m.complete(p, WILDCARD_PAT)
401}
402
403// test ref_pat
404// fn main() {
405// let &a = ();
406// let &mut b = ();
407// }
408fn ref_pat(p: &mut Parser<'_>) -> CompletedMarker {
409 assert!(p.at(T![&]));
410 let m = p.start();
411 p.bump(T![&]);
412 p.eat(T![mut]);
413 pattern_single(p);
414 m.complete(p, REF_PAT)
415}
416
417// test tuple_pat
418// fn main() {
419// let (a, b, ..) = ();
420// let (a,) = ();
421// let (..) = ();
422// let () = ();
423// let (| a | a, | b) = ((),());
424// }
425fn tuple_pat(p: &mut Parser<'_>) -> CompletedMarker {
426 assert!(p.at(T!['(']));
427 let m = p.start();
428 p.bump(T!['(']);
429 let mut has_comma = false;
430 let mut has_pat = false;
431 let mut has_rest = false;
432
433 // test_err tuple_pat_leading_comma
434 // fn foo() {
435 // let (,);
436 // }
437 if p.eat(T![,]) {
438 p.error("expected pattern");
439 has_comma = true;
440 }
441
442 while !p.at(EOF) && !p.at(T![')']) {
443 has_pat = true;
444 if !p.at_ts(PAT_TOP_FIRST) {
445 p.error("expected a pattern");
446 break;
447 }
448 has_rest |= p.at(T![..]);
449
450 pattern(p);
451 if !p.at(T![')']) {
452 has_comma = true;
453 p.expect(T![,]);
454 }
455 }
456 p.expect(T![')']);
457
458 m.complete(p, if !has_comma && !has_rest && has_pat { PAREN_PAT } else { TUPLE_PAT })
459}
460
461// test slice_pat
462// fn main() {
463// let [a, b, ..] = [];
464// let [| a, ..] = [];
465// }
466fn slice_pat(p: &mut Parser<'_>) -> CompletedMarker {
467 assert!(p.at(T!['[']));
468 let m = p.start();
469 p.bump(T!['[']);
470 pat_list(p, T![']']);
471 p.expect(T![']']);
472 m.complete(p, SLICE_PAT)
473}
474
475fn pat_list(p: &mut Parser<'_>, ket: SyntaxKind) {
476 while !p.at(EOF) && !p.at(ket) {
477 pattern(p);
478 if !p.eat(T![,]) {
479 if p.at_ts(PAT_TOP_FIRST) {
480 p.error(format!("expected {:?}, got {:?}", T![,], p.current()));
481 } else {
482 break;
483 }
484 }
485 }
486}
487
488// test bind_pat
489// fn main() {
490// let a = ();
491// let mut b = ();
492// let ref c = ();
493// let ref mut d = ();
494// let e @ _ = ();
495// let ref mut f @ g @ _ = ();
496// }
497fn ident_pat(p: &mut Parser<'_>, with_at: bool) -> CompletedMarker {
498 assert!(matches!(p.current(), T![ref] | T![mut] | IDENT));
499 let m = p.start();
500 p.eat(T![ref]);
501 p.eat(T![mut]);
502 name_r(p, PAT_RECOVERY_SET);
503 if with_at && p.eat(T![@]) {
504 pattern_single(p);
505 }
506 m.complete(p, IDENT_PAT)
507}
508
509// test box_pat
510// fn main() {
511// let box i = ();
512// let box Outer { box i, j: box Inner(box &x) } = ();
513// let box ref mut i = ();
514// }
515fn box_pat(p: &mut Parser<'_>) -> CompletedMarker {
516 assert!(p.at(T![box]));
517 let m = p.start();
518 p.bump(T![box]);
519 pattern_single(p);
520 m.complete(p, BOX_PAT)
521}
522
523// test const_block_pat
524// fn main() {
525// let const { 15 } = ();
526// let const { foo(); bar() } = ();
527//
528// match 42 {
529// const { 0 } .. const { 1 } => (),
530// .. const { 0 } => (),
531// const { 2 } .. => (),
532// }
533//
534// let (const { () },) = ();
535// }
536fn const_block_pat(p: &mut Parser<'_>) -> CompletedMarker {
537 assert!(p.at(T![const]));
538 let m = p.start();
539 p.bump(T![const]);
540 expressions::block_expr(p);
541 m.complete(p, CONST_BLOCK_PAT)
542}