Skip to main content

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 body_ctxt: SyntaxContext,
332    pub discriminant: Box<Expr>,
333    pub cases: Vec<SwitchCase>,
334}
335
336#[ast_node("ThrowStatement")]
337#[derive(Eq, Hash, EqIgnoreSpan, Default)]
338#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
339#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
340pub struct ThrowStmt {
341    pub span: Span,
342    #[cfg_attr(feature = "serde-impl", serde(rename = "argument"))]
343    pub arg: Box<Expr>,
344}
345
346#[ast_node("TryStatement")]
347#[derive(Eq, Hash, EqIgnoreSpan, Default)]
348#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
349#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
350pub struct TryStmt {
351    pub span: Span,
352
353    pub block: BlockStmt,
354
355    #[cfg_attr(feature = "serde-impl", serde(default))]
356    #[cfg_attr(
357        feature = "encoding-impl",
358        encoding(with = "cbor4ii::core::types::Maybe")
359    )]
360    pub handler: Option<CatchClause>,
361
362    #[cfg_attr(feature = "serde-impl", serde(default))]
363    #[cfg_attr(
364        feature = "encoding-impl",
365        encoding(with = "cbor4ii::core::types::Maybe")
366    )]
367    pub finalizer: Option<BlockStmt>,
368}
369
370#[ast_node("WhileStatement")]
371#[derive(Eq, Hash, EqIgnoreSpan, Default)]
372#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
373#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
374pub struct WhileStmt {
375    pub span: Span,
376    pub test: Box<Expr>,
377    pub body: Box<Stmt>,
378}
379
380#[ast_node("DoWhileStatement")]
381#[derive(Eq, Hash, EqIgnoreSpan, Default)]
382#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
383#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
384pub struct DoWhileStmt {
385    pub span: Span,
386    pub test: Box<Expr>,
387    pub body: Box<Stmt>,
388}
389
390#[ast_node("ForStatement")]
391#[derive(Eq, Hash, EqIgnoreSpan, Default)]
392#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
393#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
394pub struct ForStmt {
395    pub span: Span,
396
397    #[cfg_attr(feature = "serde-impl", serde(default))]
398    #[cfg_attr(
399        feature = "encoding-impl",
400        encoding(with = "cbor4ii::core::types::Maybe")
401    )]
402    pub init: Option<VarDeclOrExpr>,
403
404    #[cfg_attr(feature = "serde-impl", serde(default))]
405    #[cfg_attr(
406        feature = "encoding-impl",
407        encoding(with = "cbor4ii::core::types::Maybe")
408    )]
409    pub test: Option<Box<Expr>>,
410
411    #[cfg_attr(feature = "serde-impl", serde(default))]
412    #[cfg_attr(
413        feature = "encoding-impl",
414        encoding(with = "cbor4ii::core::types::Maybe")
415    )]
416    pub update: Option<Box<Expr>>,
417
418    pub body: Box<Stmt>,
419}
420
421#[ast_node("ForInStatement")]
422#[derive(Eq, Hash, EqIgnoreSpan, Default)]
423#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
424#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
425pub struct ForInStmt {
426    pub span: Span,
427    pub left: ForHead,
428    pub right: Box<Expr>,
429    pub body: Box<Stmt>,
430}
431
432#[ast_node("ForOfStatement")]
433#[derive(Eq, Hash, EqIgnoreSpan, Default)]
434#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
435#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
436pub struct ForOfStmt {
437    pub span: Span,
438    /// Span of the await token.
439    ///
440    /// es2018
441    ///
442    /// for-await-of statements, e.g., `for await (const x of xs) {`
443    #[cfg_attr(feature = "serde-impl", serde(default, rename = "await"))]
444    pub is_await: bool,
445    pub left: ForHead,
446    pub right: Box<Expr>,
447    pub body: Box<Stmt>,
448}
449
450impl Take for ForOfStmt {
451    fn dummy() -> Self {
452        Default::default()
453    }
454}
455
456#[ast_node("SwitchCase")]
457#[derive(Eq, Hash, EqIgnoreSpan, Default)]
458#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
459#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
460pub struct SwitchCase {
461    pub span: Span,
462
463    /// None for `default:`
464    #[cfg_attr(feature = "serde-impl", serde(default))]
465    #[cfg_attr(
466        feature = "encoding-impl",
467        encoding(with = "cbor4ii::core::types::Maybe")
468    )]
469    pub test: Option<Box<Expr>>,
470
471    #[cfg_attr(feature = "serde-impl", serde(rename = "consequent"))]
472    pub cons: Vec<Stmt>,
473}
474
475impl Take for SwitchCase {
476    fn dummy() -> Self {
477        Self {
478            span: DUMMY_SP,
479            test: None,
480            cons: Vec::new(),
481        }
482    }
483}
484
485#[ast_node("CatchClause")]
486#[derive(Eq, Hash, EqIgnoreSpan, Default)]
487#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
488#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
489pub struct CatchClause {
490    pub span: Span,
491    /// es2019
492    ///
493    /// The param is null if the catch binding is omitted. E.g., try { foo() }
494    /// catch { bar() }
495    #[cfg_attr(feature = "serde-impl", serde(default))]
496    #[cfg_attr(
497        feature = "encoding-impl",
498        encoding(with = "cbor4ii::core::types::Maybe")
499    )]
500    pub param: Option<Pat>,
501
502    pub body: BlockStmt,
503}
504
505/// A head for for-in and for-of loop.
506#[ast_node]
507#[derive(Eq, Hash, Is, EqIgnoreSpan)]
508#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
509#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
510pub enum ForHead {
511    #[tag("VariableDeclaration")]
512    VarDecl(Box<VarDecl>),
513
514    #[tag("UsingDeclaration")]
515    UsingDecl(Box<UsingDecl>),
516
517    #[tag("*")]
518    Pat(Box<Pat>),
519}
520
521bridge_from!(ForHead, Box<VarDecl>, VarDecl);
522bridge_from!(ForHead, Box<Pat>, Pat);
523
524impl Take for ForHead {
525    fn dummy() -> Self {
526        Default::default()
527    }
528}
529
530impl Default for ForHead {
531    fn default() -> Self {
532        ForHead::Pat(Take::dummy())
533    }
534}
535
536#[ast_node]
537#[derive(Eq, Hash, Is, EqIgnoreSpan)]
538#[allow(variant_size_differences)]
539#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
540#[cfg_attr(feature = "shrink-to-fit", derive(shrink_to_fit::ShrinkToFit))]
541pub enum VarDeclOrExpr {
542    #[tag("VariableDeclaration")]
543    VarDecl(Box<VarDecl>),
544
545    #[tag("*")]
546    Expr(Box<Expr>),
547}
548
549bridge_from!(VarDeclOrExpr, Box<VarDecl>, VarDecl);
550bridge_from!(VarDeclOrExpr, Box<Expr>, Expr);
551
552impl Take for VarDeclOrExpr {
553    fn dummy() -> Self {
554        VarDeclOrExpr::Expr(Take::dummy())
555    }
556}