swc_ecma_minifier/compress/pure/
if_return.rs1use swc_common::{util::take::Take, DUMMY_SP};
2use swc_ecma_ast::*;
3use swc_ecma_utils::prepend_stmt;
4
5use super::Pure;
6use crate::compress::util::{is_fine_for_if_cons, negate};
7
8impl Pure<'_> {
9 #[allow(clippy::unnecessary_filter_map)]
26 pub(super) fn negate_if_terminate(
27 &mut self,
28 stmts: &mut Vec<Stmt>,
29 handle_return: bool,
30 handle_continue: bool,
31 ) {
32 if handle_return {
33 if !self.options.if_return || !self.options.bools {
34 return;
35 }
36
37 if stmts.len() == 1 {
38 for s in stmts.iter_mut() {
39 if let Stmt::If(s) = s {
40 if let Stmt::Block(cons) = &mut *s.cons {
41 self.negate_if_terminate(&mut cons.stmts, true, false);
42 }
43 }
44 }
45 }
46 }
47
48 let len = stmts.len();
49
50 let pos_of_if = stmts.iter().enumerate().rposition(|(idx, s)| {
51 idx != len - 1
52 && match s {
53 Stmt::If(IfStmt {
54 cons, alt: None, ..
55 }) => match &**cons {
56 Stmt::Return(ReturnStmt { arg: None, .. }) => handle_return,
57
58 Stmt::Continue(ContinueStmt { label: None, .. }) => handle_continue,
59 _ => false,
60 },
61 _ => false,
62 }
63 });
64
65 let pos_of_if = match pos_of_if {
66 Some(v) => v,
67 _ => return,
68 };
69
70 if stmts[pos_of_if..].iter().any(|s| match s {
72 Stmt::Decl(Decl::Var(v))
73 if matches!(
74 &**v,
75 VarDecl {
76 kind: VarDeclKind::Const | VarDeclKind::Let,
77 ..
78 }
79 ) =>
80 {
81 true
82 }
83 _ => false,
84 }) {
85 return;
86 }
87
88 self.changed = true;
89 report_change!(
90 "if_return: Negating `foo` in `if (foo) return; bar()` to make it `if (!foo) bar()`"
91 );
92
93 let mut new = Vec::with_capacity(stmts.len());
94 let mut fn_decls = Vec::with_capacity(stmts.len());
95 new.extend(stmts.drain(..pos_of_if));
96 let cons = stmts
97 .drain(1..)
98 .filter_map(|stmt| {
99 if matches!(stmt, Stmt::Decl(Decl::Fn(..))) {
100 fn_decls.push(stmt);
101 return None;
102 }
103
104 Some(stmt)
105 })
106 .collect::<Vec<_>>();
107
108 let if_stmt = stmts.take().into_iter().next().unwrap();
109 match if_stmt {
110 Stmt::If(mut s) => {
111 assert_eq!(s.alt, None);
112 negate(self.expr_ctx, &mut s.test, true, false);
113
114 s.cons = if cons.len() == 1 && is_fine_for_if_cons(&cons[0]) {
115 Box::new(cons.into_iter().next().unwrap())
116 } else {
117 Box::new(
118 BlockStmt {
119 span: DUMMY_SP,
120 stmts: cons,
121 ..Default::default()
122 }
123 .into(),
124 )
125 };
126
127 new.push(s.into())
128 }
129 _ => {
130 unreachable!()
131 }
132 }
133
134 new.extend(fn_decls);
135
136 *stmts = new;
137 }
138
139 pub(super) fn merge_else_if(&mut self, s: &mut IfStmt) {
140 if let Some(Stmt::If(IfStmt {
141 span: span_of_alt,
142 test: test_of_alt,
143 cons: cons_of_alt,
144 alt: Some(alt_of_alt),
145 ..
146 })) = s.alt.as_deref_mut()
147 {
148 match &**cons_of_alt {
149 Stmt::Return(..) | Stmt::Continue(ContinueStmt { label: None, .. }) => {}
150 _ => return,
151 }
152
153 match &mut **alt_of_alt {
154 Stmt::Block(..) => {}
155 Stmt::Expr(..) => {
156 *alt_of_alt = Box::new(
157 BlockStmt {
158 span: DUMMY_SP,
159 stmts: vec![*alt_of_alt.take()],
160 ..Default::default()
161 }
162 .into(),
163 );
164 }
165 _ => {
166 return;
167 }
168 }
169
170 self.changed = true;
171 report_change!("if_return: Merging `else if` into `else`");
172
173 match &mut **alt_of_alt {
174 Stmt::Block(alt_of_alt) => {
175 prepend_stmt(
176 &mut alt_of_alt.stmts,
177 IfStmt {
178 span: *span_of_alt,
179 test: test_of_alt.take(),
180 cons: cons_of_alt.take(),
181 alt: None,
182 }
183 .into(),
184 );
185 }
186
187 _ => {
188 unreachable!()
189 }
190 }
191
192 s.alt = Some(alt_of_alt.take());
193 }
194 }
195}