oxc_ecmascript/side_effects/
statements.rs

1use oxc_ast::ast::*;
2
3use crate::constant_evaluation::{DetermineValueType, ValueType};
4
5use super::{MayHaveSideEffects, PropertyReadSideEffects, context::MayHaveSideEffectsContext};
6
7impl<'a> MayHaveSideEffects<'a> for Statement<'a> {
8    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
9        match self {
10            Statement::BlockStatement(block) => block.may_have_side_effects(ctx),
11            Statement::DoWhileStatement(do_while) => do_while.may_have_side_effects(ctx),
12            Statement::ExpressionStatement(expr) => expr.expression.may_have_side_effects(ctx),
13            Statement::IfStatement(if_stmt) => if_stmt.may_have_side_effects(ctx),
14            Statement::LabeledStatement(labeled) => labeled.body.may_have_side_effects(ctx),
15            Statement::ReturnStatement(return_stmt) => {
16                return_stmt.argument.may_have_side_effects(ctx)
17            }
18            Statement::SwitchStatement(switch) => switch.may_have_side_effects(ctx),
19            Statement::TryStatement(try_stmt) => try_stmt.may_have_side_effects(ctx),
20            Statement::WhileStatement(while_stmt) => while_stmt.may_have_side_effects(ctx),
21            Statement::BreakStatement(_)
22            | Statement::ContinueStatement(_)
23            | Statement::EmptyStatement(_) => false,
24            match_declaration!(Statement) => self.to_declaration().may_have_side_effects(ctx),
25            Statement::ForInStatement(_)
26            | Statement::ForOfStatement(_)
27            | Statement::ForStatement(_)
28            | Statement::ThrowStatement(_)
29            | Statement::WithStatement(_)
30            | Statement::DebuggerStatement(_) => true,
31            #[expect(clippy::match_same_arms)]
32            match_module_declaration!(Statement) => true,
33        }
34    }
35}
36
37impl<'a> MayHaveSideEffects<'a> for BlockStatement<'a> {
38    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
39        self.body.iter().any(|stmt| stmt.may_have_side_effects(ctx))
40    }
41}
42
43impl<'a> MayHaveSideEffects<'a> for DoWhileStatement<'a> {
44    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
45        self.test.may_have_side_effects(ctx) || self.body.may_have_side_effects(ctx)
46    }
47}
48
49impl<'a> MayHaveSideEffects<'a> for IfStatement<'a> {
50    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
51        self.test.may_have_side_effects(ctx)
52            || self.consequent.may_have_side_effects(ctx)
53            || self.alternate.may_have_side_effects(ctx)
54    }
55}
56
57impl<'a> MayHaveSideEffects<'a> for SwitchStatement<'a> {
58    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
59        self.discriminant.may_have_side_effects(ctx)
60            || self.cases.iter().any(|case| {
61                case.test.may_have_side_effects(ctx)
62                    || case.consequent.iter().any(|stmt| stmt.may_have_side_effects(ctx))
63            })
64    }
65}
66
67impl<'a> MayHaveSideEffects<'a> for TryStatement<'a> {
68    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
69        self.block.may_have_side_effects(ctx)
70            || self.handler.as_ref().is_some_and(|catch_clause| {
71                catch_clause
72                    .param
73                    .as_ref()
74                    .is_some_and(|param| param.pattern.may_have_side_effects(ctx))
75                    || catch_clause.body.may_have_side_effects(ctx)
76            })
77            || self.finalizer.as_ref().is_some_and(|finalizer| finalizer.may_have_side_effects(ctx))
78    }
79}
80
81impl<'a> MayHaveSideEffects<'a> for WhileStatement<'a> {
82    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
83        self.test.may_have_side_effects(ctx) || self.body.may_have_side_effects(ctx)
84    }
85}
86
87impl<'a> MayHaveSideEffects<'a> for Declaration<'a> {
88    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
89        match self {
90            Declaration::VariableDeclaration(var_decl) => var_decl.may_have_side_effects(ctx),
91            Declaration::FunctionDeclaration(_) => false,
92            Declaration::ClassDeclaration(class_decl) => class_decl.may_have_side_effects(ctx),
93            Declaration::TSEnumDeclaration(_)
94            | Declaration::TSImportEqualsDeclaration(_)
95            | Declaration::TSModuleDeclaration(_)
96            | Declaration::TSInterfaceDeclaration(_)
97            | Declaration::TSTypeAliasDeclaration(_) => unreachable!(),
98        }
99    }
100}
101
102impl<'a> MayHaveSideEffects<'a> for VariableDeclaration<'a> {
103    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
104        if self.kind == VariableDeclarationKind::AwaitUsing {
105            return true;
106        }
107        if self.kind == VariableDeclarationKind::Using {
108            return self.declarations.iter().any(|decl| {
109                decl.init.as_ref().is_none_or(|init| {
110                    !matches!(init.value_type(ctx), ValueType::Undefined | ValueType::Null)
111                        || init.may_have_side_effects(ctx)
112                })
113            });
114        }
115        self.declarations
116            .iter()
117            .any(|decl| decl.id.may_have_side_effects(ctx) || decl.init.may_have_side_effects(ctx))
118    }
119}
120
121impl<'a> MayHaveSideEffects<'a> for BindingPattern<'a> {
122    fn may_have_side_effects(&self, ctx: &impl MayHaveSideEffectsContext<'a>) -> bool {
123        match &self.kind {
124            BindingPatternKind::ArrayPattern(array_pattern) => {
125                ctx.property_read_side_effects() != PropertyReadSideEffects::None
126                    || array_pattern.elements.iter().any(|el| el.may_have_side_effects(ctx))
127            }
128            BindingPatternKind::ObjectPattern(object_pattern) => {
129                ctx.property_read_side_effects() != PropertyReadSideEffects::None
130                    || object_pattern.properties.iter().any(|prop| {
131                        prop.key.may_have_side_effects(ctx) || prop.value.may_have_side_effects(ctx)
132                    })
133            }
134            BindingPatternKind::AssignmentPattern(assignment_pattern) => {
135                assignment_pattern.left.may_have_side_effects(ctx)
136                    || assignment_pattern.right.may_have_side_effects(ctx)
137            }
138            BindingPatternKind::BindingIdentifier(_) => false,
139        }
140    }
141}