diff --git a/crates/oxc_hir/src/hir.rs b/crates/oxc_hir/src/hir.rs index f046706e7..1c1fce9db 100644 --- a/crates/oxc_hir/src/hir.rs +++ b/crates/oxc_hir/src/hir.rs @@ -122,6 +122,11 @@ impl<'a> Expression<'a> { matches!(self, Self::Identifier(ident) if ident.name == "undefined") } + /// Determines whether the given expr is a `NaN` literal + pub fn is_nan(&self) -> bool { + matches!(self, Self::Identifier(ident) if ident.name == "NaN") + } + /// Determines whether the given expr is a `void 0` pub fn is_void_0(&self) -> bool { if let Self::UnaryExpression(expr) = self diff --git a/crates/oxc_minifier/src/compressor/fold.rs b/crates/oxc_minifier/src/compressor/fold.rs index d127934e0..af8d0ae79 100644 --- a/crates/oxc_minifier/src/compressor/fold.rs +++ b/crates/oxc_minifier/src/compressor/fold.rs @@ -483,6 +483,13 @@ impl<'a> Compressor<'a> { _ => Tri::Unknown, }; } + + // Then, try to evaluate based on the value of the expression. + // There's only one special case: + // Any strict equality comparison against NaN returns false. + if left_expr.is_nan() || right_expr.is_nan() { + return Tri::False; + } Tri::Unknown } diff --git a/crates/oxc_minifier/tests/closure/fold_constants.rs b/crates/oxc_minifier/tests/closure/fold_constants.rs index 76f539caa..f0fcb02c7 100644 --- a/crates/oxc_minifier/tests/closure/fold_constants.rs +++ b/crates/oxc_minifier/tests/closure/fold_constants.rs @@ -404,6 +404,43 @@ fn test_string_bigint_comparison() { test_wcb("'1' !== 1n", "true"); } +#[test] +fn test_nan_comparison() { + test_wcb("NaN < 1", "false"); + test_wcb("NaN <= 1", "false"); + test_wcb("NaN > 1", "false"); + test_wcb("NaN >= 1", "false"); + test_wcb("NaN < 1n", "false"); + test_wcb("NaN <= 1n", "false"); + test_wcb("NaN > 1n", "false"); + test_wcb("NaN >= 1n", "false"); + + test_wcb("NaN < NaN", "false"); + test_wcb("NaN >= NaN", "false"); + test_wcb("NaN == NaN", "false"); + test_wcb("NaN === NaN", "false"); + + test_wcb("NaN < null", "false"); + test_wcb("null >= NaN", "false"); + test_wcb("NaN == null", "false"); + test_wcb("null != NaN", "true"); + test_wcb("null === NaN", "false"); + + test_wcb("NaN < undefined", "false"); + test_wcb("undefined >= NaN", "false"); + test_wcb("NaN == undefined", "false"); + test_wcb("undefined != NaN", "true"); + test_wcb("undefined === NaN", "false"); + + test_same("NaN=NaN"); + test_same("NaN==x"); + test_same("x!=NaN"); + test_wcb("NaN === x", "false"); + test_wcb("x !== NaN", "true"); + test_same("NaN==foo()"); +} + #[test] fn js_typeof() { test("x = typeof 1", "x='number'");