feat(ecmascript): support /* @__PURE__ */ in may_have_side_effects (#9409)

Actually this PR alone won't remove `/* @__PURE__ */ foo()`. We probably need to call `.may_have_side_effects` in `remove_unused_expression`.

close #9397
This commit is contained in:
sapphi-red committed 2025-02-27 09:00:17 +00:00
1 parent d129055dc6
commit 32139d23a0
2 files changed
+63

No files matched your search

@@ -65,6 +65,8 @@ impl MayHaveSideEffects for Expression<'_> {
match_member_expression!(Expression) => {
self.to_member_expression().may_have_side_effects(is_global_reference)
}
Expression::CallExpression(e) => e.may_have_side_effects(is_global_reference),
Expression::NewExpression(e) => e.may_have_side_effects(is_global_reference),
_ => true,
}
}
@@ -456,3 +458,41 @@ fn property_access_may_have_side_effects(
_ => true,
}
}
impl MayHaveSideEffects for CallExpression<'_> {
fn may_have_side_effects(&self, is_global_reference: &impl IsGlobalReference) -> bool {
if self.pure {
self.arguments.iter().any(|e| e.may_have_side_effects(is_global_reference))
} else {
true
}
}
}
impl MayHaveSideEffects for NewExpression<'_> {
fn may_have_side_effects(&self, is_global_reference: &impl IsGlobalReference) -> bool {
if self.pure {
self.arguments.iter().any(|e| e.may_have_side_effects(is_global_reference))
} else {
true
}
}
}
impl MayHaveSideEffects for Argument<'_> {
fn may_have_side_effects(&self, is_global_reference: &impl IsGlobalReference) -> bool {
match self {
Argument::SpreadElement(e) => match &e.argument {
Expression::ArrayExpression(arr) => arr.may_have_side_effects(is_global_reference),
Expression::StringLiteral(_) => false,
Expression::TemplateLiteral(t) => {
t.expressions.iter().any(|e| e.may_have_side_effects(is_global_reference))
}
_ => true,
},
match_expression!(Argument) => {
self.to_expression().may_have_side_effects(is_global_reference)
}
}
}
}
@@ -618,6 +618,29 @@ fn test_property_access() {
test("a[0]", true);
}
#[test]
fn test_call_like_expressions() {
test("foo()", true);
test("/* #__PURE__ */ foo()", false);
test("/* #__PURE__ */ foo(1)", false);
test("/* #__PURE__ */ foo(bar())", true);
test("/* #__PURE__ */ foo(...[])", false);
test("/* #__PURE__ */ foo(...[1])", false);
test("/* #__PURE__ */ foo(...[bar()])", true);
test("/* #__PURE__ */ foo(...bar)", true);
test("/* #__PURE__ */ (() => { foo() })()", false);
test("new Foo()", true);
test("/* #__PURE__ */ new Foo()", false);
test("/* #__PURE__ */ new Foo(1)", false);
test("/* #__PURE__ */ new Foo(bar())", true);
test("/* #__PURE__ */ new Foo(...[])", false);
test("/* #__PURE__ */ new Foo(...[1])", false);
test("/* #__PURE__ */ new Foo(...[bar()])", true);
test("/* #__PURE__ */ new Foo(...bar)", true);
test("/* #__PURE__ */ new class { constructor() { foo() } }()", false);
}
#[test]
fn test_object_with_to_primitive_related_properties_overridden() {
test("+{}", false);