swc_ecma_ast/
stmt.rs

1use is_macro::Is;
2use swc_common::{ast_node, util::take::Take, EqIgnoreSpan, Span, SyntaxContext, DUMMY_SP};
3
4use crate::{
5    decl::{Decl, VarDecl},
6    expr::Expr,
7    pat::Pat,
8    Ident, Lit, Str, UsingDecl,
9};
10
11/// Use when only block statements are allowed.
12#[ast_node("BlockStatement")]
13#[derive(Eq, Hash, EqIgnoreSpan, Default)]
14#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
15#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
16pub struct BlockStmt {
17    /// Span including the braces.
18    pub span: Span,
19
20    pub ctxt: SyntaxContext,
21
22    pub stmts: Vec<Stmt>,
23}
24
25impl Take for BlockStmt {
26    fn dummy() -> Self {
27        BlockStmt {
28            span: DUMMY_SP,
29            stmts: Vec::new(),
30            ctxt: Default::default(),
31        }
32    }
33}
34
35#[ast_node(no_clone)]
36#[derive(Eq, Hash, Is, EqIgnoreSpan)]
37#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
38#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
39pub enum Stmt {
40    #[tag("BlockStatement")]
41    Block(BlockStmt),
42
43    #[tag("EmptyStatement")]
44    Empty(EmptyStmt),
45
46    #[tag("DebuggerStatement")]
47    Debugger(DebuggerStmt),
48
49    #[tag("WithStatement")]
50    With(WithStmt),
51
52    #[tag("ReturnStatement")]
53    #[is(name = "return_stmt")]
54    Return(ReturnStmt),
55
56    #[tag("LabeledStatement")]
57    Labeled(LabeledStmt),
58
59    #[tag("BreakStatement")]
60    #[is(name = "break_stmt")]
61    Break(BreakStmt),
62
63    #[tag("ContinueStatement")]
64    #[is(name = "continue_stmt")]
65    Continue(ContinueStmt),
66
67    #[tag("IfStatement")]
68    #[is(name = "if_stmt")]
69    If(IfStmt),
70
71    #[tag("SwitchStatement")]
72    Switch(SwitchStmt),
73
74    #[tag("ThrowStatement")]
75    Throw(ThrowStmt),
76
77    /// A try statement. If handler is null then finalizer must be a BlockStmt.
78    #[tag("TryStatement")]
79    #[is(name = "try_stmt")]
80    Try(Box<TryStmt>),
81
82    #[tag("WhileStatement")]
83    #[is(name = "while_stmt")]
84    While(WhileStmt),
85
86    #[tag("DoWhileStatement")]
87    DoWhile(DoWhileStmt),
88
89    #[tag("ForStatement")]
90    #[is(name = "for_stmt")]
91    For(ForStmt),
92
93    #[tag("ForInStatement")]
94    ForIn(ForInStmt),
95
96    #[tag("ForOfStatement")]
97    ForOf(ForOfStmt),
98
99    #[tag("ClassDeclaration")]
100    #[tag("FunctionDeclaration")]
101    #[tag("VariableDeclaration")]
102    #[tag("TsInterfaceDeclaration")]
103    #[tag("TsTypeAliasDeclaration")]
104    #[tag("TsEnumDeclaration")]
105    #[tag("TsModuleDeclaration")]
106    #[tag("UsingDeclaration")]
107    Decl(Decl),
108
109    #[tag("ExpressionStatement")]
110    Expr(ExprStmt),
111}
112
113boxed!(Stmt, [TryStmt]);
114
115macro_rules! stmt_from {
116    ($($varant_ty:ty),*) => {
117        $(
118            bridge_from!(Box<crate::Stmt>, crate::Stmt, $varant_ty);
119            bridge_from!(crate::ModuleItem, crate::Stmt, $varant_ty);
120        )*
121    };
122}
123
124stmt_from!(
125    ExprStmt,
126    BlockStmt,
127    EmptyStmt,
128    DebuggerStmt,
129    WithStmt,
130    ReturnStmt,
131    LabeledStmt,
132    BreakStmt,
133    ContinueStmt,
134    IfStmt,
135    SwitchStmt,
136    ThrowStmt,
137    TryStmt,
138    WhileStmt,
139    DoWhileStmt,
140    ForStmt,
141    ForInStmt,
142    ForOfStmt,
143    Decl
144);
145
146impl Stmt {
147    pub fn is_use_strict(&self) -> bool {
148        match self {
149            Stmt::Expr(expr) => match *expr.expr {
150                Expr::Lit(Lit::Str(Str { ref raw, .. })) => {
151                    matches!(raw, Some(value) if value == "\"use strict\"" || value == "'use strict'")
152                }
153                _ => false,
154            },
155            _ => false,
156        }
157    }
158
159    /// Returns true if the statement does not prevent the directives below
160    /// `self` from being directives.
161    pub fn can_precede_directive(&self) -> bool {
162        match self {
163            Stmt::Expr(expr) => matches!(*expr.expr, Expr::Lit(Lit::Str(_))),
164            _ => false,
165        }
166    }
167}
168
169// Memory layout depedns on the version of rustc.
170// #[cfg(target_pointer_width = "64")]
171// assert_eq_size!(Stmt, [u8; 56]);
172
173// Implement Clone without inline to avoid multiple copies of the
174// implementation.
175impl Clone for Stmt {
176    fn clone(&self) -> Self {
177        use Stmt::*;
178        match self {
179            #[cfg(all(swc_ast_unknown, feature = "encoding-impl"))]
180            Unknown(tag, v) => Unknown(*tag, v.clone()),
181            Block(s) => Block(s.clone()),
182            Empty(s) => Empty(s.clone()),
183            Debugger(s) => Debugger(s.clone()),
184            With(s) => With(s.clone()),
185            Return(s) => Return(s.clone()),
186            Labeled(s) => Labeled(s.clone()),
187            Break(s) => Break(s.clone()),
188            Continue(s) => Continue(s.clone()),
189            If(s) => If(s.clone()),
190            Switch(s) => Switch(s.clone()),
191            Throw(s) => Throw(s.clone()),
192            Try(s) => Try(s.clone()),
193            While(s) => While(s.clone()),
194            DoWhile(s) => DoWhile(s.clone()),
195            For(s) => For(s.clone()),
196            ForIn(s) => ForIn(s.clone()),
197            ForOf(s) => ForOf(s.clone()),
198            Decl(s) => Decl(s.clone()),
199            Expr(s) => Expr(s.clone()),
200        }
201    }
202}
203
204impl Default for Stmt {
205    fn default() -> Self {
206        Self::Empty(EmptyStmt { span: DUMMY_SP })
207    }
208}
209
210impl Take for Stmt {
211    fn dummy() -> Self {
212        Default::default()
213    }
214}
215
216#[ast_node("ExpressionStatement")]
217#[derive(Eq, Hash, EqIgnoreSpan, Default)]
218#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
219#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
220pub struct ExprStmt {
221    pub span: Span,
222    #[cfg_attr(feature = "serde-impl", serde(rename = "expression"))]
223    pub expr: Box<Expr>,
224}
225
226#[ast_node("EmptyStatement")]
227#[derive(Eq, Hash, Copy, EqIgnoreSpan)]
228#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
229#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
230pub struct EmptyStmt {
231    /// Span of semicolon.
232    pub span: Span,
233}
234
235#[ast_node("DebuggerStatement")]
236#[derive(Eq, Hash, Copy, EqIgnoreSpan)]
237#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
238#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
239pub struct DebuggerStmt {
240    pub span: Span,
241}
242
243#[ast_node("WithStatement")]
244#[derive(Eq, Hash, EqIgnoreSpan, Default)]
245#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
246#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
247pub struct WithStmt {
248    pub span: Span,
249    #[cfg_attr(feature = "serde-impl", serde(rename = "object"))]
250    pub obj: Box<Expr>,
251    pub body: Box<Stmt>,
252}
253
254#[ast_node("ReturnStatement")]
255#[derive(Eq, Hash, EqIgnoreSpan, Default)]
256#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
257#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
258pub struct ReturnStmt {
259    pub span: Span,
260    #[cfg_attr(feature = "serde-impl", serde(default, rename = "argument"))]
261    #[cfg_attr(
262        feature = "encoding-impl",
263        encoding(with = "cbor4ii::core::types::Maybe")
264    )]
265    pub arg: Option<Box<Expr>>,
266}
267
268#[ast_node("LabeledStatement")]
269#[derive(Eq, Hash, EqIgnoreSpan, Default)]
270#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
271#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
272pub struct LabeledStmt {
273    pub span: Span,
274    pub label: Ident,
275    pub body: Box<Stmt>,
276}
277
278#[ast_node("BreakStatement")]
279#[derive(Eq, Hash, EqIgnoreSpan, Default)]
280#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
281#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
282pub struct BreakStmt {
283    pub span: Span,
284    #[cfg_attr(feature = "serde-impl", serde(default))]
285    #[cfg_attr(
286        feature = "encoding-impl",
287        encoding(with = "cbor4ii::core::types::Maybe")
288    )]
289    pub label: Option<Ident>,
290}
291
292#[ast_node("ContinueStatement")]
293#[derive(Eq, Hash, EqIgnoreSpan, Default)]
294#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
295#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
296pub struct ContinueStmt {
297    pub span: Span,
298    #[cfg_attr(feature = "serde-impl", serde(default))]
299    #[cfg_attr(
300        feature = "encoding-impl",
301        encoding(with = "cbor4ii::core::types::Maybe")
302    )]
303    pub label: Option<Ident>,
304}
305
306#[ast_node("IfStatement")]
307#[derive(Eq, Hash, EqIgnoreSpan, Default)]
308#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
309#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
310pub struct IfStmt {
311    pub span: Span,
312    pub test: Box<Expr>,
313
314    #[cfg_attr(feature = "serde-impl", serde(rename = "consequent"))]
315    pub cons: Box<Stmt>,
316
317    #[cfg_attr(feature = "serde-impl", serde(default, rename = "alternate"))]
318    #[cfg_attr(
319        feature = "encoding-impl",
320        encoding(with = "cbor4ii::core::types::Maybe")
321    )]
322    pub alt: Option<Box<Stmt>>,
323}
324
325#[ast_node("SwitchStatement")]
326#[derive(Eq, Hash, EqIgnoreSpan, Default)]
327#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
328#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
329pub struct SwitchStmt {
330    pub span: Span,
331    pub discriminant: Box<Expr>,
332    pub cases: Vec<SwitchCase>,
333}
334
335#[ast_node("ThrowStatement")]
336#[derive(Eq, Hash, EqIgnoreSpan, Default)]
337#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
338#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
339pub struct ThrowStmt {
340    pub span: Span,
341    #[cfg_attr(feature = "serde-impl", serde(rename = "argument"))]
342    pub arg: Box<Expr>,
343}
344
345#[ast_node("TryStatement")]
346#[derive(Eq, Hash, EqIgnoreSpan, Default)]
347#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
348#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
349pub struct TryStmt {
350    pub span: Span,
351
352    pub block: BlockStmt,
353
354    #[cfg_attr(feature = "serde-impl", serde(default))]
355    #[cfg_attr(
356        feature = "encoding-impl",
357        encoding(with = "cbor4ii::core::types::Maybe")
358    )]
359    pub handler: Option<CatchClause>,
360
361    #[cfg_attr(feature = "serde-impl", serde(default))]
362    #[cfg_attr(
363        feature = "encoding-impl",
364        encoding(with = "cbor4ii::core::types::Maybe")
365    )]
366    pub finalizer: Option<BlockStmt>,
367}
368
369#[ast_node("WhileStatement")]
370#[derive(Eq, Hash, EqIgnoreSpan, Default)]
371#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
372#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
373pub struct WhileStmt {
374    pub span: Span,
375    pub test: Box<Expr>,
376    pub body: Box<Stmt>,
377}
378
379#[ast_node("DoWhileStatement")]
380#[derive(Eq, Hash, EqIgnoreSpan, Default)]
381#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
382#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
383pub struct DoWhileStmt {
384    pub span: Span,
385    pub test: Box<Expr>,
386    pub body: Box<Stmt>,
387}
388
389#[ast_node("ForStatement")]
390#[derive(Eq, Hash, EqIgnoreSpan, Default)]
391#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
392#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
393pub struct ForStmt {
394    pub span: Span,
395
396    #[cfg_attr(feature = "serde-impl", serde(default))]
397    #[cfg_attr(
398        feature = "encoding-impl",
399        encoding(with = "cbor4ii::core::types::Maybe")
400    )]
401    pub init: Option<VarDeclOrExpr>,
402
403    #[cfg_attr(feature = "serde-impl", serde(default))]
404    #[cfg_attr(
405        feature = "encoding-impl",
406        encoding(with = "cbor4ii::core::types::Maybe")
407    )]
408    pub test: Option<Box<Expr>>,
409
410    #[cfg_attr(feature = "serde-impl", serde(default))]
411    #[cfg_attr(
412        feature = "encoding-impl",
413        encoding(with = "cbor4ii::core::types::Maybe")
414    )]
415    pub update: Option<Box<Expr>>,
416
417    pub body: Box<Stmt>,
418}
419
420#[ast_node("ForInStatement")]
421#[derive(Eq, Hash, EqIgnoreSpan, Default)]
422#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
423#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
424pub struct ForInStmt {
425    pub span: Span,
426    pub left: ForHead,
427    pub right: Box<Expr>,
428    pub body: Box<Stmt>,
429}
430
431#[ast_node("ForOfStatement")]
432#[derive(Eq, Hash, EqIgnoreSpan, Default)]
433#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
434#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
435pub struct ForOfStmt {
436    pub span: Span,
437    /// Span of the await token.
438    ///
439    /// es2018
440    ///
441    /// for-await-of statements, e.g., `for await (const x of xs) {`
442    #[cfg_attr(feature = "serde-impl", serde(default, rename = "await"))]
443    pub is_await: bool,
444    pub left: ForHead,
445    pub right: Box<Expr>,
446    pub body: Box<Stmt>,
447}
448
449impl Take for ForOfStmt {
450    fn dummy() -> Self {
451        Default::default()
452    }
453}
454
455#[ast_node("SwitchCase")]
456#[derive(Eq, Hash, EqIgnoreSpan, Default)]
457#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
458#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
459pub struct SwitchCase {
460    pub span: Span,
461
462    /// None for `default:`
463    #[cfg_attr(feature = "serde-impl", serde(default))]
464    #[cfg_attr(
465        feature = "encoding-impl",
466        encoding(with = "cbor4ii::core::types::Maybe")
467    )]
468    pub test: Option<Box<Expr>>,
469
470    #[cfg_attr(feature = "serde-impl", serde(rename = "consequent"))]
471    pub cons: Vec<Stmt>,
472}
473
474impl Take for SwitchCase {
475    fn dummy() -> Self {
476        Self {
477            span: DUMMY_SP,
478            test: None,
479            cons: Vec::new(),
480        }
481    }
482}
483
484#[ast_node("CatchClause")]
485#[derive(Eq, Hash, EqIgnoreSpan, Default)]
486#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
487#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
488pub struct CatchClause {
489    pub span: Span,
490    /// es2019
491    ///
492    /// The param is null if the catch binding is omitted. E.g., try { foo() }
493    /// catch { bar() }
494    #[cfg_attr(feature = "serde-impl", serde(default))]
495    #[cfg_attr(
496        feature = "encoding-impl",
497        encoding(with = "cbor4ii::core::types::Maybe")
498    )]
499    pub param: Option<Pat>,
500
501    pub body: BlockStmt,
502}
503
504/// A head for for-in and for-of loop.
505#[ast_node]
506#[derive(Eq, Hash, Is, EqIgnoreSpan)]
507#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
508#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
509pub enum ForHead {
510    #[tag("VariableDeclaration")]
511    VarDecl(Box<VarDecl>),
512
513    #[tag("UsingDeclaration")]
514    UsingDecl(Box<UsingDecl>),
515
516    #[tag("*")]
517    Pat(Box<Pat>),
518}
519
520bridge_from!(ForHead, Box<VarDecl>, VarDecl);
521bridge_from!(ForHead, Box<Pat>, Pat);
522
523impl Take for ForHead {
524    fn dummy() -> Self {
525        Default::default()
526    }
527}
528
529impl Default for ForHead {
530    fn default() -> Self {
531        ForHead::Pat(Take::dummy())
532    }
533}
534
535#[ast_node]
536#[derive(Eq, Hash, Is, EqIgnoreSpan)]
537#[allow(variant_size_differences)]
538#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
539#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
540pub enum VarDeclOrExpr {
541    #[tag("VariableDeclaration")]
542    VarDecl(Box<VarDecl>),
543
544    #[tag("*")]
545    Expr(Box<Expr>),
546}
547
548bridge_from!(VarDeclOrExpr, Box<VarDecl>, VarDecl);
549bridge_from!(VarDeclOrExpr, Box<Expr>, Expr);
550
551impl Take for VarDeclOrExpr {
552    fn dummy() -> Self {
553        VarDeclOrExpr::Expr(Take::dummy())
554    }
555}