Skip to main content

swc_ecma_codegen/
stmt.rs

1use swc_common::Spanned;
2use swc_ecma_ast::*;
3use swc_ecma_codegen_macros::node_impl;
4
5#[cfg(swc_ast_unknown)]
6use crate::unknown_error;
7use crate::{
8    scope_helpers::for_each_pat_binding,
9    text_writer::{BindingStorage, ScopeKind},
10    util::{EndsWithAlphaNum, StartsWithAlphaNum},
11};
12
13#[node_impl]
14impl MacroNode for Stmt {
15    fn emit(&mut self, emitter: &mut Macro) -> Result {
16        match self {
17            Stmt::Expr(ref e) => emit!(e),
18            Stmt::Block(ref e) => {
19                emit!(e);
20                return Ok(());
21            }
22            Stmt::Empty(ref e) => emit!(e),
23            Stmt::Debugger(ref e) => emit!(e),
24            Stmt::With(ref e) => emit!(e),
25            Stmt::Return(ref e) => emit!(e),
26            Stmt::Labeled(ref e) => emit!(e),
27            Stmt::Break(ref e) => emit!(e),
28            Stmt::Continue(ref e) => emit!(e),
29            Stmt::If(ref e) => emit!(e),
30            Stmt::Switch(ref e) => emit!(e),
31            Stmt::Throw(ref e) => emit!(e),
32            Stmt::Try(ref e) => emit!(e),
33            Stmt::While(ref e) => emit!(e),
34            Stmt::DoWhile(ref e) => emit!(e),
35            Stmt::For(ref e) => emit!(e),
36            Stmt::ForIn(ref e) => emit!(e),
37            Stmt::ForOf(ref e) => emit!(e),
38            Stmt::Decl(Decl::Var(e)) => {
39                emit!(e);
40                semi!(emitter);
41            }
42            Stmt::Decl(e @ Decl::Using(..)) => {
43                emit!(e);
44                semi!(emitter);
45            }
46            Stmt::Decl(ref e) => emit!(e),
47            #[cfg(swc_ast_unknown)]
48            _ => return Err(unknown_error()),
49        }
50        if emitter.comments.is_some() {
51            emitter.emit_trailing_comments_of_pos(self.span().hi(), true, true)?;
52        }
53
54        if !emitter.cfg.minify {
55            emitter.wr.write_line()?;
56        }
57
58        Ok(())
59    }
60}
61
62#[node_impl]
63impl MacroNode for EmptyStmt {
64    fn emit(&mut self, emitter: &mut Macro) -> Result {
65        emitter.emit_leading_comments_of_span(self.span(), false)?;
66
67        emitter.wr.write_punct(None, ";", false)?;
68
69        Ok(())
70    }
71}
72
73#[node_impl]
74impl MacroNode for BlockStmt {
75    fn emit(&mut self, emitter: &mut Macro) -> Result {
76        emitter.emit_block_stmt_inner(self, false)?;
77
78        Ok(())
79    }
80}
81
82#[node_impl]
83impl MacroNode for FunctionBody {
84    fn emit(&mut self, emitter: &mut Macro) -> Result {
85        emitter.emit_function_body_inner(self, false)?;
86
87        Ok(())
88    }
89}
90
91#[node_impl]
92impl MacroNode for ExprStmt {
93    fn emit(&mut self, emitter: &mut Macro) -> Result {
94        emitter.emit_leading_comments_of_span(self.span, false)?;
95
96        emitter.emit_trailing_comments_of_pos_with(self.span.hi, true, |emitter| {
97            emit!(self.expr);
98
99            semi!(emitter);
100
101            Ok(())
102        })?;
103
104        Ok(())
105    }
106}
107
108#[node_impl]
109impl MacroNode for DebuggerStmt {
110    fn emit(&mut self, emitter: &mut Macro) -> Result {
111        emitter.wr.commit_pending_semi()?;
112
113        emitter.emit_leading_comments_of_span(self.span(), false)?;
114
115        keyword!(emitter, self.span, "debugger");
116        semi!(emitter);
117
118        Ok(())
119    }
120}
121
122#[node_impl]
123impl MacroNode for WithStmt {
124    fn emit(&mut self, emitter: &mut Macro) -> Result {
125        emitter.wr.commit_pending_semi()?;
126
127        srcmap!(emitter, self, true);
128
129        keyword!(emitter, "with");
130        formatting_space!(emitter);
131
132        punct!(emitter, "(");
133        emit!(self.obj);
134        punct!(emitter, ")");
135
136        emit!(self.body);
137
138        Ok(())
139    }
140}
141
142#[node_impl]
143impl MacroNode for ReturnStmt {
144    fn emit(&mut self, emitter: &mut Macro) -> Result {
145        emitter.wr.commit_pending_semi()?;
146
147        emitter.emit_leading_comments_of_span(self.span, false)?;
148
149        srcmap!(emitter, self, true);
150
151        keyword!(emitter, "return");
152
153        if let Some(arg) = self.arg.as_deref() {
154            let need_paren = self
155                .arg
156                .as_deref()
157                .map(|expr| emitter.has_leading_comment(expr))
158                .unwrap_or(false);
159            if need_paren {
160                punct!(emitter, "(");
161            } else if arg.starts_with_alpha_num() {
162                space!(emitter);
163            } else {
164                formatting_space!(emitter);
165            }
166
167            emit!(arg);
168            if need_paren {
169                punct!(emitter, ")");
170            }
171        }
172
173        semi!(emitter);
174
175        Ok(())
176    }
177}
178
179#[node_impl]
180impl MacroNode for LabeledStmt {
181    fn emit(&mut self, emitter: &mut Macro) -> Result {
182        emitter.wr.commit_pending_semi()?;
183
184        emit!(self.label);
185
186        // TODO: Comment
187        punct!(emitter, ":");
188        formatting_space!(emitter);
189
190        emit!(self.body);
191
192        Ok(())
193    }
194}
195
196#[node_impl]
197impl MacroNode for SwitchStmt {
198    fn emit(&mut self, emitter: &mut Macro) -> Result {
199        emitter.wr.commit_pending_semi()?;
200
201        emitter.emit_leading_comments_of_span(self.span(), false)?;
202
203        srcmap!(emitter, self, true);
204
205        keyword!(emitter, "switch");
206
207        punct!(emitter, "(");
208        emit!(self.discriminant);
209        punct!(emitter, ")");
210
211        punct!(emitter, "{");
212        emitter.emit_list(self.span(), Some(&self.cases), ListFormat::CaseBlockClauses)?;
213
214        srcmap!(emitter, self, false, true);
215        punct!(emitter, "}");
216
217        Ok(())
218    }
219}
220
221#[node_impl]
222impl MacroNode for CatchClause {
223    fn emit(&mut self, emitter: &mut Macro) -> Result {
224        emitter.emit_leading_comments_of_span(self.span(), false)?;
225
226        srcmap!(emitter, self, true);
227        emitter.start_scope(None, ScopeKind::Catch, false, false, Some(self.span()))?;
228
229        keyword!(emitter, "catch");
230
231        formatting_space!(emitter);
232
233        if let Some(param) = &self.param {
234            if emitter.scope_tracking_enabled() {
235                let mut names = vec![];
236                for_each_pat_binding(param, &mut |name| names.push(name.to_string()));
237                for name in names {
238                    emitter.add_scope_variable(&name, Some(&name), BindingStorage::Lexical)?;
239                }
240            }
241
242            punct!(emitter, "(");
243            emit!(param);
244            punct!(emitter, ")");
245        }
246
247        formatting_space!(emitter);
248
249        emit!(self.body);
250        emitter.end_scope()?;
251
252        Ok(())
253    }
254}
255
256#[node_impl]
257impl MacroNode for SwitchCase {
258    fn emit(&mut self, emitter: &mut Macro) -> Result {
259        emitter.emit_leading_comments_of_span(self.span(), false)?;
260
261        srcmap!(emitter, self, true);
262
263        if let Some(ref test) = self.test {
264            keyword!(emitter, "case");
265
266            let starts_with_alpha_num = test.starts_with_alpha_num();
267
268            if starts_with_alpha_num {
269                space!(emitter);
270            } else {
271                formatting_space!(emitter);
272            }
273
274            emit!(test);
275        } else {
276            keyword!(emitter, "default");
277        }
278
279        let emit_as_single_stmt = self.cons.len() == 1 && {
280            // treat synthesized nodes as located on the same line for emit purposes
281            self.is_synthesized()
282                || self.cons[0].is_synthesized()
283                || emitter
284                    .cm
285                    .is_on_same_line(self.span().lo(), self.cons[0].span().lo())
286        };
287
288        let mut format = ListFormat::CaseOrDefaultClauseStatements;
289        if emit_as_single_stmt {
290            punct!(emitter, ":");
291            space!(emitter);
292            format &= !(ListFormat::MultiLine | ListFormat::Indented);
293        } else {
294            punct!(emitter, ":");
295        }
296        emitter.emit_list(self.span(), Some(&self.cons), format)?;
297
298        Ok(())
299    }
300}
301
302#[node_impl]
303impl MacroNode for ThrowStmt {
304    fn emit(&mut self, emitter: &mut Macro) -> Result {
305        emitter.emit_leading_comments_of_span(self.span(), false)?;
306
307        srcmap!(emitter, self, true);
308
309        keyword!(emitter, "throw");
310
311        {
312            let need_paren = emitter.has_leading_comment(&self.arg);
313            if need_paren {
314                punct!(emitter, "(");
315            } else if self.arg.starts_with_alpha_num() {
316                space!(emitter);
317            } else {
318                formatting_space!(emitter);
319            }
320
321            emit!(self.arg);
322            if need_paren {
323                punct!(emitter, ")");
324            }
325        }
326        semi!(emitter);
327
328        Ok(())
329    }
330}
331
332#[node_impl]
333impl MacroNode for TryStmt {
334    fn emit(&mut self, emitter: &mut Macro) -> Result {
335        emitter.emit_leading_comments_of_span(self.span(), false)?;
336
337        emitter.wr.commit_pending_semi()?;
338
339        srcmap!(emitter, self, true);
340
341        keyword!(emitter, "try");
342
343        formatting_space!(emitter);
344        emit!(self.block);
345
346        if let Some(ref catch) = self.handler {
347            formatting_space!(emitter);
348            emit!(catch);
349        }
350
351        if let Some(ref finally) = self.finalizer {
352            formatting_space!(emitter);
353            keyword!(emitter, "finally");
354            // space!(emitter);
355            emit!(finally);
356        }
357
358        Ok(())
359    }
360}
361
362#[node_impl]
363impl MacroNode for WhileStmt {
364    fn emit(&mut self, emitter: &mut Macro) -> Result {
365        emitter.wr.commit_pending_semi()?;
366
367        emitter.emit_leading_comments_of_span(self.span(), false)?;
368
369        srcmap!(emitter, self, true);
370
371        keyword!(emitter, "while");
372
373        punct!(emitter, "(");
374        emit!(self.test);
375        punct!(emitter, ")");
376
377        emit!(self.body);
378
379        Ok(())
380    }
381}
382
383#[node_impl]
384impl MacroNode for DoWhileStmt {
385    fn emit(&mut self, emitter: &mut Macro) -> Result {
386        emitter.wr.commit_pending_semi()?;
387
388        emitter.emit_leading_comments_of_span(self.span(), false)?;
389
390        srcmap!(emitter, self, true);
391
392        keyword!(emitter, "do");
393        if self.body.starts_with_alpha_num() {
394            space!(emitter);
395        } else {
396            formatting_space!(emitter)
397        }
398        emit!(self.body);
399
400        keyword!(emitter, "while");
401
402        formatting_space!(emitter);
403
404        punct!(emitter, "(");
405        emit!(self.test);
406        punct!(emitter, ")");
407
408        if emitter.cfg.target <= EsVersion::Es5 {
409            semi!(emitter);
410        }
411
412        srcmap!(emitter, self, false);
413
414        Ok(())
415    }
416}
417
418#[node_impl]
419impl MacroNode for ForStmt {
420    fn emit(&mut self, emitter: &mut Macro) -> Result {
421        emitter.wr.commit_pending_semi()?;
422
423        emitter.emit_leading_comments_of_span(self.span(), false)?;
424
425        srcmap!(emitter, self, true);
426
427        keyword!(emitter, "for");
428
429        punct!(emitter, "(");
430        opt!(emitter, self.init);
431        emitter.wr.write_punct(None, ";", false)?;
432        opt_leading_space!(emitter, self.test);
433        emitter.wr.write_punct(None, ";", false)?;
434        opt_leading_space!(emitter, self.update);
435        punct!(emitter, ")");
436
437        emit!(self.body);
438
439        Ok(())
440    }
441}
442
443#[node_impl]
444impl MacroNode for ForInStmt {
445    fn emit(&mut self, emitter: &mut Macro) -> Result {
446        emitter.wr.commit_pending_semi()?;
447
448        emitter.emit_leading_comments_of_span(self.span(), false)?;
449
450        srcmap!(emitter, self, true);
451
452        keyword!(emitter, "for");
453
454        punct!(emitter, "(");
455        emit!(self.left);
456
457        if self.left.ends_with_alpha_num() {
458            space!(emitter);
459        } else {
460            formatting_space!(emitter);
461        }
462        keyword!(emitter, "in");
463
464        {
465            let starts_with_alpha_num = self.right.starts_with_alpha_num();
466
467            if starts_with_alpha_num {
468                space!(emitter);
469            } else {
470                formatting_space!(emitter)
471            }
472            emit!(self.right);
473        }
474
475        punct!(emitter, ")");
476
477        emit!(self.body);
478
479        Ok(())
480    }
481}
482
483#[node_impl]
484impl MacroNode for ForOfStmt {
485    fn emit(&mut self, emitter: &mut Macro) -> Result {
486        emitter.wr.commit_pending_semi()?;
487
488        emitter.emit_leading_comments_of_span(self.span(), false)?;
489
490        srcmap!(emitter, self, true);
491
492        keyword!(emitter, "for");
493
494        if self.is_await {
495            space!(emitter);
496            keyword!(emitter, "await");
497        }
498        formatting_space!(emitter);
499        punct!(emitter, "(");
500        emit!(self.left);
501        if self.left.ends_with_alpha_num() {
502            space!(emitter);
503        } else {
504            formatting_space!(emitter);
505        }
506        keyword!(emitter, "of");
507
508        {
509            let starts_with_alpha_num = self.right.starts_with_alpha_num();
510
511            if starts_with_alpha_num {
512                space!(emitter);
513            } else {
514                formatting_space!(emitter)
515            }
516            emit!(self.right);
517        }
518        punct!(emitter, ")");
519        emit!(self.body);
520
521        Ok(())
522    }
523}
524
525#[node_impl]
526impl MacroNode for BreakStmt {
527    fn emit(&mut self, emitter: &mut Macro) -> Result {
528        emitter.wr.commit_pending_semi()?;
529
530        srcmap!(emitter, self, true);
531
532        keyword!(emitter, "break");
533
534        if let Some(ref label) = self.label {
535            space!(emitter);
536            emit!(label);
537        }
538
539        semi!(emitter);
540
541        Ok(())
542    }
543}
544
545#[node_impl]
546impl MacroNode for ContinueStmt {
547    fn emit(&mut self, emitter: &mut Macro) -> Result {
548        emitter.wr.commit_pending_semi()?;
549
550        srcmap!(emitter, self, true);
551
552        keyword!(emitter, "continue");
553
554        if let Some(ref label) = self.label {
555            space!(emitter);
556            emit!(label);
557        }
558
559        semi!(emitter);
560
561        Ok(())
562    }
563}
564
565#[node_impl]
566impl MacroNode for IfStmt {
567    fn emit(&mut self, emitter: &mut Macro) -> Result {
568        emitter.emit_leading_comments_of_span(self.span(), false)?;
569
570        emitter.wr.commit_pending_semi()?;
571
572        srcmap!(emitter, self, true);
573
574        keyword!(emitter, "if");
575
576        formatting_space!(emitter);
577        punct!(emitter, "(");
578        emit!(self.test);
579        punct!(emitter, ")");
580        formatting_space!(emitter);
581
582        let is_cons_block = match *self.cons {
583            Stmt::Block(..) => true,
584            _ => false,
585        };
586
587        emit!(self.cons);
588
589        if let Some(ref alt) = self.alt {
590            if is_cons_block {
591                formatting_space!(emitter);
592            }
593            keyword!(emitter, "else");
594            if alt.starts_with_alpha_num() {
595                space!(emitter);
596            } else {
597                formatting_space!(emitter);
598            }
599            emit!(alt);
600        }
601
602        Ok(())
603    }
604}
605
606#[node_impl]
607impl MacroNode for ModuleExportName {
608    fn emit(&mut self, emitter: &mut Macro) -> Result {
609        match self {
610            ModuleExportName::Ident(ident) => emit!(ident),
611            ModuleExportName::Str(s) => emit!(s),
612            #[cfg(swc_ast_unknown)]
613            _ => return Err(unknown_error()),
614        }
615
616        Ok(())
617    }
618}
619
620#[node_impl]
621impl MacroNode for VarDeclOrExpr {
622    fn emit(&mut self, emitter: &mut Macro) -> Result {
623        match self {
624            VarDeclOrExpr::Expr(node) => emit!(node),
625            VarDeclOrExpr::VarDecl(node) => emit!(node),
626            #[cfg(swc_ast_unknown)]
627            _ => return Err(unknown_error()),
628        }
629
630        Ok(())
631    }
632}
633
634/// Copied from [ratel][]
635///
636/// [ratel]:https://github.com/ratel-rust/ratel-core
637#[cfg(test)]
638mod tests {
639    use crate::tests::{assert_min, assert_pretty};
640
641    #[test]
642    fn block_statement() {
643        assert_min("{}", "{}");
644        assert_min("{foo;}", "{foo}");
645    }
646
647    #[test]
648    fn empty_block_statement() {
649        assert_pretty("{\n}", "{}");
650        assert_pretty("{\n//todo\n}", "{\n//todo\n}");
651
652        assert_pretty(
653            "try {\n\n} catch {\n  // Pass\n}\n",
654            "try {} catch  {\n// Pass\n}",
655        );
656    }
657
658    #[test]
659    fn empty_object_lit() {
660        assert_pretty("Object.assign({\n}, a, b);", "Object.assign({}, a, b);");
661    }
662
663    #[test]
664    fn labeled_statement() {
665        assert_min("foo: {}", "foo:{}");
666        assert_min("foo: bar;", "foo:bar");
667    }
668
669    #[test]
670    fn function_statement() {
671        assert_min("function foo() {}", "function foo(){}");
672    }
673
674    #[test]
675    fn declaration_statement() {
676        assert_min("var foo;", "var foo");
677        assert_min("let foo;", "let foo");
678        assert_min("var foo = 10;", "var foo=10");
679        assert_min("let foo = 10;", "let foo=10");
680        assert_min("const foo = 10;", "const foo=10");
681        assert_min("var foo, bar;", "var foo,bar");
682        assert_min("let foo, bar;", "let foo,bar");
683        assert_min("var foo = 10, bar = 20;", "var foo=10,bar=20");
684        assert_min("let foo = 10, bar = 20;", "let foo=10,bar=20");
685        assert_min("const foo = 10, bar = 20;", "const foo=10,bar=20");
686        assert_min("const a = {...foo};", "const a={...foo}");
687    }
688
689    #[test]
690    fn if_statement() {
691        assert_min("if (true) foo;", "if(true)foo");
692        assert_min("if (true) { foo; }", "if(true){foo}");
693        assert_min("if (true) foo; else bar;", "if(true)foo;else bar");
694        assert_min("if (true) { foo; } else { bar; }", "if(true){foo}else{bar}");
695        assert_min("if (true) foo; else { bar; }", "if(true)foo;else{bar}");
696        assert_min("if (true) { foo; } else bar;", "if(true){foo}else bar");
697        assert_min("if (true) y(); else x++;", "if(true)y();else x++");
698        assert_min("if (true) y(); else x--;", "if(true)y();else x--");
699    }
700
701    #[test]
702    fn while_statement() {
703        assert_min("while (true) foo;", "while(true)foo");
704        assert_min("while (true) { foo; }", "while(true){foo}");
705    }
706
707    #[test]
708    fn do_statement() {
709        assert_min("do { foo; } while (true)", "do{foo}while(true)");
710        assert_min("do foo; while (true)", "do foo;while(true)");
711    }
712
713    #[test]
714    fn for_statement() {
715        assert_min("for (var i = 0; i < 10; i++) {}", "for(var i=0;i<10;i++){}");
716        assert_min("for (i = 0; i < 10; i++) {}", "for(i=0;i<10;i++){}");
717        assert_min("for (;;) {}", "for(;;){}");
718        assert_min("for (foo in bar){}", "for(foo in bar){}");
719        assert_min("for (let foo in bar){}", "for(let foo in bar){}");
720        assert_min("for (foo of bar){}", "for(foo of bar){}");
721        assert_min("for (let foo of bar){}", "for(let foo of bar){}");
722    }
723
724    #[test]
725    fn for_statement_pretty() {
726        assert_pretty(
727            "for (var i = 0; i < 10; i++) {}",
728            "for(var i = 0; i < 10; i++){}",
729        );
730        assert_pretty("for (i = 0; i < 10; i++) {}", "for(i = 0; i < 10; i++){}");
731        assert_pretty("for (;;) {}", "for(;;){}");
732        assert_pretty("for (foo in bar){}", "for(foo in bar){}");
733        assert_pretty("for (let foo in bar){}", "for(let foo in bar){}");
734        assert_pretty("for (foo of bar){}", "for (foo of bar){}");
735        assert_pretty("for (let foo of bar){}", "for (let foo of bar){}");
736    }
737
738    #[test]
739    fn import() {
740        assert_min(
741            "import colors, { color } from 'patterns/colors';",
742            "import colors,{color}from\"patterns/colors\"",
743        );
744        assert_pretty(
745            "import colors, { color } from 'patterns/colors';",
746            "import colors, { color } from 'patterns/colors';",
747        );
748    }
749
750    #[test]
751    fn issue_204_01() {
752        assert_min(r"'\r\n';", r#""\r\n""#);
753    }
754
755    #[test]
756    fn issue_204_02() {
757        assert_min(r"const a = fn() + '\r\n';", r#"const a=fn()+"\r\n""#);
758    }
759
760    #[test]
761    fn issue_177() {
762        assert_min(
763            "#!/usr/bin/env node
764let x = 4;",
765            "#!/usr/bin/env node
766let x=4",
767        );
768    }
769
770    #[test]
771    fn issue_197() {
772        assert_pretty(
773            "// type Foo = 'Oops';
774const Link = 'Boo';",
775            "// type Foo = 'Oops';
776const Link = 'Boo';",
777        );
778    }
779
780    #[test]
781    fn issue_266() {
782        assert_min(
783            "'Q' + +x1 + ',' + +y1 + ',' + (this._x1 = +x) + ',' + (this._y1 = +y);",
784            "\"Q\"+ +x1+\",\"+ +y1+\",\"+(this._x1=+x)+\",\"+(this._y1=+y)",
785        );
786    }
787}