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