parse_js/parse/
stmt.rs

1use super::decl::VarDeclParseMode;
2use super::expr::Asi;
3use super::pattern::is_valid_pattern_identifier;
4use super::ParseCtx;
5use super::Parser;
6use crate::ast::ExportName;
7use crate::ast::ExportNames;
8use crate::ast::ForInit;
9use crate::ast::Node;
10use crate::ast::Syntax;
11use crate::error::SyntaxErrorType;
12use crate::error::SyntaxResult;
13use crate::loc::Loc;
14use crate::token::TokenType;
15
16struct BreakOrContinue {
17  loc: Loc,
18  label: Option<String>,
19}
20
21impl<'a> Parser<'a> {
22  // Parses `a`, `a as b`, `default as b`. Creates the symbol if importing.
23  fn parse_import_or_export_name(&mut self, ctx: ParseCtx) -> SyntaxResult<ExportName> {
24    let (target, alias) = match self.consume_if(TokenType::KeywordDefault)?.match_loc() {
25      Some(target) => {
26        self.require(TokenType::KeywordAs)?;
27        let alias = self.require(TokenType::Identifier)?.loc;
28        (target, alias)
29      }
30      None => {
31        let target = self.require(TokenType::Identifier)?.loc;
32        let alias = if self.consume_if(TokenType::KeywordAs)?.is_match() {
33          self.require(TokenType::Identifier)?.loc
34        } else {
35          target
36        };
37        (target, alias)
38      }
39    };
40    let alias_node = Node::new(alias, Syntax::IdentifierPattern {
41      name: self.string(alias),
42    });
43    Ok(ExportName {
44      target: self.string(target),
45      alias: alias_node,
46    })
47  }
48
49  pub fn parse_stmts(&mut self, ctx: ParseCtx, end: TokenType) -> SyntaxResult<Vec<Node>> {
50    let mut body = Vec::new();
51    while self.peek()?.typ != end {
52      body.push(self.parse_stmt(ctx)?);
53    }
54    Ok(body)
55  }
56
57  pub fn parse_stmt(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
58    match self.peek()?.typ {
59      TokenType::BraceOpen => self.parse_stmt_block(ctx),
60      TokenType::KeywordBreak => self.parse_stmt_break(ctx),
61      TokenType::KeywordClass => self.parse_decl_class(ctx, false, false),
62      TokenType::KeywordConst | TokenType::KeywordLet | TokenType::KeywordVar => {
63        self.parse_decl_var(ctx, VarDeclParseMode::Asi, false)
64      }
65      TokenType::KeywordContinue => self.parse_stmt_continue(ctx),
66      TokenType::KeywordDebugger => self.parse_stmt_debugger(ctx),
67      TokenType::KeywordDo => self.parse_stmt_do_while(ctx),
68      TokenType::KeywordExport => self.parse_stmt_export(ctx),
69      TokenType::KeywordFor => self.parse_stmt_for(ctx),
70      TokenType::KeywordAsync | TokenType::KeywordFunction => {
71        self.parse_decl_function(ctx, false, false)
72      }
73      TokenType::KeywordIf => self.parse_stmt_if(ctx),
74      TokenType::KeywordImport => self.parse_stmt_import_or_expr_import(ctx),
75      TokenType::KeywordReturn => self.parse_stmt_return(ctx),
76      TokenType::KeywordSwitch => self.parse_stmt_switch(ctx),
77      TokenType::KeywordThrow => self.parse_stmt_throw(ctx),
78      TokenType::KeywordTry => self.parse_stmt_try(ctx),
79      TokenType::KeywordWhile => self.parse_stmt_while(ctx),
80      TokenType::Semicolon => self.parse_stmt_empty(ctx),
81      t if is_valid_pattern_identifier(t, ctx.rules) => {
82        let checkpoint = self.checkpoint();
83        let label_name = self.next()?.loc;
84        if self.consume_if(TokenType::Colon)?.is_match() {
85          let statement = self.parse_stmt(ctx)?;
86          Ok(Node::new(
87            self.since_checkpoint(checkpoint),
88            Syntax::LabelStmt {
89              name: self.string(label_name),
90              statement,
91            },
92          ))
93        } else {
94          self.restore_checkpoint(checkpoint);
95          self.parse_stmt_expression(ctx)
96        }
97      }
98      _ => self.parse_stmt_expression(ctx),
99    }
100  }
101
102  pub fn parse_stmt_empty(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
103    let loc = self.require(TokenType::Semicolon)?.loc;
104    Ok(Node::new(loc, Syntax::EmptyStmt {}))
105  }
106
107  pub fn parse_stmt_block(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
108    let start = self.require(TokenType::BraceOpen)?;
109    let body = self.parse_stmts(ctx, TokenType::BraceClose)?;
110    let end = self.require(TokenType::BraceClose)?;
111    Ok(Node::new(start.loc + end.loc, Syntax::BlockStmt { body }))
112  }
113
114  fn parse_stmt_break_or_continue(
115    &mut self,
116    ctx: ParseCtx,
117    t: TokenType,
118  ) -> SyntaxResult<BreakOrContinue> {
119    let mut loc = self.require(t)?.loc;
120    let next = self.peek()?;
121    let label =
122      if is_valid_pattern_identifier(next.typ, ctx.rules) && !next.preceded_by_line_terminator {
123        // Label.
124        self.consume_peeked();
125        loc.extend(next.loc);
126        Some(self.string(next.loc))
127      } else if next.typ == TokenType::Semicolon {
128        self.consume_peeked();
129        None
130      } else if next.preceded_by_line_terminator || next.typ == TokenType::BraceClose {
131        // ASI.
132        None
133      } else {
134        return Err(next.error(SyntaxErrorType::ExpectedSyntax("continue label")));
135      };
136    Ok(BreakOrContinue { loc, label })
137  }
138
139  pub fn parse_stmt_break(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
140    let stmt = self.parse_stmt_break_or_continue(ctx, TokenType::KeywordBreak)?;
141    Ok(Node::new(stmt.loc, Syntax::BreakStmt { label: stmt.label }))
142  }
143
144  pub fn parse_stmt_continue(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
145    let stmt = self.parse_stmt_break_or_continue(ctx, TokenType::KeywordContinue)?;
146    Ok(Node::new(stmt.loc, Syntax::ContinueStmt {
147      label: stmt.label,
148    }))
149  }
150
151  pub fn parse_stmt_debugger(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
152    let loc = self.require(TokenType::KeywordDebugger)?.loc;
153    Ok(Node::new(loc, Syntax::DebuggerStmt {}))
154  }
155
156  // https://tc39.es/ecma262/#sec-exports
157  // https://jakearchibald.com/2021/export-default-thing-vs-thing-as-default/
158  pub fn parse_stmt_export(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
159    // TODO Ensure top-level.
160    let start = self.require(TokenType::KeywordExport)?;
161    let cp = self.checkpoint();
162    let t = self.next()?;
163    Ok(match t.typ {
164      TokenType::BraceOpen => {
165        let mut names = Vec::<ExportName>::new();
166        loop {
167          if self.consume_if(TokenType::BraceClose)?.is_match() {
168            break;
169          };
170          let name = self.parse_import_or_export_name(ctx)?;
171          names.push(name);
172          if !self.consume_if(TokenType::Comma)?.is_match() {
173            self.require(TokenType::BraceClose)?;
174            break;
175          };
176        }
177        let from = self.consume_if(TokenType::KeywordFrom)?.and_then(|| {
178          let from = self.parse_and_normalise_literal_string(ctx)?;
179          Ok(from)
180        })?;
181        // TODO Loc
182        Node::new(start.loc, Syntax::ExportListStmt {
183          names: ExportNames::Specific(names),
184          from,
185        })
186      }
187      TokenType::Asterisk => {
188        let alias = if self.consume_if(TokenType::KeywordAs)?.is_match() {
189          let alias = self.require(TokenType::Identifier)?.loc;
190          let alias_node = Node::new(alias, Syntax::IdentifierPattern {
191            name: self.string(alias),
192          });
193          Some(alias_node)
194          // We don't need to add the symbol as it's not exposed within the module's scope.
195        } else {
196          None
197        };
198        self.require(TokenType::KeywordFrom)?;
199        let from = self.parse_and_normalise_literal_string(ctx)?;
200        // TODO Loc
201        Node::new(start.loc, Syntax::ExportListStmt {
202          names: ExportNames::All(alias),
203          from: Some(from),
204        })
205      }
206      TokenType::KeywordDefault => match self.peek()?.typ {
207        // `class` and `function` are treated as statements that are hoisted, not expressions; however, they can be unnamed, which gives them the name `default`.
208        TokenType::KeywordAsync | TokenType::KeywordFunction => {
209          self.parse_decl_function(ctx, true, true)?
210        }
211        TokenType::KeywordClass => self.parse_decl_class(ctx, true, true)?,
212        _ => {
213          let expression = self.parse_expr(ctx, TokenType::Semicolon)?;
214          Node::new(start.loc + expression.loc, Syntax::ExportDefaultExprStmt {
215            expression,
216          })
217        }
218      },
219      TokenType::KeywordVar | TokenType::KeywordLet | TokenType::KeywordConst => {
220        // Reconsume declaration keyword.
221        self.restore_checkpoint(cp);
222        self.parse_decl_var(ctx, VarDeclParseMode::Asi, true)?
223      }
224      TokenType::KeywordFunction => {
225        // Reconsume declaration keyword.
226        self.restore_checkpoint(cp);
227        self.parse_decl_function(ctx, true, false)?
228      }
229      TokenType::KeywordClass => {
230        // Reconsume declaration keyword.
231        self.restore_checkpoint(cp);
232        self.parse_decl_class(ctx, true, false)?
233      }
234      _ => return Err(t.error(SyntaxErrorType::ExpectedSyntax("exportable"))),
235    })
236  }
237
238  // WARNING: Do not reuse this functions for other statements, as this will output a statement node, not an expression, which can lead to double semicolons that cause invalid code when outputting.
239  pub fn parse_stmt_expression(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
240    let mut asi = Asi::can();
241    let expression = self.parse_expr_with_asi(ctx, TokenType::Semicolon, &mut asi)?;
242    if !asi.did_end_with_asi {
243      self.require(TokenType::Semicolon)?;
244    };
245    Ok(Node::new(expression.loc, Syntax::ExpressionStmt {
246      expression,
247    }))
248  }
249
250  fn parse_stmt_for_body(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
251    if self.peek()?.typ == TokenType::BraceOpen {
252      let start = self.require(TokenType::BraceOpen)?;
253      let body = self.parse_stmts(ctx, TokenType::BraceClose)?;
254      let end = self.require(TokenType::BraceClose)?;
255      Ok(Node::new(start.loc + end.loc, Syntax::ForBody { body }))
256    } else {
257      self.parse_stmt(ctx)
258    }
259  }
260
261  pub fn parse_stmt_for(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
262    let start = self.require(TokenType::KeywordFor)?;
263    let await_ = self.consume_if(TokenType::KeywordAwait)?;
264    self.require(TokenType::ParenthesisOpen)?;
265    enum LhsRaw {
266      Declaration(Node),
267      Expression(Node),
268      Pattern(Node),
269      Empty,
270    }
271    let lhs_raw = match self.peek()?.typ {
272      TokenType::KeywordVar | TokenType::KeywordLet | TokenType::KeywordConst => {
273        LhsRaw::Declaration(self.parse_decl_var(ctx, VarDeclParseMode::Leftmost, false)?)
274      }
275      TokenType::Semicolon => LhsRaw::Empty,
276      _ => {
277        // A pattern could be reinterpreted as an expression (and vice versa), so we can only try parsing both.
278        let checkpoint = self.checkpoint();
279        match if let Ok(node) = self.parse_pattern(ctx) {
280          match self.peek()?.typ {
281            TokenType::KeywordIn | TokenType::KeywordOf => Some(LhsRaw::Pattern(node)),
282            _ => {
283              // Mistakenly interpreted as pattern.
284              None
285            }
286          }
287        } else {
288          None
289        } {
290          Some(p) => p,
291          None => {
292            self.restore_checkpoint(checkpoint);
293            LhsRaw::Expression(self.parse_expr(ctx, TokenType::Semicolon)?)
294          }
295        }
296      }
297    };
298    let n = match self.peek()?.typ {
299      TokenType::KeywordOf | TokenType::KeywordIn => {
300        // for-of or for-in statement.
301        let of = match self.next()?.typ {
302          TokenType::KeywordOf => true,
303          TokenType::KeywordIn => false,
304          _ => unreachable!(),
305        };
306        if !of && await_.is_match() {
307          // A for-in statement cannot have await.
308          return Err(await_.error(SyntaxErrorType::RequiredTokenNotFound(
309            TokenType::ParenthesisOpen,
310          )));
311        };
312        let (decl_mode, pat) = match lhs_raw {
313          LhsRaw::Empty => return Err(start.error(SyntaxErrorType::ForLoopHeaderHasNoLhs)),
314          LhsRaw::Declaration(node) => match *node.stx {
315            Syntax::VarDecl {
316              mut declarators,
317              mode,
318              export,
319            } => {
320              if export {
321                return Err(start.error(SyntaxErrorType::ForLoopHeaderHasInvalidLhs));
322              };
323              if declarators.len() != 1 {
324                return Err(start.error(SyntaxErrorType::ForLoopHeaderHasMultipleDeclarators));
325              }
326              let decl = declarators.pop().unwrap();
327              if decl.initializer.is_some() {
328                return Err(start.error(SyntaxErrorType::ForLoopHeaderHasInvalidLhs));
329              };
330              (Some(mode), decl.pattern)
331            }
332            _ => unreachable!(),
333          },
334          LhsRaw::Pattern(pat) => (None, pat),
335          LhsRaw::Expression(_) => {
336            return Err(start.error(SyntaxErrorType::ForLoopHeaderHasInvalidLhs))
337          }
338        };
339        let rhs = self.parse_expr(ctx, TokenType::ParenthesisClose)?;
340        self.require(TokenType::ParenthesisClose)?;
341        let body = self.parse_stmt_for_body(ctx)?;
342        if of {
343          Node::new(start.loc + body.loc, Syntax::ForOfStmt {
344            await_: await_.is_match(),
345            decl_mode,
346            pat,
347            rhs,
348            body,
349          })
350        } else {
351          Node::new(start.loc + body.loc, Syntax::ForInStmt {
352            decl_mode,
353            pat,
354            rhs,
355            body,
356          })
357        }
358      }
359      _ => {
360        // for statement.
361        if await_.is_match() {
362          // A for statement cannot have await.
363          return Err(await_.error(SyntaxErrorType::RequiredTokenNotFound(
364            TokenType::ParenthesisOpen,
365          )));
366        }
367        let init = match lhs_raw {
368          LhsRaw::Declaration(decl) => {
369            self.require(TokenType::Semicolon)?;
370            ForInit::Declaration(decl)
371          }
372          LhsRaw::Expression(expr) => {
373            // We must check, due to possibility of illegal ASI (see previous).
374            self.require(TokenType::Semicolon)?;
375            ForInit::Expression(expr)
376          }
377          LhsRaw::Empty => {
378            self.require(TokenType::Semicolon)?;
379            ForInit::None
380          }
381          LhsRaw::Pattern(_) => {
382            return Err(start.error(SyntaxErrorType::ForLoopHeaderHasInvalidLhs))
383          }
384        };
385        let condition = if self.consume_if(TokenType::Semicolon)?.is_match() {
386          None
387        } else {
388          let expr = self.parse_expr(ctx, TokenType::Semicolon)?;
389          self.require(TokenType::Semicolon)?;
390          Some(expr)
391        };
392        let post = if self.consume_if(TokenType::ParenthesisClose)?.is_match() {
393          None
394        } else {
395          let expr = self.parse_expr(ctx, TokenType::ParenthesisClose)?;
396          self.require(TokenType::ParenthesisClose)?;
397          Some(expr)
398        };
399        let body = self.parse_stmt_for_body(ctx)?;
400        Node::new(start.loc + body.loc, Syntax::ForStmt {
401          init,
402          condition,
403          post,
404          body,
405        })
406      }
407    };
408    Ok(n)
409  }
410
411  pub fn parse_stmt_if(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
412    let start = self.require(TokenType::KeywordIf)?;
413    self.require(TokenType::ParenthesisOpen)?;
414    let test = self.parse_expr(ctx, TokenType::ParenthesisClose)?;
415    self.require(TokenType::ParenthesisClose)?;
416    let consequent = self.parse_stmt(ctx)?;
417    let alternate = if self.consume_if(TokenType::KeywordElse)?.is_match() {
418      Some(self.parse_stmt(ctx)?)
419    } else {
420      None
421    };
422    let end = alternate.as_ref().unwrap_or(&consequent);
423
424    Ok(Node::new(start.loc + end.loc, Syntax::IfStmt {
425      test,
426      consequent,
427      alternate,
428    }))
429  }
430
431  pub fn parse_stmt_import_or_expr_import(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
432    let cp = self.checkpoint();
433    let start = self.require(TokenType::KeywordImport)?;
434    if self.consume_if(TokenType::ParenthesisOpen)?.is_match() {
435      self.restore_checkpoint(cp);
436      return self.parse_stmt_expression(ctx);
437    };
438
439    // TODO Ensure top-level.
440
441    let (default, can_have_names) =
442      if let Some(alias) = self.consume_if(TokenType::Identifier)?.match_loc() {
443        let alias_node = Node::new(alias, Syntax::IdentifierPattern {
444          name: self.string(alias),
445        });
446        (
447          Some(alias_node),
448          self.consume_if(TokenType::Comma)?.is_match(),
449        )
450      } else {
451        (None, true)
452      };
453    let names = if !can_have_names {
454      None
455    } else if self.consume_if(TokenType::Asterisk)?.is_match() {
456      self.require(TokenType::KeywordAs)?;
457      let alias = self.require(TokenType::Identifier)?.loc;
458      let alias_node = Node::new(alias, Syntax::IdentifierPattern {
459        name: self.string(alias),
460      });
461      Some(ExportNames::All(Some(alias_node)))
462    } else {
463      self.require(TokenType::BraceOpen)?;
464      let mut names = Vec::<ExportName>::new();
465      while !self.consume_if(TokenType::BraceClose)?.is_match() {
466        let name = self.parse_import_or_export_name(ctx)?;
467        names.push(name);
468        if !self.consume_if(TokenType::Comma)?.is_match() {
469          break;
470        };
471      }
472      self.require(TokenType::BraceClose)?;
473      Some(ExportNames::Specific(names))
474    };
475    self.require(TokenType::KeywordFrom)?;
476    let module = self.parse_and_normalise_literal_string(ctx)?;
477    self.require(TokenType::Semicolon)?;
478    // TODO Loc
479    Ok(Node::new(start.loc, Syntax::ImportStmt {
480      default,
481      module,
482      names,
483    }))
484  }
485
486  pub fn parse_stmt_return(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
487    let start = self.require(TokenType::KeywordReturn)?;
488    let mut loc = start.loc;
489    let value =
490      if self.peek()?.preceded_by_line_terminator || self.peek()?.typ == TokenType::BraceClose {
491        // Automatic Semicolon Insertion.
492        None
493      } else if self.consume_if(TokenType::Semicolon)?.is_match() {
494        None
495      } else {
496        let mut asi = Asi::can();
497        let value = self.parse_expr_with_asi(ctx, TokenType::Semicolon, &mut asi)?;
498        if !asi.did_end_with_asi {
499          self.require(TokenType::Semicolon)?;
500        };
501        loc.extend(value.loc);
502        Some(value)
503      };
504    Ok(Node::new(loc, Syntax::ReturnStmt { value }))
505  }
506
507  pub fn parse_stmt_throw(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
508    let start = self.require(TokenType::KeywordThrow)?;
509    if self.peek()?.preceded_by_line_terminator {
510      // Illegal under Automatic Semicolon Insertion rules.
511      return Err(start.error(SyntaxErrorType::LineTerminatorAfterThrow));
512    }
513    let mut asi = Asi::can();
514    let value = self.parse_expr_with_asi(ctx, TokenType::Semicolon, &mut asi)?;
515    if !asi.did_end_with_asi {
516      self.require(TokenType::Semicolon)?;
517    };
518    Ok(Node::new(start.loc + value.loc, Syntax::ThrowStmt {
519      value,
520    }))
521  }
522
523  pub fn parse_stmt_try(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
524    let start = self.require(TokenType::KeywordTry)?;
525    let mut loc = start.loc;
526    let wrapped = self.parse_stmt_block(ctx)?;
527    let catch = if self.consume_if(TokenType::KeywordCatch)?.is_match() {
528      let parameter = if self.consume_if(TokenType::ParenthesisOpen)?.is_match() {
529        let pattern = self.parse_pattern(ctx)?;
530        self.require(TokenType::ParenthesisClose)?;
531        Some(pattern)
532      } else {
533        None
534      };
535      let start = self.require(TokenType::BraceOpen)?;
536      let body = self.parse_stmts(ctx, TokenType::BraceClose)?;
537      let end = self.require(TokenType::BraceClose)?;
538      loc += end.loc;
539      Some(Node::new(start.loc + end.loc, Syntax::CatchBlock {
540        parameter,
541        body,
542      }))
543    } else {
544      None
545    };
546    let finally = if self.consume_if(TokenType::KeywordFinally)?.is_match() {
547      let body = self.parse_stmt_block(ctx)?;
548      loc.extend(body.loc);
549      Some(body)
550    } else {
551      None
552    };
553    if catch.is_none() && finally.is_none() {
554      return Err(start.error(SyntaxErrorType::TryStatementHasNoCatchOrFinally));
555    }
556    Ok(Node::new(loc, Syntax::TryStmt {
557      wrapped,
558      catch,
559      finally,
560    }))
561  }
562
563  pub fn parse_stmt_while(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
564    let start = self.require(TokenType::KeywordWhile)?;
565    self.require(TokenType::ParenthesisOpen)?;
566    let condition = self.parse_expr(ctx, TokenType::ParenthesisClose)?;
567    self.require(TokenType::ParenthesisClose)?;
568    let body = self.parse_stmt(ctx)?;
569    Ok(Node::new(start.loc + body.loc, Syntax::WhileStmt {
570      condition,
571      body,
572    }))
573  }
574
575  pub fn parse_stmt_do_while(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
576    let start = self.require(TokenType::KeywordDo)?;
577    let body = self.parse_stmt(ctx)?;
578    self.require(TokenType::KeywordWhile)?;
579    self.require(TokenType::ParenthesisOpen)?;
580    let condition = self.parse_expr(ctx, TokenType::ParenthesisClose)?;
581    let end = self.require(TokenType::ParenthesisClose)?;
582    self.consume_if(TokenType::Semicolon)?;
583    Ok(Node::new(start.loc + end.loc, Syntax::DoWhileStmt {
584      condition,
585      body,
586    }))
587  }
588
589  pub fn parse_stmt_switch(&mut self, ctx: ParseCtx) -> SyntaxResult<Node> {
590    let start = self.require(TokenType::KeywordSwitch)?;
591    self.require(TokenType::ParenthesisOpen)?;
592    let test = self.parse_expr(ctx, TokenType::ParenthesisClose)?;
593    self.require(TokenType::ParenthesisClose)?;
594    self.require(TokenType::BraceOpen)?;
595    let mut branches = Vec::new();
596    while self.peek()?.typ != TokenType::BraceClose {
597      let mut loc = self.peek()?.loc;
598      let case = if self.consume_if(TokenType::KeywordCase)?.is_match() {
599        Some(self.parse_expr(ctx, TokenType::Colon)?)
600      } else {
601        self.require(TokenType::KeywordDefault)?;
602        None
603      };
604      self.require(TokenType::Colon)?;
605      let mut body = Vec::new();
606      loop {
607        match self.peek()?.typ {
608          TokenType::KeywordCase | TokenType::KeywordDefault | TokenType::BraceClose => break,
609          _ => {
610            let stmt = self.parse_stmt(ctx)?;
611            loc.extend(stmt.loc);
612            body.push(stmt);
613          }
614        }
615      }
616      branches.push(Node::new(loc, Syntax::SwitchBranch { case, body }));
617    }
618    let end = self.require(TokenType::BraceClose)?;
619    Ok(Node::new(start.loc + end.loc, Syntax::SwitchStmt {
620      test,
621      branches,
622    }))
623  }
624}