main
rs 780 lines 25.4 KB
Raw
1 use serde::Deserialize;
2 use serde::Deserializer;
3 use serde::Serialize;
4 use serde::Serializer;
5 use serde::de::Error as _;
6
7 use crate::common::BaseNode;
8 use crate::common::RawNode;
9 use crate::expressions::Expression;
10 use crate::expressions::Identifier;
11 use crate::patterns::PatternLike;
12
13 fn is_false(v: &bool) -> bool {
14 !v
15 }
16
17 #[derive(Debug, Clone, Serialize)]
18 #[serde(tag = "type")]
19 pub enum Statement {
20 // Statements
21 BlockStatement(BlockStatement),
22 ReturnStatement(ReturnStatement),
23 IfStatement(IfStatement),
24 ForStatement(ForStatement),
25 WhileStatement(WhileStatement),
26 DoWhileStatement(DoWhileStatement),
27 ForInStatement(ForInStatement),
28 ForOfStatement(ForOfStatement),
29 SwitchStatement(SwitchStatement),
30 ThrowStatement(ThrowStatement),
31 TryStatement(TryStatement),
32 BreakStatement(BreakStatement),
33 ContinueStatement(ContinueStatement),
34 LabeledStatement(LabeledStatement),
35 ExpressionStatement(ExpressionStatement),
36 EmptyStatement(EmptyStatement),
37 DebuggerStatement(DebuggerStatement),
38 WithStatement(WithStatement),
39 // Declarations are also statements
40 VariableDeclaration(VariableDeclaration),
41 FunctionDeclaration(FunctionDeclaration),
42 ClassDeclaration(ClassDeclaration),
43 // Import/export declarations
44 ImportDeclaration(crate::declarations::ImportDeclaration),
45 ExportNamedDeclaration(crate::declarations::ExportNamedDeclaration),
46 ExportDefaultDeclaration(crate::declarations::ExportDefaultDeclaration),
47 ExportAllDeclaration(crate::declarations::ExportAllDeclaration),
48 // TypeScript declarations
49 TSTypeAliasDeclaration(crate::declarations::TSTypeAliasDeclaration),
50 TSInterfaceDeclaration(crate::declarations::TSInterfaceDeclaration),
51 TSEnumDeclaration(crate::declarations::TSEnumDeclaration),
52 TSModuleDeclaration(crate::declarations::TSModuleDeclaration),
53 TSDeclareFunction(crate::declarations::TSDeclareFunction),
54 // Flow declarations
55 TypeAlias(crate::declarations::TypeAlias),
56 OpaqueType(crate::declarations::OpaqueType),
57 InterfaceDeclaration(crate::declarations::InterfaceDeclaration),
58 DeclareVariable(crate::declarations::DeclareVariable),
59 DeclareFunction(crate::declarations::DeclareFunction),
60 DeclareClass(crate::declarations::DeclareClass),
61 DeclareModule(crate::declarations::DeclareModule),
62 DeclareModuleExports(crate::declarations::DeclareModuleExports),
63 DeclareExportDeclaration(crate::declarations::DeclareExportDeclaration),
64 DeclareExportAllDeclaration(crate::declarations::DeclareExportAllDeclaration),
65 DeclareInterface(crate::declarations::DeclareInterface),
66 DeclareTypeAlias(crate::declarations::DeclareTypeAlias),
67 DeclareOpaqueType(crate::declarations::DeclareOpaqueType),
68 EnumDeclaration(crate::declarations::EnumDeclaration),
69 /// Catch-all for statement `type`s the typed AST does not model, e.g. the
70 /// TypeScript module-interop statements `import x = require(...)`,
71 /// `export = x`, and `export as namespace X`. Carries the complete raw
72 /// Babel node so the Babel path can preserve unmodeled top-level
73 /// statements verbatim instead of failing the whole file.
74 ///
75 /// Deserialization dispatches through [`KnownStatement`]: a modeled `type`
76 /// whose body is malformed errors with the typed variant's precise message
77 /// rather than degrading to `Unknown`. Adding a variant to this enum
78 /// requires adding it to the `known_statements!` list below, which is the
79 /// single source for the dispatch enum, its `From` mapping, and
80 /// [`KNOWN_STATEMENT_TYPES`]. A variant added here but not there degrades
81 /// to `Unknown` silently; that is the one drift case structure cannot
82 /// catch.
83 #[serde(untagged)]
84 Unknown(UnknownStatement),
85 }
86
87 // NOTE: `Deserialize` for `Statement` is hand-written below; the
88 // `#[serde(tag = "type")]` and `#[serde(untagged)]` attributes on the enum
89 // configure only the derived `Serialize`.
90
91 #[derive(Debug, Clone)]
92 pub struct UnknownStatement {
93 raw: RawNode,
94 base: BaseNode,
95 }
96
97 impl UnknownStatement {
98 pub fn from_raw(raw: RawNode) -> Result<Self, String> {
99 match raw.type_name() {
100 Some(_) => {
101 // Parsing into BaseNode reads only the fields BaseNode declares,
102 // not the whole (arbitrarily large) unknown subtree.
103 let base = crate::common::from_json_str_unbounded::<BaseNode>(raw.get())
104 .map_err(|err| format!("failed to read unknown statement base: {err}"))?;
105 Ok(Self { raw, base })
106 }
107 None => Err("unknown statement is missing a string `type` field".to_string()),
108 }
109 }
110
111 /// The node's `type` discriminant, read from the captured [`BaseNode`].
112 /// Falls back to `"Unknown"` rather than panicking if the raw node was
113 /// mutated out from under it.
114 pub fn node_type(&self) -> &str {
115 self.base.node_type.as_deref().unwrap_or("Unknown")
116 }
117
118 pub fn raw(&self) -> &RawNode {
119 &self.raw
120 }
121
122 /// Mutate the raw node, then refresh the cached [`BaseNode`] so `base()`
123 /// and `node_type()` cannot drift from `raw`. Mutations that remove the
124 /// string `type` field are rejected and rolled back.
125 pub fn with_raw_mut<R>(&mut self, f: impl FnOnce(&mut RawNode) -> R) -> Result<R, String> {
126 let saved = self.raw.clone();
127 let result = f(&mut self.raw);
128 if self.raw.type_name().is_none() {
129 self.raw = saved;
130 return Err("unknown statement mutation removed the string `type` field".to_string());
131 }
132 match crate::common::from_json_str_unbounded::<BaseNode>(self.raw.get()) {
133 Ok(base) => {
134 self.base = base;
135 Ok(result)
136 }
137 Err(err) => {
138 self.raw = saved;
139 Err(format!("failed to refresh unknown statement base: {err}"))
140 }
141 }
142 }
143
144 pub fn base(&self) -> &BaseNode {
145 &self.base
146 }
147 }
148
149 impl Serialize for UnknownStatement {
150 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
151 where
152 S: Serializer,
153 {
154 self.raw.serialize(serializer)
155 }
156 }
157
158 impl<'de> Deserialize<'de> for UnknownStatement {
159 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
160 where
161 D: Deserializer<'de>,
162 {
163 let raw = RawNode::deserialize(deserializer)?;
164 Self::from_raw(raw).map_err(D::Error::custom)
165 }
166 }
167
168 impl<'de> Deserialize<'de> for Statement {
169 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
170 where
171 D: Deserializer<'de>,
172 {
173 let raw = RawNode::deserialize(deserializer)?;
174 let node_type = raw
175 .type_name()
176 .ok_or_else(|| D::Error::custom("statement is missing a string `type` field"))?;
177
178 if is_known_statement_type(&node_type) {
179 let known: KnownStatement =
180 crate::common::from_json_str_unbounded(raw.get()).map_err(D::Error::custom)?;
181 Ok(known.into())
182 } else {
183 UnknownStatement::from_raw(raw)
184 .map(Statement::Unknown)
185 .map_err(D::Error::custom)
186 }
187 }
188 }
189
190 /// Single source of truth for the statement `type` tags [`Statement`] models.
191 /// Generates the [`KnownStatement`] dispatch enum, its `From` mapping, and
192 /// [`KNOWN_STATEMENT_TYPES`] from one list, so the three cannot drift from
193 /// each other. A variant added to [`Statement`] but not listed here still
194 /// degrades to [`Statement::Unknown`] silently; that residual gap is
195 /// documented on the variant.
196 macro_rules! known_statements {
197 ($($variant:ident => $ty:ty),+ $(,)?) => {
198 const KNOWN_STATEMENT_TYPES: &[&str] = &[$(stringify!($variant)),+];
199
200 /// Whether `node_type` is a statement `type` tag modeled by
201 /// [`Statement`], i.e. one that deserializes into a typed variant
202 /// rather than the [`Statement::Unknown`] catch-all. Callers that
203 /// need to discriminate statements from other node kinds must use
204 /// this instead of attempting a `Statement` deserialization: with
205 /// the tolerant catch-all, that attempt succeeds for any object
206 /// carrying a string `type` tag.
207 pub fn is_known_statement_type(node_type: &str) -> bool {
208 KNOWN_STATEMENT_TYPES.contains(&node_type)
209 }
210
211 #[derive(Debug, Deserialize)]
212 #[serde(tag = "type")]
213 enum KnownStatement {
214 $($variant($ty),)+
215 }
216
217 impl From<KnownStatement> for Statement {
218 fn from(value: KnownStatement) -> Self {
219 match value {
220 $(KnownStatement::$variant(s) => Statement::$variant(s),)+
221 }
222 }
223 }
224 };
225 }
226
227 known_statements! {
228 BlockStatement => BlockStatement,
229 ReturnStatement => ReturnStatement,
230 IfStatement => IfStatement,
231 ForStatement => ForStatement,
232 WhileStatement => WhileStatement,
233 DoWhileStatement => DoWhileStatement,
234 ForInStatement => ForInStatement,
235 ForOfStatement => ForOfStatement,
236 SwitchStatement => SwitchStatement,
237 ThrowStatement => ThrowStatement,
238 TryStatement => TryStatement,
239 BreakStatement => BreakStatement,
240 ContinueStatement => ContinueStatement,
241 LabeledStatement => LabeledStatement,
242 ExpressionStatement => ExpressionStatement,
243 EmptyStatement => EmptyStatement,
244 DebuggerStatement => DebuggerStatement,
245 WithStatement => WithStatement,
246 VariableDeclaration => VariableDeclaration,
247 FunctionDeclaration => FunctionDeclaration,
248 ClassDeclaration => ClassDeclaration,
249 ImportDeclaration => crate::declarations::ImportDeclaration,
250 ExportNamedDeclaration => crate::declarations::ExportNamedDeclaration,
251 ExportDefaultDeclaration => crate::declarations::ExportDefaultDeclaration,
252 ExportAllDeclaration => crate::declarations::ExportAllDeclaration,
253 TSTypeAliasDeclaration => crate::declarations::TSTypeAliasDeclaration,
254 TSInterfaceDeclaration => crate::declarations::TSInterfaceDeclaration,
255 TSEnumDeclaration => crate::declarations::TSEnumDeclaration,
256 TSModuleDeclaration => crate::declarations::TSModuleDeclaration,
257 TSDeclareFunction => crate::declarations::TSDeclareFunction,
258 TypeAlias => crate::declarations::TypeAlias,
259 OpaqueType => crate::declarations::OpaqueType,
260 InterfaceDeclaration => crate::declarations::InterfaceDeclaration,
261 DeclareVariable => crate::declarations::DeclareVariable,
262 DeclareFunction => crate::declarations::DeclareFunction,
263 DeclareClass => crate::declarations::DeclareClass,
264 DeclareModule => crate::declarations::DeclareModule,
265 DeclareModuleExports => crate::declarations::DeclareModuleExports,
266 DeclareExportDeclaration => crate::declarations::DeclareExportDeclaration,
267 DeclareExportAllDeclaration => crate::declarations::DeclareExportAllDeclaration,
268 DeclareInterface => crate::declarations::DeclareInterface,
269 DeclareTypeAlias => crate::declarations::DeclareTypeAlias,
270 DeclareOpaqueType => crate::declarations::DeclareOpaqueType,
271 EnumDeclaration => crate::declarations::EnumDeclaration,
272 }
273
274 #[derive(Debug, Clone, Serialize, Deserialize)]
275 pub struct BlockStatement {
276 #[serde(flatten)]
277 pub base: BaseNode,
278 pub body: Vec<Statement>,
279 #[serde(default)]
280 pub directives: Vec<Directive>,
281 }
282
283 #[derive(Debug, Clone, Serialize, Deserialize)]
284 pub struct Directive {
285 #[serde(flatten)]
286 pub base: BaseNode,
287 pub value: DirectiveLiteral,
288 }
289
290 #[derive(Debug, Clone, Serialize, Deserialize)]
291 pub struct DirectiveLiteral {
292 #[serde(flatten)]
293 pub base: BaseNode,
294 pub value: String,
295 }
296
297 #[derive(Debug, Clone, Serialize, Deserialize)]
298 pub struct ReturnStatement {
299 #[serde(flatten)]
300 pub base: BaseNode,
301 pub argument: Option<Box<Expression>>,
302 }
303
304 #[derive(Debug, Clone, Serialize, Deserialize)]
305 pub struct ExpressionStatement {
306 #[serde(flatten)]
307 pub base: BaseNode,
308 pub expression: Box<Expression>,
309 }
310
311 #[derive(Debug, Clone, Serialize, Deserialize)]
312 pub struct IfStatement {
313 #[serde(flatten)]
314 pub base: BaseNode,
315 pub test: Box<Expression>,
316 pub consequent: Box<Statement>,
317 pub alternate: Option<Box<Statement>>,
318 }
319
320 #[derive(Debug, Clone, Serialize, Deserialize)]
321 pub struct ForStatement {
322 #[serde(flatten)]
323 pub base: BaseNode,
324 pub init: Option<Box<ForInit>>,
325 pub test: Option<Box<Expression>>,
326 pub update: Option<Box<Expression>>,
327 pub body: Box<Statement>,
328 }
329
330 #[derive(Debug, Clone, Serialize, Deserialize)]
331 #[serde(tag = "type")]
332 pub enum ForInit {
333 VariableDeclaration(VariableDeclaration),
334 #[serde(untagged)]
335 Expression(Box<Expression>),
336 }
337
338 #[derive(Debug, Clone, Serialize, Deserialize)]
339 pub struct WhileStatement {
340 #[serde(flatten)]
341 pub base: BaseNode,
342 pub test: Box<Expression>,
343 pub body: Box<Statement>,
344 }
345
346 #[derive(Debug, Clone, Serialize, Deserialize)]
347 pub struct DoWhileStatement {
348 #[serde(flatten)]
349 pub base: BaseNode,
350 pub test: Box<Expression>,
351 pub body: Box<Statement>,
352 }
353
354 #[derive(Debug, Clone, Serialize, Deserialize)]
355 pub struct ForInStatement {
356 #[serde(flatten)]
357 pub base: BaseNode,
358 pub left: Box<ForInOfLeft>,
359 pub right: Box<Expression>,
360 pub body: Box<Statement>,
361 }
362
363 #[derive(Debug, Clone, Serialize, Deserialize)]
364 pub struct ForOfStatement {
365 #[serde(flatten)]
366 pub base: BaseNode,
367 pub left: Box<ForInOfLeft>,
368 pub right: Box<Expression>,
369 pub body: Box<Statement>,
370 #[serde(default, rename = "await")]
371 pub is_await: bool,
372 }
373
374 #[derive(Debug, Clone, Serialize, Deserialize)]
375 #[serde(tag = "type")]
376 pub enum ForInOfLeft {
377 VariableDeclaration(VariableDeclaration),
378 #[serde(untagged)]
379 Pattern(Box<PatternLike>),
380 }
381
382 #[derive(Debug, Clone, Serialize, Deserialize)]
383 pub struct SwitchStatement {
384 #[serde(flatten)]
385 pub base: BaseNode,
386 pub discriminant: Box<Expression>,
387 pub cases: Vec<SwitchCase>,
388 }
389
390 #[derive(Debug, Clone, Serialize, Deserialize)]
391 pub struct SwitchCase {
392 #[serde(flatten)]
393 pub base: BaseNode,
394 pub test: Option<Box<Expression>>,
395 pub consequent: Vec<Statement>,
396 }
397
398 #[derive(Debug, Clone, Serialize, Deserialize)]
399 pub struct ThrowStatement {
400 #[serde(flatten)]
401 pub base: BaseNode,
402 pub argument: Box<Expression>,
403 }
404
405 #[derive(Debug, Clone, Serialize, Deserialize)]
406 pub struct TryStatement {
407 #[serde(flatten)]
408 pub base: BaseNode,
409 pub block: BlockStatement,
410 pub handler: Option<CatchClause>,
411 pub finalizer: Option<BlockStatement>,
412 }
413
414 #[derive(Debug, Clone, Serialize, Deserialize)]
415 pub struct CatchClause {
416 #[serde(flatten)]
417 pub base: BaseNode,
418 pub param: Option<PatternLike>,
419 pub body: BlockStatement,
420 }
421
422 #[derive(Debug, Clone, Serialize, Deserialize)]
423 pub struct BreakStatement {
424 #[serde(flatten)]
425 pub base: BaseNode,
426 pub label: Option<Identifier>,
427 }
428
429 #[derive(Debug, Clone, Serialize, Deserialize)]
430 pub struct ContinueStatement {
431 #[serde(flatten)]
432 pub base: BaseNode,
433 pub label: Option<Identifier>,
434 }
435
436 #[derive(Debug, Clone, Serialize, Deserialize)]
437 pub struct LabeledStatement {
438 #[serde(flatten)]
439 pub base: BaseNode,
440 pub label: Identifier,
441 pub body: Box<Statement>,
442 }
443
444 #[derive(Debug, Clone, Serialize, Deserialize)]
445 pub struct EmptyStatement {
446 #[serde(flatten)]
447 pub base: BaseNode,
448 }
449
450 #[derive(Debug, Clone, Serialize, Deserialize)]
451 pub struct DebuggerStatement {
452 #[serde(flatten)]
453 pub base: BaseNode,
454 }
455
456 #[derive(Debug, Clone, Serialize, Deserialize)]
457 pub struct WithStatement {
458 #[serde(flatten)]
459 pub base: BaseNode,
460 pub object: Box<Expression>,
461 pub body: Box<Statement>,
462 }
463
464 #[derive(Debug, Clone, Serialize, Deserialize)]
465 pub struct VariableDeclaration {
466 #[serde(flatten)]
467 pub base: BaseNode,
468 pub declarations: Vec<VariableDeclarator>,
469 pub kind: VariableDeclarationKind,
470 #[serde(default, skip_serializing_if = "Option::is_none")]
471 pub declare: Option<bool>,
472 }
473
474 #[derive(Debug, Clone, Serialize, Deserialize)]
475 #[serde(rename_all = "lowercase")]
476 pub enum VariableDeclarationKind {
477 Var,
478 Let,
479 Const,
480 Using,
481 #[serde(rename = "await using")]
482 AwaitUsing,
483 }
484
485 #[derive(Debug, Clone, Serialize, Deserialize)]
486 pub struct VariableDeclarator {
487 #[serde(flatten)]
488 pub base: BaseNode,
489 pub id: PatternLike,
490 pub init: Option<Box<Expression>>,
491 #[serde(default, skip_serializing_if = "Option::is_none")]
492 pub definite: Option<bool>,
493 }
494
495 #[derive(Debug, Clone, Serialize, Deserialize)]
496 pub struct FunctionDeclaration {
497 #[serde(flatten)]
498 pub base: BaseNode,
499 pub id: Option<Identifier>,
500 pub params: Vec<PatternLike>,
501 pub body: BlockStatement,
502 #[serde(default)]
503 pub generator: bool,
504 #[serde(default, rename = "async")]
505 pub is_async: bool,
506 #[serde(default, skip_serializing_if = "Option::is_none")]
507 pub declare: Option<bool>,
508 #[serde(
509 default,
510 skip_serializing_if = "Option::is_none",
511 rename = "returnType"
512 )]
513 pub return_type: Option<RawNode>,
514 #[serde(
515 default,
516 skip_serializing_if = "Option::is_none",
517 rename = "typeParameters"
518 )]
519 pub type_parameters: Option<RawNode>,
520 #[serde(
521 default,
522 skip_serializing_if = "Option::is_none",
523 rename = "predicate",
524 deserialize_with = "crate::common::nullable_value"
525 )]
526 pub predicate: Option<RawNode>,
527 /// Set by the Hermes parser for Flow `component Foo(...) { ... }` syntax
528 #[serde(
529 default,
530 skip_serializing_if = "is_false",
531 rename = "__componentDeclaration"
532 )]
533 pub component_declaration: bool,
534 /// Set by the Hermes parser for Flow `hook useFoo(...) { ... }` syntax
535 #[serde(
536 default,
537 skip_serializing_if = "is_false",
538 rename = "__hookDeclaration"
539 )]
540 pub hook_declaration: bool,
541 }
542
543 #[derive(Debug, Clone, Serialize, Deserialize)]
544 pub struct ClassDeclaration {
545 #[serde(flatten)]
546 pub base: BaseNode,
547 pub id: Option<Identifier>,
548 #[serde(rename = "superClass")]
549 pub super_class: Option<Box<Expression>>,
550 pub body: crate::expressions::ClassBody,
551 #[serde(default, skip_serializing_if = "Option::is_none")]
552 pub decorators: Option<Vec<RawNode>>,
553 #[serde(default, skip_serializing_if = "Option::is_none", rename = "abstract")]
554 pub is_abstract: Option<bool>,
555 #[serde(default, skip_serializing_if = "Option::is_none")]
556 pub declare: Option<bool>,
557 #[serde(
558 default,
559 skip_serializing_if = "Option::is_none",
560 rename = "implements"
561 )]
562 pub implements: Option<Vec<RawNode>>,
563 #[serde(
564 default,
565 skip_serializing_if = "Option::is_none",
566 rename = "superTypeParameters"
567 )]
568 pub super_type_parameters: Option<RawNode>,
569 #[serde(
570 default,
571 skip_serializing_if = "Option::is_none",
572 rename = "typeParameters"
573 )]
574 pub type_parameters: Option<RawNode>,
575 #[serde(default, skip_serializing_if = "Option::is_none")]
576 pub mixins: Option<Vec<RawNode>>,
577 }
578
579 #[cfg(test)]
580 mod tests {
581 use serde_json::json;
582
583 use super::Statement;
584 use crate::common::RawNode;
585
586 #[test]
587 fn unknown_statement_round_trips_at_program_level() {
588 let input = json!({
589 "type": "File",
590 "comments": [],
591 "errors": [],
592 "program": {
593 "type": "Program",
594 "sourceType": "module",
595 "interpreter": null,
596 "body": [
597 {
598 "type": "TSImportEqualsDeclaration",
599 "start": 0,
600 "end": 39,
601 "importKind": "value",
602 "isExport": false,
603 "id": { "type": "Identifier", "name": "lib" },
604 "moduleReference": {
605 "type": "TSExternalModuleReference",
606 "expression": { "type": "StringLiteral", "value": "shared-runtime" }
607 }
608 }
609 ],
610 "directives": []
611 }
612 });
613
614 let file: crate::File = serde_json::from_value(input.clone()).unwrap();
615
616 match &file.program.body[0] {
617 Statement::Unknown(unknown) => {
618 assert_eq!(unknown.node_type(), "TSImportEqualsDeclaration");
619 }
620 other => panic!("expected Unknown, got {other:?}"),
621 }
622 assert_eq!(serde_json::to_value(&file).unwrap(), input);
623 }
624
625 #[test]
626 fn unknown_statement_round_trips_inside_function_block() {
627 let input = json!({
628 "type": "FunctionDeclaration",
629 "id": null,
630 "generator": false,
631 "async": false,
632 "params": [],
633 "body": {
634 "type": "BlockStatement",
635 "body": [
636 {
637 "type": "TSExportAssignment",
638 "expression": { "type": "Identifier", "name": "x" }
639 }
640 ],
641 "directives": []
642 }
643 });
644
645 let stmt: Statement = serde_json::from_value(input.clone()).unwrap();
646 let Statement::FunctionDeclaration(function) = &stmt else {
647 panic!("expected function declaration, got {stmt:?}");
648 };
649 assert!(matches!(function.body.body[0], Statement::Unknown(_)));
650 assert_eq!(serde_json::to_value(&stmt).unwrap(), input);
651 }
652
653 /// The public discrimination helper mirrors the deserializer's dispatch:
654 /// exactly the macro-listed statement tags are "known".
655 #[test]
656 fn is_known_statement_type_matches_macro_list() {
657 assert!(super::is_known_statement_type("IfStatement"));
658 assert!(super::is_known_statement_type("VariableDeclaration"));
659 assert!(!super::is_known_statement_type("CallExpression"));
660 assert!(!super::is_known_statement_type("TSImportEqualsDeclaration"));
661 }
662
663 #[test]
664 fn known_statement_type_uses_typed_variant() {
665 let stmt: Statement = serde_json::from_value(json!({
666 "type": "EmptyStatement"
667 }))
668 .unwrap();
669
670 assert!(matches!(stmt, Statement::EmptyStatement(_)));
671 }
672
673 /// Babel serializes `using`/`await using` declarations as ordinary
674 /// VariableDeclarations whose `kind` is "using" / "await using" (with a
675 /// space). Both must round-trip so the NAPI boundary does not reject
676 /// files containing them.
677 #[test]
678 fn using_declaration_kinds_round_trip() {
679 for kind in ["using", "await using"] {
680 let input = json!({
681 "type": "VariableDeclaration",
682 "kind": kind,
683 "declarations": [
684 {
685 "type": "VariableDeclarator",
686 "id": { "type": "Identifier", "name": "resource" },
687 "init": { "type": "NullLiteral" }
688 }
689 ]
690 });
691
692 let stmt: Statement = serde_json::from_value(input.clone()).unwrap();
693 assert!(matches!(stmt, Statement::VariableDeclaration(_)));
694 assert_eq!(serde_json::to_value(&stmt).unwrap()["kind"], json!(kind));
695 }
696 }
697
698 #[test]
699 fn malformed_known_statement_type_errors() {
700 let err = serde_json::from_value::<Statement>(json!({
701 "type": "IfStatement",
702 "consequent": {
703 "type": "EmptyStatement"
704 }
705 }))
706 .unwrap_err();
707
708 assert!(
709 err.to_string().contains("missing field `test`"),
710 "unexpected error: {err}"
711 );
712 }
713
714 #[test]
715 fn statement_without_type_field_errors() {
716 let err = serde_json::from_value::<Statement>(json!({
717 "start": 0,
718 "end": 1
719 }))
720 .unwrap_err();
721
722 assert!(
723 err.to_string().contains("`type`"),
724 "unexpected error: {err}"
725 );
726 }
727
728 #[test]
729 fn non_object_statement_errors() {
730 let err = serde_json::from_value::<Statement>(json!([1, 2])).unwrap_err();
731 assert!(
732 err.to_string().contains("`type`"),
733 "unexpected error: {err}"
734 );
735 }
736
737 #[test]
738 fn non_string_type_field_errors() {
739 let err = serde_json::from_value::<Statement>(json!({ "type": 7 })).unwrap_err();
740 assert!(
741 err.to_string().contains("`type`"),
742 "unexpected error: {err}"
743 );
744 }
745
746 /// Mutating the raw node through the scoped mutator refreshes the cached
747 /// base, and mutations that strip `type` are rejected.
748 #[test]
749 fn with_raw_mut_refreshes_base_and_guards_type() {
750 let raw = json!({
751 "type": "TSExportAssignment",
752 "start": 5,
753 "expression": { "type": "Identifier", "name": "x" }
754 });
755 let Statement::Unknown(mut unknown) = serde_json::from_value(raw).unwrap() else {
756 panic!("expected Unknown");
757 };
758
759 unknown
760 .with_raw_mut(|v| {
761 let mut parsed = v.parse_value();
762 parsed["start"] = json!(9);
763 parsed["expression"]["name"] = json!("y");
764 *v = RawNode::from_value(&parsed);
765 })
766 .unwrap();
767 assert_eq!(unknown.base().start, Some(9));
768 assert_eq!(
769 unknown.raw().parse_value()["expression"]["name"],
770 json!("y")
771 );
772
773 let err = unknown.with_raw_mut(|v| {
774 let mut parsed = v.parse_value();
775 parsed.as_object_mut().unwrap().remove("type");
776 *v = RawNode::from_value(&parsed);
777 });
778 assert!(err.is_err(), "type removal must be rejected");
779 }
780 }