swc_ecma_minifier/compress/pure/
if_return.rs

1use 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    /// # Input
10    ///
11    /// ```js
12    /// function f(a, b) {
13    ///     if (a) return;
14    ///     console.log(b);
15    /// }
16    /// ```
17    ///
18    /// # Output
19    /// ```js
20    /// function f(a, b) {
21    ///     if (!a)
22    ///         console.log(b);
23    /// }
24    /// ```
25    #[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 we negate a block, these variables will have narrower scope.
71        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}