oxc-project / oxc-project/backlog
AST: Encode `TryStatement` Clauses as an Enum
@camc314 is already working on this.
Since Jul 28, 2026.
- Dominant language
- No language data
- Stars
- 7
- Forks
- 0
- PR merge metrics
- No merged PRs in 30d
Description
Parent: oxc-project/backlog#210
Summary
Replace the independent optional handler and finalizer fields on TryStatement with an enum representing the three forms allowed by the ECMAScript grammar: catch only, finally only, or catch followed by finally.
Motivation
Every try statement must have a catch clause, a finally clause, or both:
try {} catch {}
try {} finally {}
try {} catch {} finally {}
The current Rust AST stores the two clauses independently:
pub handler: Option<Box<'a, CatchClause<'a>>>,
pub finalizer: Option<Box<'a, BlockStatement<'a>>>,
This permits a fourth state which is rejected by the grammar:
handler == None && finalizer == None
Codegen and the formatter interpret the fields independently, so a manually constructed invalid node can print a bare try {}. Semantic and control-flow analysis also has to branch on each optional field rather than on the grammatical form of the statement.
The parser currently creates this invalid state during error recovery. It reports the missing clause, but still constructs a TryStatement with both fields absent:
let handler = self.at(Kind::Catch).then(|| self.parse_catch_clause());
let finalizer = self.eat(Kind::Finally).then(|| self.parse_block());
if handler.is_none() && finalizer.is_none() {
self.error(diagnostics::expect_catch_finally(...));
}
Statement::new_try_statement(..., handler, finalizer, self)
Parser recovery therefore needs an intentional representation rather than relying on a state that is invalid for every successfully parsed program.
Current AST Shape
pub struct TryStatement<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub block: Box<'a, BlockStatement<'a>>,
pub handler: Option<Box<'a, CatchClause<'a>>>,
pub finalizer: Option<Box<'a, BlockStatement<'a>>>,
}
The independent options represent four combinations, while the grammar permits only three.
Proposed AST Shape
Store the grammatical alternatives explicitly:
pub struct TryStatement<'a> {
pub node_id: Cell<NodeId>,
pub span: Span,
pub block: Box<'a, BlockStatement<'a>>,
pub clauses: TryStatementClauses<'a>,
}
pub enum TryStatementClauses<'a> {
Catch {
handler: Box<'a, CatchClause<'a>>,
},
Finally {
finalizer: Box<'a, BlockStatement<'a>>,
},
CatchFinally {
handler: Box<'a, CatchClause<'a>>,
finalizer: Box<'a, BlockStatement<'a>>,
},
}
Syntax Mapping
| Source form | Proposed representation |
|---|---|
try {} catch (error) {} |
Catch { handler } |
try {} catch {} |
Catch { handler } with no catch parameter |
try {} finally {} |
Finally { finalizer } |
try {} catch {} finally {} |
CatchFinally { handler, finalizer } |
try {} |
Invalid input; diagnose and construct an explicit recovery repair, not an empty clause set |
Guaranteed Invariants
- Every valid
TryStatementcontains at least one clause. - Catch always precedes finally when both clauses are present.
- A catch clause and its body remain grouped in
CatchClause. - Codegen cannot emit a bare
try {}from a validTryStatement. - Adding or removing a clause changes the grammatical form atomically.
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Assessment
This issue has not been assessed yet.