1use serde::de::Error as _;
2use serde::{Deserialize, Deserializer, Serialize, Serializer};
3
4use crate::common::BaseNode;
5use crate::common::RawNode;
6
7use crate::expressions::{Expression, Identifier};
8use crate::patterns::PatternLike;
9
10fn is_false(v: &bool) -> bool {
11 !v
12}
13
14#[derive(Debug, Clone, Serialize)]
15#[serde(tag = "type")]
16pub enum Statement {
17 BlockStatement(BlockStatement),
19 ReturnStatement(ReturnStatement),
20 IfStatement(IfStatement),
21 ForStatement(ForStatement),
22 WhileStatement(WhileStatement),
23 DoWhileStatement(DoWhileStatement),
24 ForInStatement(ForInStatement),
25 ForOfStatement(ForOfStatement),
26 SwitchStatement(SwitchStatement),
27 ThrowStatement(ThrowStatement),
28 TryStatement(TryStatement),
29 BreakStatement(BreakStatement),
30 ContinueStatement(ContinueStatement),
31 LabeledStatement(LabeledStatement),
32 ExpressionStatement(ExpressionStatement),
33 EmptyStatement(EmptyStatement),
34 DebuggerStatement(DebuggerStatement),
35 WithStatement(WithStatement),
36 VariableDeclaration(VariableDeclaration),
38 FunctionDeclaration(FunctionDeclaration),
39 ClassDeclaration(ClassDeclaration),
40 ImportDeclaration(crate::declarations::ImportDeclaration),
42 ExportNamedDeclaration(crate::declarations::ExportNamedDeclaration),
43 ExportDefaultDeclaration(crate::declarations::ExportDefaultDeclaration),
44 ExportAllDeclaration(crate::declarations::ExportAllDeclaration),
45 TSTypeAliasDeclaration(crate::declarations::TSTypeAliasDeclaration),
47 TSInterfaceDeclaration(crate::declarations::TSInterfaceDeclaration),
48 TSEnumDeclaration(crate::declarations::TSEnumDeclaration),
49 TSModuleDeclaration(crate::declarations::TSModuleDeclaration),
50 TSDeclareFunction(crate::declarations::TSDeclareFunction),
51 TypeAlias(crate::declarations::TypeAlias),
53 OpaqueType(crate::declarations::OpaqueType),
54 InterfaceDeclaration(crate::declarations::InterfaceDeclaration),
55 DeclareVariable(crate::declarations::DeclareVariable),
56 DeclareFunction(crate::declarations::DeclareFunction),
57 DeclareClass(crate::declarations::DeclareClass),
58 DeclareModule(crate::declarations::DeclareModule),
59 DeclareModuleExports(crate::declarations::DeclareModuleExports),
60 DeclareExportDeclaration(crate::declarations::DeclareExportDeclaration),
61 DeclareExportAllDeclaration(crate::declarations::DeclareExportAllDeclaration),
62 DeclareInterface(crate::declarations::DeclareInterface),
63 DeclareTypeAlias(crate::declarations::DeclareTypeAlias),
64 DeclareOpaqueType(crate::declarations::DeclareOpaqueType),
65 EnumDeclaration(crate::declarations::EnumDeclaration),
66 #[serde(untagged)]
81 Unknown(UnknownStatement),
82}
83
84#[derive(Debug, Clone)]
89pub struct UnknownStatement {
90 raw: RawNode,
91 base: BaseNode,
92}
93
94impl UnknownStatement {
95 pub fn from_raw(raw: RawNode) -> Result<Self, String> {
96 match raw.type_name() {
97 Some(_) => {
98 let base = crate::common::from_json_str_unbounded::<BaseNode>(raw.get())
101 .map_err(|err| format!("failed to read unknown statement base: {err}"))?;
102 Ok(Self { raw, base })
103 }
104 None => Err("unknown statement is missing a string `type` field".to_string()),
105 }
106 }
107
108 pub fn node_type(&self) -> &str {
112 self.base.node_type.as_deref().unwrap_or("Unknown")
113 }
114
115 pub fn raw(&self) -> &RawNode {
116 &self.raw
117 }
118
119 pub fn with_raw_mut<R>(
123 &mut self,
124 f: impl FnOnce(&mut RawNode) -> R,
125 ) -> 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
149impl 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
158impl<'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
168impl<'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
190macro_rules! known_statements {
197 ($($variant:ident => $ty:ty),+ $(,)?) => {
198 const KNOWN_STATEMENT_TYPES: &[&str] = &[$(stringify!($variant)),+];
199
200 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
227known_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)]
275pub 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)]
284pub struct Directive {
285 #[serde(flatten)]
286 pub base: BaseNode,
287 pub value: DirectiveLiteral,
288}
289
290#[derive(Debug, Clone, Serialize, Deserialize)]
291pub struct DirectiveLiteral {
292 #[serde(flatten)]
293 pub base: BaseNode,
294 pub value: String,
295}
296
297#[derive(Debug, Clone, Serialize, Deserialize)]
298pub struct ReturnStatement {
299 #[serde(flatten)]
300 pub base: BaseNode,
301 pub argument: Option<Box<Expression>>,
302}
303
304#[derive(Debug, Clone, Serialize, Deserialize)]
305pub struct ExpressionStatement {
306 #[serde(flatten)]
307 pub base: BaseNode,
308 pub expression: Box<Expression>,
309}
310
311#[derive(Debug, Clone, Serialize, Deserialize)]
312pub 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)]
321pub 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")]
332pub enum ForInit {
333 VariableDeclaration(VariableDeclaration),
334 #[serde(untagged)]
335 Expression(Box<Expression>),
336}
337
338#[derive(Debug, Clone, Serialize, Deserialize)]
339pub 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)]
347pub 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)]
355pub 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)]
364pub 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")]
376pub enum ForInOfLeft {
377 VariableDeclaration(VariableDeclaration),
378 #[serde(untagged)]
379 Pattern(Box<PatternLike>),
380}
381
382#[derive(Debug, Clone, Serialize, Deserialize)]
383pub 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)]
391pub 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)]
399pub struct ThrowStatement {
400 #[serde(flatten)]
401 pub base: BaseNode,
402 pub argument: Box<Expression>,
403}
404
405#[derive(Debug, Clone, Serialize, Deserialize)]
406pub 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)]
415pub 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)]
423pub struct BreakStatement {
424 #[serde(flatten)]
425 pub base: BaseNode,
426 pub label: Option<Identifier>,
427}
428
429#[derive(Debug, Clone, Serialize, Deserialize)]
430pub struct ContinueStatement {
431 #[serde(flatten)]
432 pub base: BaseNode,
433 pub label: Option<Identifier>,
434}
435
436#[derive(Debug, Clone, Serialize, Deserialize)]
437pub 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)]
445pub struct EmptyStatement {
446 #[serde(flatten)]
447 pub base: BaseNode,
448}
449
450#[derive(Debug, Clone, Serialize, Deserialize)]
451pub struct DebuggerStatement {
452 #[serde(flatten)]
453 pub base: BaseNode,
454}
455
456#[derive(Debug, Clone, Serialize, Deserialize)]
457pub 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)]
465pub 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")]
476pub enum VariableDeclarationKind {
477 Var,
478 Let,
479 Const,
480 Using,
481}
482
483#[derive(Debug, Clone, Serialize, Deserialize)]
484pub struct VariableDeclarator {
485 #[serde(flatten)]
486 pub base: BaseNode,
487 pub id: PatternLike,
488 pub init: Option<Box<Expression>>,
489 #[serde(default, skip_serializing_if = "Option::is_none")]
490 pub definite: Option<bool>,
491}
492
493#[derive(Debug, Clone, Serialize, Deserialize)]
494pub struct FunctionDeclaration {
495 #[serde(flatten)]
496 pub base: BaseNode,
497 pub id: Option<Identifier>,
498 pub params: Vec<PatternLike>,
499 pub body: BlockStatement,
500 #[serde(default)]
501 pub generator: bool,
502 #[serde(default, rename = "async")]
503 pub is_async: bool,
504 #[serde(default, skip_serializing_if = "Option::is_none")]
505 pub declare: Option<bool>,
506 #[serde(
507 default,
508 skip_serializing_if = "Option::is_none",
509 rename = "returnType"
510 )]
511 pub return_type: Option<RawNode>,
512 #[serde(
513 default,
514 skip_serializing_if = "Option::is_none",
515 rename = "typeParameters"
516 )]
517 pub type_parameters: Option<RawNode>,
518 #[serde(
519 default,
520 skip_serializing_if = "Option::is_none",
521 rename = "predicate",
522 deserialize_with = "crate::common::nullable_value"
523 )]
524 pub predicate: Option<RawNode>,
525 #[serde(
527 default,
528 skip_serializing_if = "is_false",
529 rename = "__componentDeclaration"
530 )]
531 pub component_declaration: bool,
532 #[serde(
534 default,
535 skip_serializing_if = "is_false",
536 rename = "__hookDeclaration"
537 )]
538 pub hook_declaration: bool,
539}
540
541#[derive(Debug, Clone, Serialize, Deserialize)]
542pub struct ClassDeclaration {
543 #[serde(flatten)]
544 pub base: BaseNode,
545 pub id: Option<Identifier>,
546 #[serde(rename = "superClass")]
547 pub super_class: Option<Box<Expression>>,
548 pub body: crate::expressions::ClassBody,
549 #[serde(default, skip_serializing_if = "Option::is_none")]
550 pub decorators: Option<Vec<RawNode>>,
551 #[serde(default, skip_serializing_if = "Option::is_none", rename = "abstract")]
552 pub is_abstract: Option<bool>,
553 #[serde(default, skip_serializing_if = "Option::is_none")]
554 pub declare: Option<bool>,
555 #[serde(
556 default,
557 skip_serializing_if = "Option::is_none",
558 rename = "implements"
559 )]
560 pub implements: Option<Vec<RawNode>>,
561 #[serde(
562 default,
563 skip_serializing_if = "Option::is_none",
564 rename = "superTypeParameters"
565 )]
566 pub super_type_parameters: Option<RawNode>,
567 #[serde(
568 default,
569 skip_serializing_if = "Option::is_none",
570 rename = "typeParameters"
571 )]
572 pub type_parameters: Option<RawNode>,
573 #[serde(default, skip_serializing_if = "Option::is_none")]
574 pub mixins: Option<Vec<RawNode>>,
575}
576
577#[cfg(test)]
578mod tests {
579 use serde_json::json;
580
581 use super::Statement;
582 use crate::common::RawNode;
583
584 #[test]
585 fn unknown_statement_round_trips_at_program_level() {
586 let input = json!({
587 "type": "File",
588 "comments": [],
589 "errors": [],
590 "program": {
591 "type": "Program",
592 "sourceType": "module",
593 "interpreter": null,
594 "body": [
595 {
596 "type": "TSImportEqualsDeclaration",
597 "start": 0,
598 "end": 39,
599 "importKind": "value",
600 "isExport": false,
601 "id": { "type": "Identifier", "name": "lib" },
602 "moduleReference": {
603 "type": "TSExternalModuleReference",
604 "expression": { "type": "StringLiteral", "value": "shared-runtime" }
605 }
606 }
607 ],
608 "directives": []
609 }
610 });
611
612 let file: crate::File = serde_json::from_value(input.clone()).unwrap();
613
614 match &file.program.body[0] {
615 Statement::Unknown(unknown) => {
616 assert_eq!(unknown.node_type(), "TSImportEqualsDeclaration");
617 }
618 other => panic!("expected Unknown, got {other:?}"),
619 }
620 assert_eq!(serde_json::to_value(&file).unwrap(), input);
621 }
622
623 #[test]
624 fn unknown_statement_round_trips_inside_function_block() {
625 let input = json!({
626 "type": "FunctionDeclaration",
627 "id": null,
628 "generator": false,
629 "async": false,
630 "params": [],
631 "body": {
632 "type": "BlockStatement",
633 "body": [
634 {
635 "type": "TSExportAssignment",
636 "expression": { "type": "Identifier", "name": "x" }
637 }
638 ],
639 "directives": []
640 }
641 });
642
643 let stmt: Statement = serde_json::from_value(input.clone()).unwrap();
644 let Statement::FunctionDeclaration(function) = &stmt else {
645 panic!("expected function declaration, got {stmt:?}");
646 };
647 assert!(matches!(function.body.body[0], Statement::Unknown(_)));
648 assert_eq!(serde_json::to_value(&stmt).unwrap(), input);
649 }
650
651 #[test]
654 fn is_known_statement_type_matches_macro_list() {
655 assert!(super::is_known_statement_type("IfStatement"));
656 assert!(super::is_known_statement_type("VariableDeclaration"));
657 assert!(!super::is_known_statement_type("CallExpression"));
658 assert!(!super::is_known_statement_type("TSImportEqualsDeclaration"));
659 }
660
661 #[test]
662 fn known_statement_type_uses_typed_variant() {
663 let stmt: Statement = serde_json::from_value(json!({
664 "type": "EmptyStatement"
665 }))
666 .unwrap();
667
668 assert!(matches!(stmt, Statement::EmptyStatement(_)));
669 }
670
671 #[test]
672 fn malformed_known_statement_type_errors() {
673 let err = serde_json::from_value::<Statement>(json!({
674 "type": "IfStatement",
675 "consequent": {
676 "type": "EmptyStatement"
677 }
678 }))
679 .unwrap_err();
680
681 assert!(
682 err.to_string().contains("missing field `test`"),
683 "unexpected error: {err}"
684 );
685 }
686
687 #[test]
688 fn statement_without_type_field_errors() {
689 let err = serde_json::from_value::<Statement>(json!({
690 "start": 0,
691 "end": 1
692 }))
693 .unwrap_err();
694
695 assert!(
696 err.to_string().contains("`type`"),
697 "unexpected error: {err}"
698 );
699 }
700
701 #[test]
702 fn non_object_statement_errors() {
703 let err = serde_json::from_value::<Statement>(json!([1, 2])).unwrap_err();
704 assert!(
705 err.to_string().contains("`type`"),
706 "unexpected error: {err}"
707 );
708 }
709
710 #[test]
711 fn non_string_type_field_errors() {
712 let err = serde_json::from_value::<Statement>(json!({ "type": 7 })).unwrap_err();
713 assert!(
714 err.to_string().contains("`type`"),
715 "unexpected error: {err}"
716 );
717 }
718
719 #[test]
722 fn with_raw_mut_refreshes_base_and_guards_type() {
723 let raw = json!({
724 "type": "TSExportAssignment",
725 "start": 5,
726 "expression": { "type": "Identifier", "name": "x" }
727 });
728 let Statement::Unknown(mut unknown) = serde_json::from_value(raw).unwrap() else {
729 panic!("expected Unknown");
730 };
731
732 unknown
733 .with_raw_mut(|v| {
734 let mut parsed = v.parse_value();
735 parsed["start"] = json!(9);
736 parsed["expression"]["name"] = json!("y");
737 *v = RawNode::from_value(&parsed);
738 })
739 .unwrap();
740 assert_eq!(unknown.base().start, Some(9));
741 assert_eq!(
742 unknown.raw().parse_value()["expression"]["name"],
743 json!("y")
744 );
745
746 let err = unknown.with_raw_mut(|v| {
747 let mut parsed = v.parse_value();
748 parsed.as_object_mut().unwrap().remove("type");
749 *v = RawNode::from_value(&parsed);
750 });
751 assert!(err.is_err(), "type removal must be rejected");
752 }
753}