swc_ecma_codegen/
stmt.rs

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