Skip to content

Commit 660c314

Browse files
committed
fix(ecmascript): fix may_have_side_effects for unary expressions (#8989)
Fixes the FIXME test cases related to unary expressions in may_have_side_effects.
1 parent addaa8e commit 660c314

File tree

2 files changed

+83
-29
lines changed

2 files changed

+83
-29
lines changed

crates/oxc_ecmascript/src/side_effects/may_have_side_effects.rs

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,50 @@ pub trait MayHaveSideEffects {
6060
}
6161

6262
fn unary_expression_may_have_side_effects(&self, e: &UnaryExpression<'_>) -> bool {
63-
/// A "simple" operator is one whose children are expressions, has no direct side-effects.
64-
fn is_simple_unary_operator(operator: UnaryOperator) -> bool {
65-
operator != UnaryOperator::Delete
66-
}
67-
if e.operator == UnaryOperator::Typeof && matches!(&e.argument, Expression::Identifier(_)) {
68-
return false;
69-
}
70-
if is_simple_unary_operator(e.operator) {
71-
return self.expression_may_have_side_effects(&e.argument);
63+
match e.operator {
64+
UnaryOperator::Delete => true,
65+
UnaryOperator::Void | UnaryOperator::LogicalNot => {
66+
self.expression_may_have_side_effects(&e.argument)
67+
}
68+
UnaryOperator::Typeof => {
69+
if matches!(&e.argument, Expression::Identifier(_)) {
70+
false
71+
} else {
72+
self.expression_may_have_side_effects(&e.argument)
73+
}
74+
}
75+
UnaryOperator::UnaryPlus => {
76+
match &e.argument {
77+
Expression::NumericLiteral(_)
78+
| Expression::NullLiteral(_)
79+
| Expression::BooleanLiteral(_)
80+
| Expression::StringLiteral(_) => false,
81+
Expression::Identifier(ident) => {
82+
!(matches!(ident.name.as_str(), "Infinity" | "NaN" | "undefined")
83+
&& self.is_global_reference(ident))
84+
}
85+
// ToNumber throws an error when the argument is Symbol / BigInt / an object that
86+
// returns Symbol or BigInt from ToPrimitive
87+
_ => true,
88+
}
89+
}
90+
UnaryOperator::UnaryNegation | UnaryOperator::BitwiseNot => {
91+
match &e.argument {
92+
Expression::BigIntLiteral(_)
93+
| Expression::NumericLiteral(_)
94+
| Expression::NullLiteral(_)
95+
| Expression::BooleanLiteral(_)
96+
| Expression::StringLiteral(_) => false,
97+
Expression::Identifier(ident) => {
98+
!(matches!(ident.name.as_str(), "Infinity" | "NaN" | "undefined")
99+
&& self.is_global_reference(ident))
100+
}
101+
// ToNumber throws an error when the argument is Symbol an object that
102+
// returns Symbol from ToPrimitive
103+
_ => true,
104+
}
105+
}
72106
}
73-
true
74107
}
75108

76109
fn binary_expression_may_have_side_effects(&self, e: &BinaryExpression<'_>) -> bool {

crates/oxc_minifier/tests/ecmascript/may_have_side_effects.rs

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -120,9 +120,9 @@ fn closure_compiler_tests() {
120120
test("undefined", false);
121121
test("void 0", false);
122122
test("void foo()", true);
123-
test("-Infinity", false);
124-
test("Infinity", false);
125-
test("NaN", false);
123+
test_with_global_variables("-Infinity", vec!["Infinity".to_string()], false);
124+
test_with_global_variables("Infinity", vec!["Infinity".to_string()], false);
125+
test_with_global_variables("NaN", vec!["NaN".to_string()], false);
126126
// test("({}||[]).foo = 2;", false);
127127
// test("(true ? {} : []).foo = 2;", false);
128128
// test("({},[]).foo = 2;", false);
@@ -344,21 +344,48 @@ fn test_simple_expressions() {
344344

345345
#[test]
346346
fn test_unary_expressions() {
347-
test("+'foo'", false);
348-
test("+foo()", true);
349-
test("-'foo'", false);
350-
test("-foo()", true);
347+
test("delete 'foo'", true);
348+
test("delete foo()", true);
349+
350+
test("void 'foo'", false);
351+
test("void foo()", true);
351352
test("!'foo'", false);
352353
test("!foo()", true);
353-
test("~'foo'", false);
354-
test("~foo()", true);
354+
355355
test("typeof 'foo'", false);
356356
test_with_global_variables("typeof a", vec!["a".to_string()], false);
357357
test("typeof foo()", true);
358-
test("void 'foo'", false);
359-
test("void foo()", true);
360-
test("delete 'foo'", true);
361-
test("delete foo()", true);
358+
359+
test("+0", false);
360+
test("+0n", true);
361+
test("+null", false); // 0
362+
test("+true", false); // 1
363+
test("+'foo'", false); // NaN
364+
test_with_global_variables("+Infinity", vec!["Infinity".to_string()], false);
365+
test_with_global_variables("+NaN", vec!["NaN".to_string()], false);
366+
test_with_global_variables("+undefined", vec!["undefined".to_string()], false); // NaN
367+
test("+foo()", true);
368+
test("+foo", true); // foo can be Symbol or BigInt
369+
test("+Symbol()", true);
370+
test("+{ valueOf() { return Symbol() } }", true);
371+
372+
test("-0", false);
373+
test("-0n", false);
374+
test("-null", false); // -0
375+
test("-true", false); // -1
376+
test("-'foo'", false); // -NaN
377+
test_with_global_variables("-Infinity", vec!["Infinity".to_string()], false);
378+
test_with_global_variables("-NaN", vec!["NaN".to_string()], false);
379+
test_with_global_variables("-undefined", vec!["undefined".to_string()], false); // NaN
380+
test("-foo()", true);
381+
test("-foo", true); // foo can be Symbol
382+
test("-Symbol()", true);
383+
test("-{ valueOf() { return Symbol() } }", true);
384+
385+
test("~0", false);
386+
test("~'foo'", false);
387+
test("~foo()", true);
388+
test("~foo", true);
362389
}
363390

364391
#[test]
@@ -477,12 +504,6 @@ fn tests() {
477504
test("'' + { valueOf() { console.log('sideeffect') } }", false);
478505
test("'' + { [s]() { console.log('sideeffect') } }", false); // assuming s is Symbol.toPrimitive
479506

480-
// FIXME: actually these have a side effect, but it's ignored now
481-
test("+s", false); // assuming s is a Symbol
482-
test("+b", false); // assuming b is a BitInt
483-
test("+{ valueOf() { return Symbol() } }", false);
484-
test("~s", false); // assuming s is a Symbol
485-
486507
// FIXME: actually these have a side effect, but it's ignored now
487508
// same for -, *, /, %, **, <<, >>
488509
test("'' + s", false); // assuming s is Symbol

0 commit comments

Comments
 (0)