Skip to main content

leo_parser_rowan/parser/
statements.rs

1// Copyright (C) 2019-2026 Provable Inc.
2// This file is part of the Leo library.
3
4// The Leo library is free software: you can redistribute it and/or modify
5// it under the terms of the GNU General Public License as published by
6// the Free Software Foundation, either version 3 of the License, or
7// (at your option) any later version.
8
9// The Leo library is distributed in the hope that it will be useful,
10// but WITHOUT ANY WARRANTY; without even the implied warranty of
11// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12// GNU General Public License for more details.
13
14// You should have received a copy of the GNU General Public License
15// along with the Leo library. If not, see <https://www.gnu.org/licenses/>.
16
17//! Statement parsing for the Leo language.
18//!
19//! This module implements parsing for all Leo statement forms:
20//! - Let and const bindings
21//! - Assignments (including compound assignments)
22//! - Control flow (if, for)
23//! - Return statements
24//! - Assert statements
25//! - Expression statements
26//! - Blocks
27
28use super::{CompletedMarker, EXPR_RECOVERY, Parser, STMT_RECOVERY, expressions::ExprOpts};
29use crate::syntax_kind::{SyntaxKind, SyntaxKind::*};
30
31impl Parser<'_, '_> {
32    /// Recovery tokens for pattern parsing.
33    const PATTERN_RECOVERY: &'static [SyntaxKind] = &[COMMA, R_PAREN, COLON, EQ];
34
35    /// Recover trailing junk in a semicolon-terminated statement.
36    fn recover_statement_terminator(&mut self) {
37        if self.eat(SEMICOLON) {
38            return;
39        }
40
41        // Preserve the original missing-semicolon diagnostic at the first
42        // unexpected token, then optionally recover trailing junk afterwards.
43        self.expect(SEMICOLON);
44
45        if self.at(R_BRACE) || self.at_eof() {
46            return;
47        }
48
49        self.recover(&[SEMICOLON, R_BRACE]);
50        self.eat(SEMICOLON);
51    }
52
53    /// Parse a statement.
54    pub fn parse_stmt(&mut self) -> Option<CompletedMarker> {
55        self.skip_trivia();
56
57        match self.current() {
58            KW_LET => self.parse_let_stmt(),
59            KW_CONST => self.parse_const_stmt(),
60            KW_RETURN => self.parse_return_stmt(),
61            KW_IF => self.parse_if_stmt(),
62            KW_FOR => self.parse_for_stmt(),
63            KW_ASSERT => self.parse_assert_stmt(),
64            KW_ASSERT_EQ => self.parse_assert_eq_stmt(),
65            KW_ASSERT_NEQ => self.parse_assert_neq_stmt(),
66            L_BRACE => self.parse_block(),
67            _ => self.parse_expr_or_assign_stmt(),
68        }
69    }
70
71    /// Parse a let statement: `let x: Type = expr;` or `let (a, b) = expr;`
72    fn parse_let_stmt(&mut self) -> Option<CompletedMarker> {
73        let m = self.start();
74        self.bump_any(); // let
75
76        // Parse pattern (identifier or tuple destructuring)
77        self.parse_pattern();
78
79        // Optional type annotation
80        if self.eat(COLON) && self.parse_type().is_none() {
81            self.error("expected type");
82        }
83
84        // Initializer
85        self.expect(EQ);
86        if self.parse_expr().is_none() {
87            self.error_recover("expected expression", EXPR_RECOVERY);
88        }
89
90        self.recover_statement_terminator();
91        Some(m.complete(self, LET_STMT))
92    }
93
94    /// Parse a const statement: `const X: Type = expr;`
95    fn parse_const_stmt(&mut self) -> Option<CompletedMarker> {
96        let m = self.start();
97        self.bump_any(); // const
98
99        // Name
100        self.skip_trivia();
101        if self.at(IDENT) {
102            self.bump_any();
103        } else {
104            self.error("expected identifier");
105        }
106
107        // Type annotation (required for const).
108        // If ':' is missing but '=' follows, skip the type to avoid cascading.
109        if self.expect(COLON) {
110            if self.parse_type().is_none() {
111                self.error("expected type");
112            }
113        } else if !self.at(EQ) && self.parse_type().is_none() {
114            self.error("expected type");
115        }
116
117        // Initializer
118        self.expect(EQ);
119        if self.parse_expr().is_none() {
120            self.error_recover("expected expression", EXPR_RECOVERY);
121        }
122
123        self.recover_statement_terminator();
124        Some(m.complete(self, CONST_STMT))
125    }
126
127    /// Parse a return statement: `return expr;` or `return;`
128    fn parse_return_stmt(&mut self) -> Option<CompletedMarker> {
129        let m = self.start();
130        self.bump_any(); // return
131
132        // Optional expression - only attempt if we're not at semicolon/EOF
133        if !self.at(SEMICOLON) && !self.at_eof() && self.parse_expr().is_none() {
134            // Tried to parse expression but failed - recover
135            self.error_recover("expected expression or ';'", EXPR_RECOVERY);
136        }
137
138        self.recover_statement_terminator();
139        Some(m.complete(self, RETURN_STMT))
140    }
141
142    /// Parse an if statement: `if cond { } else if cond { } else { }`
143    fn parse_if_stmt(&mut self) -> Option<CompletedMarker> {
144        let m = self.start();
145        self.bump_any(); // if
146
147        // Parse condition (no struct literals to avoid ambiguity)
148        if self.parse_expr_with_opts(ExprOpts::no_struct()).is_none() {
149            self.error_recover("expected condition", EXPR_RECOVERY);
150        }
151
152        // Then block - recover if missing
153        if self.parse_block().is_none() && !self.at_eof() {
154            self.error_recover("expected block", STMT_RECOVERY);
155        }
156
157        // Optional else clause
158        if self.eat(KW_ELSE) {
159            if self.at(KW_IF) {
160                // else if
161                self.parse_if_stmt();
162            } else {
163                // else block - recover if missing
164                if self.parse_block().is_none() && !self.at_eof() {
165                    self.error_recover("expected block after 'else'", STMT_RECOVERY);
166                }
167            }
168        }
169
170        Some(m.complete(self, IF_STMT))
171    }
172
173    /// Parse a for statement: `for i: Type in lo..hi { }`
174    fn parse_for_stmt(&mut self) -> Option<CompletedMarker> {
175        let m = self.start();
176        self.bump_any(); // for
177
178        // Loop variable
179        self.skip_trivia();
180        if self.at(IDENT) {
181            self.bump_any();
182        } else {
183            self.error("expected loop variable");
184        }
185
186        // Optional type annotation
187        if self.eat(COLON) && self.parse_type().is_none() {
188            self.error("expected type");
189        }
190
191        // in keyword
192        self.expect(KW_IN);
193
194        // Range: lo..hi or lo..=hi
195        // Disallow struct literals so `IDENT {` parses as range + block body.
196        if self.parse_expr_with_opts(ExprOpts::no_struct()).is_none() {
197            self.error_recover("expected range start", EXPR_RECOVERY);
198        }
199        let inclusive = if self.eat(DOT_DOT) {
200            false
201        } else if self.eat(DOT_DOT_EQ) {
202            true
203        } else {
204            self.error("expected '..' or '..='");
205            false
206        };
207        if self.parse_expr_with_opts(ExprOpts::no_struct()).is_none() {
208            self.error_recover("expected range end", EXPR_RECOVERY);
209        }
210
211        // Body - recover if missing
212        if self.parse_block().is_none() && !self.at_eof() {
213            self.error_recover("expected block", STMT_RECOVERY);
214        }
215
216        let kind = if inclusive { FOR_INCLUSIVE_STMT } else { FOR_STMT };
217        Some(m.complete(self, kind))
218    }
219
220    /// Parse an assert statement: `assert(cond);`
221    fn parse_assert_stmt(&mut self) -> Option<CompletedMarker> {
222        let m = self.start();
223        self.bump_any(); // assert
224
225        self.expect(L_PAREN);
226        if self.parse_expr().is_none() {
227            self.error("expected expression");
228        }
229        self.expect(R_PAREN);
230
231        self.recover_statement_terminator();
232        Some(m.complete(self, ASSERT_STMT))
233    }
234
235    /// Parse an assert_eq statement: `assert_eq(a, b);`
236    fn parse_assert_eq_stmt(&mut self) -> Option<CompletedMarker> {
237        let m = self.start();
238        self.bump_any(); // assert_eq
239
240        self.expect(L_PAREN);
241        if self.parse_expr().is_none() {
242            self.error("expected first expression");
243        }
244        self.expect(COMMA);
245        if self.parse_expr().is_none() {
246            self.error("expected second expression");
247        }
248        self.expect(R_PAREN);
249
250        self.recover_statement_terminator();
251        Some(m.complete(self, ASSERT_EQ_STMT))
252    }
253
254    /// Parse an assert_neq statement: `assert_neq(a, b);`
255    fn parse_assert_neq_stmt(&mut self) -> Option<CompletedMarker> {
256        let m = self.start();
257        self.bump_any(); // assert_neq
258
259        self.expect(L_PAREN);
260        if self.parse_expr().is_none() {
261            self.error("expected first expression");
262        }
263        self.expect(COMMA);
264        if self.parse_expr().is_none() {
265            self.error("expected second expression");
266        }
267        self.expect(R_PAREN);
268
269        self.recover_statement_terminator();
270        Some(m.complete(self, ASSERT_NEQ_STMT))
271    }
272
273    /// Parse a block: `{ stmts... }`
274    pub fn parse_block(&mut self) -> Option<CompletedMarker> {
275        let m = self.start();
276
277        if !self.eat(L_BRACE) {
278            self.error("expected {");
279            m.abandon(self);
280            return None;
281        }
282
283        // Parse statements until }
284        while !self.at(R_BRACE) && !self.at_eof() {
285            let had_error = self.erroring;
286            // Clear error state at each loop iteration so errors from the
287            // previous statement don't suppress errors in the next one.
288            self.erroring = false;
289            if self.parse_stmt().is_none() {
290                // Error recovery: skip to next statement boundary
291                self.error_recover("expected statement", STMT_RECOVERY);
292            } else if self.erroring && !had_error {
293                // The statement parsed but encountered errors and left
294                // unconsumed tokens. Preserve a trailing `}` marker for the
295                // existing tree shape, but leave other statement boundaries
296                // alone so the next valid statement can still parse.
297                if self.at(R_BRACE) || !self.at_any(STMT_RECOVERY) {
298                    self.recover(STMT_RECOVERY);
299                    self.eat(SEMICOLON);
300                }
301            }
302        }
303
304        self.expect(R_BRACE);
305        Some(m.complete(self, BLOCK))
306    }
307
308    /// Parse an expression statement or assignment.
309    fn parse_expr_or_assign_stmt(&mut self) -> Option<CompletedMarker> {
310        let m = self.start();
311
312        let expr = self.parse_expr();
313        if expr.is_none() {
314            m.abandon(self);
315            return None;
316        }
317
318        // Check for assignment operators
319        if let Some(assign_kind) = self.current_assign_op() {
320            self.bump_any(); // operator
321            if self.parse_expr().is_none() {
322                self.error_recover("expected expression after assignment operator", EXPR_RECOVERY);
323            }
324            self.recover_statement_terminator();
325            return Some(m.complete(self, assign_kind));
326        }
327
328        // Expression statement
329        self.recover_statement_terminator();
330        Some(m.complete(self, EXPR_STMT))
331    }
332
333    /// Get the statement kind for the current assignment operator, if any.
334    fn current_assign_op(&self) -> Option<SyntaxKind> {
335        match self.current() {
336            EQ => Some(ASSIGN_STMT),
337            PLUS_EQ | MINUS_EQ | STAR_EQ | SLASH_EQ | PERCENT_EQ | STAR2_EQ | AMP_EQ | PIPE_EQ | CARET_EQ | SHL_EQ
338            | SHR_EQ | AMP2_EQ | PIPE2_EQ => Some(COMPOUND_ASSIGN_STMT),
339            _ => None,
340        }
341    }
342
343    /// Parse a pattern for let bindings.
344    fn parse_pattern(&mut self) {
345        self.skip_trivia();
346
347        match self.current() {
348            // Tuple pattern: (a, b, c)
349            L_PAREN => {
350                let m = self.start();
351                self.bump_any(); // (
352
353                if !self.at(R_PAREN) {
354                    self.parse_pattern();
355                    while self.eat(COMMA) {
356                        if self.at(R_PAREN) {
357                            break;
358                        }
359                        self.parse_pattern();
360                    }
361                }
362
363                self.expect(R_PAREN);
364                m.complete(self, TUPLE_PATTERN);
365            }
366            // Simple identifier pattern
367            IDENT => {
368                let m = self.start();
369                self.bump_any();
370                m.complete(self, IDENT_PATTERN);
371            }
372            // Wildcard pattern
373            UNDERSCORE => {
374                let m = self.start();
375                self.bump_any();
376                m.complete(self, WILDCARD_PATTERN);
377            }
378            _ => {
379                self.error_recover("expected pattern", Self::PATTERN_RECOVERY);
380            }
381        }
382    }
383}
384
385#[cfg(test)]
386mod tests {
387    use super::*;
388    use crate::{lexer::lex, parser::Parse};
389    use expect_test::{Expect, expect};
390
391    fn check_stmt(input: &str, expect: Expect) {
392        let (tokens, _) = lex(input);
393        let mut parser = Parser::new(input, &tokens);
394        let root = parser.start();
395        parser.parse_stmt();
396        parser.skip_trivia();
397        root.complete(&mut parser, ROOT);
398        let parse: Parse = parser.finish(vec![]);
399        let output = format!("{:#?}", parse.syntax());
400        expect.assert_eq(&output);
401    }
402
403    // =========================================================================
404    // Let Statements
405    // =========================================================================
406
407    #[test]
408    fn parse_stmt_let_simple() {
409        check_stmt("let x = 1;", expect![[r#"
410            ROOT@0..10
411              LET_STMT@0..10
412                KW_LET@0..3 "let"
413                WHITESPACE@3..4 " "
414                IDENT_PATTERN@4..5
415                  IDENT@4..5 "x"
416                WHITESPACE@5..6 " "
417                EQ@6..7 "="
418                WHITESPACE@7..8 " "
419                LITERAL_INT@8..9
420                  INTEGER@8..9 "1"
421                SEMICOLON@9..10 ";"
422        "#]]);
423    }
424
425    #[test]
426    fn parse_stmt_let_typed() {
427        check_stmt("let x: u32 = 42;", expect![[r#"
428            ROOT@0..16
429              LET_STMT@0..16
430                KW_LET@0..3 "let"
431                WHITESPACE@3..4 " "
432                IDENT_PATTERN@4..5
433                  IDENT@4..5 "x"
434                COLON@5..6 ":"
435                WHITESPACE@6..7 " "
436                TYPE_PRIMITIVE@7..10
437                  KW_U32@7..10 "u32"
438                WHITESPACE@10..11 " "
439                EQ@11..12 "="
440                WHITESPACE@12..13 " "
441                LITERAL_INT@13..15
442                  INTEGER@13..15 "42"
443                SEMICOLON@15..16 ";"
444        "#]]);
445    }
446
447    #[test]
448    fn parse_stmt_let_tuple_destructure() {
449        check_stmt("let (a, b) = tuple;", expect![[r#"
450                ROOT@0..19
451                  LET_STMT@0..19
452                    KW_LET@0..3 "let"
453                    WHITESPACE@3..4 " "
454                    TUPLE_PATTERN@4..10
455                      L_PAREN@4..5 "("
456                      IDENT_PATTERN@5..6
457                        IDENT@5..6 "a"
458                      COMMA@6..7 ","
459                      WHITESPACE@7..8 " "
460                      IDENT_PATTERN@8..9
461                        IDENT@8..9 "b"
462                      R_PAREN@9..10 ")"
463                    WHITESPACE@10..11 " "
464                    EQ@11..12 "="
465                    WHITESPACE@12..13 " "
466                    PATH_EXPR@13..18
467                      IDENT@13..18 "tuple"
468                    SEMICOLON@18..19 ";"
469            "#]]);
470    }
471
472    // =========================================================================
473    // Const Statements
474    // =========================================================================
475
476    #[test]
477    fn parse_stmt_const() {
478        check_stmt("const MAX: u32 = 100;", expect![[r#"
479            ROOT@0..21
480              CONST_STMT@0..21
481                KW_CONST@0..5 "const"
482                WHITESPACE@5..6 " "
483                IDENT@6..9 "MAX"
484                COLON@9..10 ":"
485                WHITESPACE@10..11 " "
486                TYPE_PRIMITIVE@11..14
487                  KW_U32@11..14 "u32"
488                WHITESPACE@14..15 " "
489                EQ@15..16 "="
490                WHITESPACE@16..17 " "
491                LITERAL_INT@17..20
492                  INTEGER@17..20 "100"
493                SEMICOLON@20..21 ";"
494        "#]]);
495    }
496
497    // =========================================================================
498    // Return Statements
499    // =========================================================================
500
501    #[test]
502    fn parse_stmt_return_value() {
503        check_stmt("return 42;", expect![[r#"
504            ROOT@0..10
505              RETURN_STMT@0..10
506                KW_RETURN@0..6 "return"
507                WHITESPACE@6..7 " "
508                LITERAL_INT@7..9
509                  INTEGER@7..9 "42"
510                SEMICOLON@9..10 ";"
511        "#]]);
512    }
513
514    #[test]
515    fn parse_stmt_return_empty() {
516        check_stmt("return;", expect![[r#"
517                ROOT@0..7
518                  RETURN_STMT@0..7
519                    KW_RETURN@0..6 "return"
520                    SEMICOLON@6..7 ";"
521            "#]]);
522    }
523
524    // =========================================================================
525    // Assignment Statements
526    // =========================================================================
527
528    #[test]
529    fn parse_stmt_assign() {
530        check_stmt("x = 1;", expect![[r#"
531            ROOT@0..6
532              ASSIGN_STMT@0..6
533                PATH_EXPR@0..2
534                  IDENT@0..1 "x"
535                  WHITESPACE@1..2 " "
536                EQ@2..3 "="
537                WHITESPACE@3..4 " "
538                LITERAL_INT@4..5
539                  INTEGER@4..5 "1"
540                SEMICOLON@5..6 ";"
541        "#]]);
542    }
543
544    #[test]
545    fn parse_stmt_assign_add() {
546        check_stmt("x += 1;", expect![[r#"
547            ROOT@0..7
548              COMPOUND_ASSIGN_STMT@0..7
549                PATH_EXPR@0..2
550                  IDENT@0..1 "x"
551                  WHITESPACE@1..2 " "
552                PLUS_EQ@2..4 "+="
553                WHITESPACE@4..5 " "
554                LITERAL_INT@5..6
555                  INTEGER@5..6 "1"
556                SEMICOLON@6..7 ";"
557        "#]]);
558    }
559
560    // =========================================================================
561    // If Statements
562    // =========================================================================
563
564    #[test]
565    fn parse_stmt_if_simple() {
566        check_stmt("if cond { }", expect![[r#"
567                ROOT@0..11
568                  IF_STMT@0..11
569                    KW_IF@0..2 "if"
570                    WHITESPACE@2..3 " "
571                    PATH_EXPR@3..8
572                      IDENT@3..7 "cond"
573                      WHITESPACE@7..8 " "
574                    BLOCK@8..11
575                      L_BRACE@8..9 "{"
576                      WHITESPACE@9..10 " "
577                      R_BRACE@10..11 "}"
578            "#]]);
579    }
580
581    #[test]
582    fn parse_stmt_if_else() {
583        check_stmt("if a { } else { }", expect![[r#"
584                ROOT@0..17
585                  IF_STMT@0..17
586                    KW_IF@0..2 "if"
587                    WHITESPACE@2..3 " "
588                    PATH_EXPR@3..5
589                      IDENT@3..4 "a"
590                      WHITESPACE@4..5 " "
591                    BLOCK@5..8
592                      L_BRACE@5..6 "{"
593                      WHITESPACE@6..7 " "
594                      R_BRACE@7..8 "}"
595                    WHITESPACE@8..9 " "
596                    KW_ELSE@9..13 "else"
597                    BLOCK@13..17
598                      WHITESPACE@13..14 " "
599                      L_BRACE@14..15 "{"
600                      WHITESPACE@15..16 " "
601                      R_BRACE@16..17 "}"
602            "#]]);
603    }
604
605    // =========================================================================
606    // For Statements
607    // =========================================================================
608
609    #[test]
610    fn parse_stmt_for() {
611        check_stmt("for i in 0..10 { }", expect![[r#"
612            ROOT@0..18
613              FOR_STMT@0..18
614                KW_FOR@0..3 "for"
615                WHITESPACE@3..4 " "
616                IDENT@4..5 "i"
617                WHITESPACE@5..6 " "
618                KW_IN@6..8 "in"
619                WHITESPACE@8..9 " "
620                LITERAL_INT@9..10
621                  INTEGER@9..10 "0"
622                DOT_DOT@10..12 ".."
623                LITERAL_INT@12..14
624                  INTEGER@12..14 "10"
625                BLOCK@14..18
626                  WHITESPACE@14..15 " "
627                  L_BRACE@15..16 "{"
628                  WHITESPACE@16..17 " "
629                  R_BRACE@17..18 "}"
630        "#]]);
631    }
632
633    // =========================================================================
634    // Assert Statements
635    // =========================================================================
636
637    #[test]
638    fn parse_stmt_assert() {
639        check_stmt("assert(x);", expect![[r#"
640                ROOT@0..10
641                  ASSERT_STMT@0..10
642                    KW_ASSERT@0..6 "assert"
643                    L_PAREN@6..7 "("
644                    PATH_EXPR@7..8
645                      IDENT@7..8 "x"
646                    R_PAREN@8..9 ")"
647                    SEMICOLON@9..10 ";"
648            "#]]);
649    }
650
651    #[test]
652    fn parse_stmt_assert_eq() {
653        check_stmt("assert_eq(a, b);", expect![[r#"
654                ROOT@0..16
655                  ASSERT_EQ_STMT@0..16
656                    KW_ASSERT_EQ@0..9 "assert_eq"
657                    L_PAREN@9..10 "("
658                    PATH_EXPR@10..11
659                      IDENT@10..11 "a"
660                    COMMA@11..12 ","
661                    WHITESPACE@12..13 " "
662                    PATH_EXPR@13..14
663                      IDENT@13..14 "b"
664                    R_PAREN@14..15 ")"
665                    SEMICOLON@15..16 ";"
666            "#]]);
667    }
668
669    // =========================================================================
670    // Block and Expression Statements
671    // =========================================================================
672
673    #[test]
674    fn parse_stmt_block() {
675        check_stmt("{ let x = 1; }", expect![[r#"
676            ROOT@0..14
677              BLOCK@0..14
678                L_BRACE@0..1 "{"
679                WHITESPACE@1..2 " "
680                LET_STMT@2..12
681                  KW_LET@2..5 "let"
682                  WHITESPACE@5..6 " "
683                  IDENT_PATTERN@6..7
684                    IDENT@6..7 "x"
685                  WHITESPACE@7..8 " "
686                  EQ@8..9 "="
687                  WHITESPACE@9..10 " "
688                  LITERAL_INT@10..11
689                    INTEGER@10..11 "1"
690                  SEMICOLON@11..12 ";"
691                WHITESPACE@12..13 " "
692                R_BRACE@13..14 "}"
693        "#]]);
694    }
695
696    #[test]
697    fn parse_stmt_expr() {
698        check_stmt("foo();", expect![[r#"
699                ROOT@0..6
700                  EXPR_STMT@0..6
701                    CALL_EXPR@0..5
702                      PATH_EXPR@0..3
703                        IDENT@0..3 "foo"
704                      L_PAREN@3..4 "("
705                      R_PAREN@4..5 ")"
706                    SEMICOLON@5..6 ";"
707            "#]]);
708    }
709
710    // =========================================================================
711    // Wildcard and Mixed Patterns (2a)
712    // =========================================================================
713
714    #[test]
715    fn parse_stmt_let_wildcard() {
716        check_stmt("let _ = 1;", expect![[r#"
717            ROOT@0..10
718              LET_STMT@0..10
719                KW_LET@0..3 "let"
720                WHITESPACE@3..4 " "
721                WILDCARD_PATTERN@4..5
722                  UNDERSCORE@4..5 "_"
723                WHITESPACE@5..6 " "
724                EQ@6..7 "="
725                WHITESPACE@7..8 " "
726                LITERAL_INT@8..9
727                  INTEGER@8..9 "1"
728                SEMICOLON@9..10 ";"
729        "#]]);
730    }
731
732    #[test]
733    fn parse_stmt_let_tuple_wildcard() {
734        check_stmt("let (_, x) = pair;", expect![[r#"
735            ROOT@0..18
736              LET_STMT@0..18
737                KW_LET@0..3 "let"
738                WHITESPACE@3..4 " "
739                TUPLE_PATTERN@4..10
740                  L_PAREN@4..5 "("
741                  WILDCARD_PATTERN@5..6
742                    UNDERSCORE@5..6 "_"
743                  COMMA@6..7 ","
744                  WHITESPACE@7..8 " "
745                  IDENT_PATTERN@8..9
746                    IDENT@8..9 "x"
747                  R_PAREN@9..10 ")"
748                WHITESPACE@10..11 " "
749                EQ@11..12 "="
750                WHITESPACE@12..13 " "
751                PATH_EXPR@13..17
752                  IDENT@13..17 "pair"
753                SEMICOLON@17..18 ";"
754        "#]]);
755    }
756
757    // =========================================================================
758    // Else-if Chains (2b)
759    // =========================================================================
760
761    #[test]
762    fn parse_stmt_if_else_if() {
763        check_stmt("if a { } else if b { } else { }", expect![[r#"
764            ROOT@0..31
765              IF_STMT@0..31
766                KW_IF@0..2 "if"
767                WHITESPACE@2..3 " "
768                PATH_EXPR@3..5
769                  IDENT@3..4 "a"
770                  WHITESPACE@4..5 " "
771                BLOCK@5..8
772                  L_BRACE@5..6 "{"
773                  WHITESPACE@6..7 " "
774                  R_BRACE@7..8 "}"
775                WHITESPACE@8..9 " "
776                KW_ELSE@9..13 "else"
777                IF_STMT@13..31
778                  WHITESPACE@13..14 " "
779                  KW_IF@14..16 "if"
780                  WHITESPACE@16..17 " "
781                  PATH_EXPR@17..19
782                    IDENT@17..18 "b"
783                    WHITESPACE@18..19 " "
784                  BLOCK@19..22
785                    L_BRACE@19..20 "{"
786                    WHITESPACE@20..21 " "
787                    R_BRACE@21..22 "}"
788                  WHITESPACE@22..23 " "
789                  KW_ELSE@23..27 "else"
790                  BLOCK@27..31
791                    WHITESPACE@27..28 " "
792                    L_BRACE@28..29 "{"
793                    WHITESPACE@29..30 " "
794                    R_BRACE@30..31 "}"
795        "#]]);
796    }
797
798    #[test]
799    fn parse_stmt_if_else_if_chain() {
800        check_stmt("if a { } else if b { } else if c { } else { }", expect![[r#"
801            ROOT@0..45
802              IF_STMT@0..45
803                KW_IF@0..2 "if"
804                WHITESPACE@2..3 " "
805                PATH_EXPR@3..5
806                  IDENT@3..4 "a"
807                  WHITESPACE@4..5 " "
808                BLOCK@5..8
809                  L_BRACE@5..6 "{"
810                  WHITESPACE@6..7 " "
811                  R_BRACE@7..8 "}"
812                WHITESPACE@8..9 " "
813                KW_ELSE@9..13 "else"
814                IF_STMT@13..45
815                  WHITESPACE@13..14 " "
816                  KW_IF@14..16 "if"
817                  WHITESPACE@16..17 " "
818                  PATH_EXPR@17..19
819                    IDENT@17..18 "b"
820                    WHITESPACE@18..19 " "
821                  BLOCK@19..22
822                    L_BRACE@19..20 "{"
823                    WHITESPACE@20..21 " "
824                    R_BRACE@21..22 "}"
825                  WHITESPACE@22..23 " "
826                  KW_ELSE@23..27 "else"
827                  IF_STMT@27..45
828                    WHITESPACE@27..28 " "
829                    KW_IF@28..30 "if"
830                    WHITESPACE@30..31 " "
831                    PATH_EXPR@31..33
832                      IDENT@31..32 "c"
833                      WHITESPACE@32..33 " "
834                    BLOCK@33..36
835                      L_BRACE@33..34 "{"
836                      WHITESPACE@34..35 " "
837                      R_BRACE@35..36 "}"
838                    WHITESPACE@36..37 " "
839                    KW_ELSE@37..41 "else"
840                    BLOCK@41..45
841                      WHITESPACE@41..42 " "
842                      L_BRACE@42..43 "{"
843                      WHITESPACE@43..44 " "
844                      R_BRACE@44..45 "}"
845        "#]]);
846    }
847
848    // =========================================================================
849    // For Loop with Typed Variable (2c)
850    // =========================================================================
851
852    #[test]
853    fn parse_stmt_for_typed() {
854        check_stmt("for i: u32 in 0..10 { }", expect![[r#"
855            ROOT@0..23
856              FOR_STMT@0..23
857                KW_FOR@0..3 "for"
858                WHITESPACE@3..4 " "
859                IDENT@4..5 "i"
860                COLON@5..6 ":"
861                WHITESPACE@6..7 " "
862                TYPE_PRIMITIVE@7..10
863                  KW_U32@7..10 "u32"
864                WHITESPACE@10..11 " "
865                KW_IN@11..13 "in"
866                WHITESPACE@13..14 " "
867                LITERAL_INT@14..15
868                  INTEGER@14..15 "0"
869                DOT_DOT@15..17 ".."
870                LITERAL_INT@17..19
871                  INTEGER@17..19 "10"
872                BLOCK@19..23
873                  WHITESPACE@19..20 " "
874                  L_BRACE@20..21 "{"
875                  WHITESPACE@21..22 " "
876                  R_BRACE@22..23 "}"
877        "#]]);
878    }
879
880    // =========================================================================
881    // Missing Assignment Operators (2d)
882    // =========================================================================
883
884    #[test]
885    fn parse_stmt_assign_sub() {
886        check_stmt("x -= 1;", expect![[r#"
887            ROOT@0..7
888              COMPOUND_ASSIGN_STMT@0..7
889                PATH_EXPR@0..2
890                  IDENT@0..1 "x"
891                  WHITESPACE@1..2 " "
892                MINUS_EQ@2..4 "-="
893                WHITESPACE@4..5 " "
894                LITERAL_INT@5..6
895                  INTEGER@5..6 "1"
896                SEMICOLON@6..7 ";"
897        "#]]);
898    }
899
900    #[test]
901    fn parse_stmt_assign_mul() {
902        check_stmt("x *= 2;", expect![[r#"
903            ROOT@0..7
904              COMPOUND_ASSIGN_STMT@0..7
905                PATH_EXPR@0..2
906                  IDENT@0..1 "x"
907                  WHITESPACE@1..2 " "
908                STAR_EQ@2..4 "*="
909                WHITESPACE@4..5 " "
910                LITERAL_INT@5..6
911                  INTEGER@5..6 "2"
912                SEMICOLON@6..7 ";"
913        "#]]);
914    }
915
916    #[test]
917    fn parse_stmt_assign_div() {
918        check_stmt("x /= 2;", expect![[r#"
919            ROOT@0..7
920              COMPOUND_ASSIGN_STMT@0..7
921                PATH_EXPR@0..2
922                  IDENT@0..1 "x"
923                  WHITESPACE@1..2 " "
924                SLASH_EQ@2..4 "/="
925                WHITESPACE@4..5 " "
926                LITERAL_INT@5..6
927                  INTEGER@5..6 "2"
928                SEMICOLON@6..7 ";"
929        "#]]);
930    }
931
932    #[test]
933    fn parse_stmt_assign_pow() {
934        check_stmt("x **= 2;", expect![[r#"
935            ROOT@0..8
936              COMPOUND_ASSIGN_STMT@0..8
937                PATH_EXPR@0..2
938                  IDENT@0..1 "x"
939                  WHITESPACE@1..2 " "
940                STAR2_EQ@2..5 "**="
941                WHITESPACE@5..6 " "
942                LITERAL_INT@6..7
943                  INTEGER@6..7 "2"
944                SEMICOLON@7..8 ";"
945        "#]]);
946    }
947
948    #[test]
949    fn parse_stmt_assign_bitor() {
950        check_stmt("x |= 1;", expect![[r#"
951            ROOT@0..7
952              COMPOUND_ASSIGN_STMT@0..7
953                PATH_EXPR@0..2
954                  IDENT@0..1 "x"
955                  WHITESPACE@1..2 " "
956                PIPE_EQ@2..4 "|="
957                WHITESPACE@4..5 " "
958                LITERAL_INT@5..6
959                  INTEGER@5..6 "1"
960                SEMICOLON@6..7 ";"
961        "#]]);
962    }
963
964    #[test]
965    fn parse_stmt_assign_bitand() {
966        check_stmt("x &= 1;", expect![[r#"
967            ROOT@0..7
968              COMPOUND_ASSIGN_STMT@0..7
969                PATH_EXPR@0..2
970                  IDENT@0..1 "x"
971                  WHITESPACE@1..2 " "
972                AMP_EQ@2..4 "&="
973                WHITESPACE@4..5 " "
974                LITERAL_INT@5..6
975                  INTEGER@5..6 "1"
976                SEMICOLON@6..7 ";"
977        "#]]);
978    }
979
980    #[test]
981    fn parse_stmt_assign_shl() {
982        check_stmt("x <<= 1;", expect![[r#"
983            ROOT@0..8
984              COMPOUND_ASSIGN_STMT@0..8
985                PATH_EXPR@0..2
986                  IDENT@0..1 "x"
987                  WHITESPACE@1..2 " "
988                SHL_EQ@2..5 "<<="
989                WHITESPACE@5..6 " "
990                LITERAL_INT@6..7
991                  INTEGER@6..7 "1"
992                SEMICOLON@7..8 ";"
993        "#]]);
994    }
995
996    #[test]
997    fn parse_stmt_assign_shr() {
998        check_stmt("x >>= 1;", expect![[r#"
999            ROOT@0..8
1000              COMPOUND_ASSIGN_STMT@0..8
1001                PATH_EXPR@0..2
1002                  IDENT@0..1 "x"
1003                  WHITESPACE@1..2 " "
1004                SHR_EQ@2..5 ">>="
1005                WHITESPACE@5..6 " "
1006                LITERAL_INT@6..7
1007                  INTEGER@6..7 "1"
1008                SEMICOLON@7..8 ";"
1009        "#]]);
1010    }
1011
1012    // =========================================================================
1013    // Assert_neq (2e)
1014    // =========================================================================
1015
1016    #[test]
1017    fn parse_stmt_assert_neq() {
1018        check_stmt("assert_neq(a, b);", expect![[r#"
1019            ROOT@0..17
1020              ASSERT_NEQ_STMT@0..17
1021                KW_ASSERT_NEQ@0..10 "assert_neq"
1022                L_PAREN@10..11 "("
1023                PATH_EXPR@11..12
1024                  IDENT@11..12 "a"
1025                COMMA@12..13 ","
1026                WHITESPACE@13..14 " "
1027                PATH_EXPR@14..15
1028                  IDENT@14..15 "b"
1029                R_PAREN@15..16 ")"
1030                SEMICOLON@16..17 ";"
1031        "#]]);
1032    }
1033
1034    // =========================================================================
1035    // Expression Statement with Args (2f)
1036    // =========================================================================
1037
1038    #[test]
1039    fn parse_stmt_expr_call() {
1040        check_stmt("foo(x);", expect![[r#"
1041            ROOT@0..7
1042              EXPR_STMT@0..7
1043                CALL_EXPR@0..6
1044                  PATH_EXPR@0..3
1045                    IDENT@0..3 "foo"
1046                  L_PAREN@3..4 "("
1047                  PATH_EXPR@4..5
1048                    IDENT@4..5 "x"
1049                  R_PAREN@5..6 ")"
1050                SEMICOLON@6..7 ";"
1051        "#]]);
1052    }
1053}