mirror of
https://github.com/danbulant/oxc
synced 2026-05-19 04:08:41 +00:00
feat(linter/tree-shaking): support UnaryExpression (#3153)
This commit is contained in:
parent
56a0db8621
commit
5c21b7f843
4 changed files with 410 additions and 50 deletions
|
|
@ -1,5 +1,3 @@
|
|||
use std::cell::{Cell, RefCell};
|
||||
|
||||
use oxc_ast::{
|
||||
ast::{
|
||||
match_declaration, match_expression, match_member_expression,
|
||||
|
|
@ -21,12 +19,13 @@ use oxc_semantic::{AstNode, SymbolId};
|
|||
use oxc_span::{GetSpan, Span};
|
||||
use oxc_syntax::operator::{LogicalOperator, UnaryOperator};
|
||||
use rustc_hash::FxHashSet;
|
||||
use std::{cell::Cell, cell::RefCell};
|
||||
|
||||
use crate::{
|
||||
ast_util::{get_declaration_of_variable, get_symbol_id_of_variable},
|
||||
utils::{
|
||||
calculate_binary_operation, calculate_logical_operation, get_write_expr,
|
||||
has_comment_about_side_effect_check, has_pure_notation, no_effects, Value,
|
||||
calculate_binary_operation, calculate_logical_operation, calculate_unary_operation,
|
||||
get_write_expr, has_comment_about_side_effect_check, has_pure_notation, no_effects, Value,
|
||||
},
|
||||
LintContext,
|
||||
};
|
||||
|
|
@ -117,6 +116,9 @@ impl<'a> ListenerMap for Statement<'a> {
|
|||
finalizer.body.iter().for_each(|stmt| stmt.report_effects(options));
|
||||
});
|
||||
}
|
||||
Self::ThrowStatement(stmt) => {
|
||||
options.ctx.diagnostic(NoSideEffectsDiagnostic::Throw(stmt.span));
|
||||
}
|
||||
Self::BlockStatement(stmt) => {
|
||||
stmt.body.iter().for_each(|stmt| stmt.report_effects(options));
|
||||
}
|
||||
|
|
@ -528,6 +530,9 @@ impl<'a> ListenerMap for Expression<'a> {
|
|||
Self::UnaryExpression(expr) => {
|
||||
expr.get_value_and_report_effects(options);
|
||||
}
|
||||
Self::UpdateExpression(expr) => {
|
||||
expr.argument.report_effects_when_assigned(options);
|
||||
}
|
||||
Self::ArrowFunctionExpression(_)
|
||||
| Self::FunctionExpression(_)
|
||||
| Self::Identifier(_)
|
||||
|
|
@ -644,12 +649,13 @@ impl<'a> ListenerMap for UnaryExpression<'a> {
|
|||
Expression::PrivateFieldExpression(expr) => {
|
||||
expr.object.report_effects_when_mutated(options);
|
||||
}
|
||||
_ => {}
|
||||
_ => options.ctx.diagnostic(NoSideEffectsDiagnostic::Delete(self.argument.span())),
|
||||
}
|
||||
return Value::Unknown;
|
||||
}
|
||||
|
||||
// TODO
|
||||
Value::Unknown
|
||||
let value = self.argument.get_value_and_report_effects(options);
|
||||
calculate_unary_operation(self.operator, value)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,6 +69,14 @@ enum NoSideEffectsDiagnostic {
|
|||
#[error("eslint-plugin-tree-shaking(no-side-effects-in-initialization): Debugger statements are side-effects")]
|
||||
#[diagnostic(severity(warning))]
|
||||
Debugger(#[label] Span),
|
||||
|
||||
#[error("eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of deleting anything but a MemberExpression")]
|
||||
#[diagnostic(severity(warning))]
|
||||
Delete(#[label] Span),
|
||||
|
||||
#[error("eslint-plugin-tree-shaking(no-side-effects-in-initialization): Throwing an error is a side-effect")]
|
||||
#[diagnostic(severity(warning))]
|
||||
Throw(#[label] Span),
|
||||
}
|
||||
|
||||
/// <https://github.com/lukastaegert/eslint-plugin-tree-shaking/blob/master/src/rules/no-side-effects-in-initialization.ts>
|
||||
|
|
@ -307,12 +315,12 @@ fn test() {
|
|||
// MemberExpression when mutated
|
||||
"const x = {};x.y = ext",
|
||||
"const x = {y: 1};delete x.y",
|
||||
// // MetaProperty
|
||||
// "function x(){const y = new.target}; x()",
|
||||
// // MethodDefinition
|
||||
// "class x {a(){}}",
|
||||
// "class x {static a(){}}",
|
||||
// // NewExpression
|
||||
// MetaProperty
|
||||
"function x(){const y = new.target}; x()",
|
||||
// MethodDefinition
|
||||
"class x {a(){}}",
|
||||
"class x {static a(){}}",
|
||||
// NewExpression
|
||||
"const x = new (function (){this.x = 1})()",
|
||||
"function x(){this.y = 1}; const z = new x()",
|
||||
"/*@__PURE__*/ new ext()",
|
||||
|
|
@ -351,17 +359,17 @@ fn test() {
|
|||
"const y = new (function (){{this.x = 1}})()",
|
||||
"const y = new (function (){(()=>{this.x = 1})()})()",
|
||||
"function x(){this.y = 1}; const y = new x()",
|
||||
// // TryStatement
|
||||
// "try {} catch (error) {}",
|
||||
// "try {} finally {}",
|
||||
// "try {} catch (error) {} finally {}",
|
||||
// // UnaryExpression
|
||||
// "!ext",
|
||||
// "const x = {};delete x.y",
|
||||
// r#"const x = {};delete x["y"]"#,
|
||||
// // UpdateExpression
|
||||
// "let x=1;x++",
|
||||
// "const x = {};x.y++",
|
||||
// TryStatement
|
||||
"try {} catch (error) {}",
|
||||
"try {} finally {}",
|
||||
"try {} catch (error) {} finally {}",
|
||||
// UnaryExpression
|
||||
"!ext",
|
||||
"const x = {};delete x.y",
|
||||
r#"const x = {};delete x["y"]"#,
|
||||
// UpdateExpression
|
||||
"let x=1;x++",
|
||||
"const x = {};x.y++",
|
||||
// // VariableDeclaration
|
||||
// "const x = 1",
|
||||
// // VariableDeclarator
|
||||
|
|
@ -535,7 +543,7 @@ fn test() {
|
|||
"const x = ()=>{}; const {y} = x(); y()",
|
||||
"const x = ()=>{}; const [y] = x(); y()",
|
||||
// // Identifier when mutated
|
||||
// "var x = ext; x.y = 1",
|
||||
"var x = ext; x.y = 1",
|
||||
// "var x = {}; x = ext; x.y = 1",
|
||||
// "var x = {}; var x = ext; x.y = 1",
|
||||
// "var x = {}; x = ext; x.y = 1; x.y = 1; x.y = 1",
|
||||
|
|
@ -596,8 +604,8 @@ fn test() {
|
|||
"const x = {y: ext};x.y.z = 1",
|
||||
"const x = {y:ext};const y = x.y; y.z = 1",
|
||||
"const x = {y: ext};delete x.y.z",
|
||||
// // MethodDefinition
|
||||
// "class x {static [ext()](){}}",
|
||||
// MethodDefinition
|
||||
"class x {static [ext()](){}}",
|
||||
// NewExpression
|
||||
"const x = new ext()",
|
||||
"new ext()",
|
||||
|
|
@ -636,19 +644,19 @@ fn test() {
|
|||
"(function(){this.x = 1}())",
|
||||
"const y = new (function (){(function(){this.x = 1}())})()",
|
||||
"function x(){this.y = 1}; x()",
|
||||
// // ThrowStatement
|
||||
// r#"throw new Error("Hello Error")"#,
|
||||
// // TryStatement
|
||||
// "try {ext()} catch (error) {}",
|
||||
// "try {} finally {ext()}",
|
||||
// // UnaryExpression
|
||||
// "!ext()",
|
||||
// "delete ext.x",
|
||||
// r#"delete ext["x"]"#,
|
||||
// "const x = ()=>{};delete x()",
|
||||
// // UpdateExpression
|
||||
// "ext++",
|
||||
// "const x = {};x[ext()]++",
|
||||
// ThrowStatement
|
||||
r#"throw new Error("Hello Error")"#,
|
||||
// TryStatement
|
||||
"try {ext()} catch (error) {}",
|
||||
"try {} finally {ext()}",
|
||||
// UnaryExpression
|
||||
"!ext()",
|
||||
"delete ext.x",
|
||||
r#"delete ext["x"]"#,
|
||||
"const x = ()=>{};delete x()",
|
||||
// UpdateExpression
|
||||
"ext++",
|
||||
"const x = {};x[ext()]++",
|
||||
// // VariableDeclaration
|
||||
// "const x = ext()",
|
||||
// // VariableDeclarator
|
||||
|
|
|
|||
|
|
@ -734,6 +734,12 @@ expression: no_side_effects_in_initialization
|
|||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of mutating `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:9]
|
||||
1 │ var x = ext; x.y = 1
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:5]
|
||||
1 │ if (ext()>0){}
|
||||
|
|
@ -992,6 +998,12 @@ expression: no_side_effects_in_initialization
|
|||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:18]
|
||||
1 │ class x {static [ext()](){}}
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:15]
|
||||
1 │ const x = new ext()
|
||||
|
|
@ -1033,3 +1045,57 @@ expression: no_side_effects_in_initialization
|
|||
1 │ function x(){this.y = 1}; x()
|
||||
· ────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Throwing an error is a side-effect
|
||||
╭─[no_side_effects_in_initialization.tsx:1:1]
|
||||
1 │ throw new Error("Hello Error")
|
||||
· ──────────────────────────────
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:6]
|
||||
1 │ try {ext()} catch (error) {}
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:17]
|
||||
1 │ try {} finally {ext()}
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:2]
|
||||
1 │ !ext()
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of mutating `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:8]
|
||||
1 │ delete ext.x
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of mutating `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:8]
|
||||
1 │ delete ext["x"]
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of deleting anything but a MemberExpression
|
||||
╭─[no_side_effects_in_initialization.tsx:1:25]
|
||||
1 │ const x = ()=>{};delete x()
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of assignment to `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:1]
|
||||
1 │ ext++
|
||||
· ───
|
||||
╰────
|
||||
|
||||
⚠ eslint-plugin-tree-shaking(no-side-effects-in-initialization): Cannot determine side-effects of calling global function `ext`
|
||||
╭─[no_side_effects_in_initialization.tsx:1:16]
|
||||
1 │ const x = {};x[ext()]++
|
||||
· ───
|
||||
╰────
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
use oxc_ast::{ast::Expression, AstKind, CommentKind};
|
||||
use oxc_semantic::AstNodeId;
|
||||
use oxc_span::Span;
|
||||
use oxc_syntax::operator::{BinaryOperator, LogicalOperator};
|
||||
use oxc_syntax::operator::{BinaryOperator, LogicalOperator, UnaryOperator};
|
||||
|
||||
use crate::LintContext;
|
||||
|
||||
|
|
@ -52,6 +52,21 @@ impl Value {
|
|||
Value::String(str) => Some(!matches!(str, StringValue::Empty)),
|
||||
}
|
||||
}
|
||||
/// If the value is a boolean, return the negation of the boolean, otherwise return `None`.
|
||||
pub fn neg_bool(&self) -> Option<Value> {
|
||||
match self {
|
||||
Value::Boolean(boolean) => Some(Value::Boolean(!*boolean)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
pub fn to_bool(self) -> Option<bool> {
|
||||
match self {
|
||||
Value::Boolean(boolean) => Some(boolean),
|
||||
Value::Number(num) => Some(num != 0.0),
|
||||
Value::String(str) => Some(!matches!(str, StringValue::Empty)),
|
||||
Value::Unknown => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_write_expr<'a, 'b>(
|
||||
|
|
@ -174,6 +189,8 @@ pub fn get_leading_tree_shaking_comment<'a>(span: Span, ctx: &LintContext<'a>) -
|
|||
|
||||
/// Port from <https://github.com/lukastaegert/eslint-plugin-tree-shaking/blob/463fa1f0bef7caa2b231a38b9c3557051f506c92/src/rules/no-side-effects-in-initialization.ts#L136-L161>
|
||||
/// <https://tc39.es/ecma262/#sec-evaluatestringornumericbinaryexpression>
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
#[allow(clippy::cast_sign_loss)]
|
||||
pub fn calculate_binary_operation(op: BinaryOperator, left: Value, right: Value) -> Value {
|
||||
match op {
|
||||
BinaryOperator::Addition => match (left, right) {
|
||||
|
|
@ -191,6 +208,14 @@ pub fn calculate_binary_operation(op: BinaryOperator, left: Value, right: Value)
|
|||
(Value::Number(a), Value::Number(b)) => Value::Number(a - b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::Multiplication => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Number(a * b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::Division => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Number(a / b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
// <https://tc39.es/ecma262/#sec-islessthan>
|
||||
BinaryOperator::LessThan => match (left, right) {
|
||||
// <https://tc39.es/ecma262/#sec-numeric-types-number-lessThan>
|
||||
|
|
@ -200,24 +225,131 @@ pub fn calculate_binary_operation(op: BinaryOperator, left: Value, right: Value)
|
|||
(Value::Number(a), Value::Number(b)) => Value::Boolean(a < b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
_ => Value::Unknown,
|
||||
BinaryOperator::GreaterEqualThan => {
|
||||
calculate_binary_operation(BinaryOperator::LessThan, left, right)
|
||||
.neg_bool()
|
||||
.unwrap_or(Value::Unknown)
|
||||
}
|
||||
BinaryOperator::Equality => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Boolean((a - b).abs() < f64::EPSILON),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::Inequality => {
|
||||
calculate_binary_operation(BinaryOperator::Equality, left, right)
|
||||
.neg_bool()
|
||||
.unwrap_or(Value::Unknown)
|
||||
}
|
||||
BinaryOperator::StrictEquality => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Boolean((a - b).abs() < f64::EPSILON),
|
||||
(Value::Boolean(a), Value::Boolean(b)) => Value::Boolean(a == b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::StrictInequality => {
|
||||
calculate_binary_operation(BinaryOperator::StrictEquality, left, right)
|
||||
.neg_bool()
|
||||
.unwrap_or(Value::Unknown)
|
||||
}
|
||||
BinaryOperator::LessEqualThan => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Boolean(a <= b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::GreaterThan => {
|
||||
calculate_binary_operation(BinaryOperator::LessEqualThan, left, right)
|
||||
.neg_bool()
|
||||
.unwrap_or(Value::Unknown)
|
||||
}
|
||||
|
||||
BinaryOperator::ShiftRightZeroFill => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => {
|
||||
Value::Number(f64::from((a as u32) >> (b as u32)))
|
||||
}
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::Remainder => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Number(a % b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::BitwiseOR => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Number(f64::from(a as i32 | b as i32)),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::BitwiseXOR => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Number(f64::from(a as i32 ^ b as i32)),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::BitwiseAnd => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Number(f64::from(a as i32 & b as i32)),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::Exponential => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Number(a.powf(b)),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::ShiftLeft => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => {
|
||||
Value::Number(f64::from((a as i32) << (b as i32)))
|
||||
}
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::ShiftRight => match (left, right) {
|
||||
(Value::Number(a), Value::Number(b)) => Value::Number(f64::from(a as i32 >> b as i32)),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
BinaryOperator::In | BinaryOperator::Instanceof => Value::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
/// <https://tc39.es/ecma262/#sec-binary-logical-operators-runtime-semantics-evaluation>
|
||||
pub fn calculate_logical_operation(op: LogicalOperator, left: Value, right: Value) -> Value {
|
||||
match op {
|
||||
LogicalOperator::And => match (left, right) {
|
||||
(Value::Boolean(a), Value::Boolean(b)) => Value::Boolean(a && b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
LogicalOperator::Or => match (left, right) {
|
||||
(Value::Boolean(a), Value::Boolean(b)) => Value::Boolean(a || b),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
LogicalOperator::And => {
|
||||
let left = left.to_bool();
|
||||
let right = right.to_bool();
|
||||
|
||||
match (left, right) {
|
||||
(Some(false), _) | (_, Some(false)) => Value::Boolean(false),
|
||||
(Some(true), Some(true)) => Value::Boolean(true),
|
||||
_ => Value::Unknown,
|
||||
}
|
||||
}
|
||||
LogicalOperator::Or => {
|
||||
let left = left.to_bool();
|
||||
let right = right.to_bool();
|
||||
|
||||
match (left, right) {
|
||||
(Some(true), _) | (_, Some(true)) => Value::Boolean(true),
|
||||
(Some(false), Some(false)) => Value::Boolean(false),
|
||||
_ => Value::Unknown,
|
||||
}
|
||||
}
|
||||
LogicalOperator::Coalesce => Value::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::cast_possible_truncation)]
|
||||
pub fn calculate_unary_operation(op: UnaryOperator, value: Value) -> Value {
|
||||
match op {
|
||||
UnaryOperator::UnaryNegation => match value {
|
||||
Value::Number(num) => Value::Number(-num),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
UnaryOperator::UnaryPlus => match value {
|
||||
Value::Number(num) => Value::Number(num),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
UnaryOperator::LogicalNot => match value {
|
||||
Value::Boolean(boolean) => Value::Boolean(!boolean),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
UnaryOperator::BitwiseNot => match value {
|
||||
Value::Number(num) => Value::Number(f64::from(!(num as i32))),
|
||||
_ => Value::Unknown,
|
||||
},
|
||||
UnaryOperator::Typeof => Value::String(StringValue::NonEmpty),
|
||||
UnaryOperator::Void | UnaryOperator::Delete => Value::Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_calculate_binary_operation() {
|
||||
use oxc_syntax::operator::BinaryOperator;
|
||||
|
|
@ -245,8 +377,156 @@ fn test_calculate_binary_operation() {
|
|||
let op = BinaryOperator::Subtraction;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Number(-1.0));
|
||||
|
||||
// "*",
|
||||
let op = BinaryOperator::Multiplication;
|
||||
assert_eq!(fun(op, Value::Number(4.0), Value::Number(2.0),), Value::Number(8.0));
|
||||
|
||||
// "/",
|
||||
let op = BinaryOperator::Division;
|
||||
assert_eq!(fun(op, Value::Number(4.0), Value::Number(2.0),), Value::Number(2.0));
|
||||
|
||||
// "<"
|
||||
let op = BinaryOperator::LessThan;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Unknown, Value::Number(2.0),), Value::Boolean(false));
|
||||
|
||||
// ">=",
|
||||
let op = BinaryOperator::GreaterEqualThan;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Number(2.0), Value::Number(2.0),), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Number(3.0), Value::Number(2.0),), Value::Boolean(true));
|
||||
|
||||
// "==",
|
||||
let op = BinaryOperator::Equality;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(1.0),), Value::Boolean(true));
|
||||
|
||||
// "!=",
|
||||
let op = BinaryOperator::Inequality;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(1.0),), Value::Boolean(false));
|
||||
|
||||
// "===",
|
||||
let op = BinaryOperator::StrictEquality;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(1.0),), Value::Boolean(true));
|
||||
// "!==",
|
||||
let op = BinaryOperator::StrictInequality;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(1.0),), Value::Boolean(false));
|
||||
|
||||
// "<=",
|
||||
let op = BinaryOperator::LessEqualThan;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Number(2.0), Value::Number(2.0),), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Number(3.0), Value::Number(2.0),), Value::Boolean(false));
|
||||
|
||||
// ">",
|
||||
let op = BinaryOperator::GreaterThan;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Number(2.0), Value::Number(2.0),), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Number(3.0), Value::Number(2.0),), Value::Boolean(true));
|
||||
|
||||
// "<<",
|
||||
let op = BinaryOperator::ShiftLeft;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Number(4.0));
|
||||
|
||||
// ">>",
|
||||
let op = BinaryOperator::ShiftRight;
|
||||
assert_eq!(fun(op, Value::Number(4.0), Value::Number(2.0),), Value::Number(1.0));
|
||||
|
||||
// ">>>",
|
||||
let op = BinaryOperator::ShiftRightZeroFill;
|
||||
assert_eq!(fun(op, Value::Number(4.0), Value::Number(2.0),), Value::Number(1.0));
|
||||
|
||||
// "%",
|
||||
let op = BinaryOperator::Remainder;
|
||||
assert_eq!(fun(op, Value::Number(4.0), Value::Number(2.0),), Value::Number(0.0));
|
||||
|
||||
// "|",
|
||||
let op = BinaryOperator::BitwiseOR;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Number(3.0));
|
||||
|
||||
// "^",
|
||||
let op = BinaryOperator::BitwiseXOR;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Number(3.0));
|
||||
|
||||
// "&",
|
||||
let op = BinaryOperator::BitwiseAnd;
|
||||
assert_eq!(fun(op, Value::Number(1.0), Value::Number(2.0),), Value::Number(0.0));
|
||||
|
||||
// "in",
|
||||
let op = BinaryOperator::In;
|
||||
assert_eq!(fun(op, Value::Unknown, Value::Number(2.0),), Value::Unknown);
|
||||
|
||||
// "instanceof",
|
||||
let op = BinaryOperator::Instanceof;
|
||||
assert_eq!(fun(op, Value::Unknown, Value::Number(2.0),), Value::Unknown);
|
||||
|
||||
// "**",
|
||||
let op = BinaryOperator::Exponential;
|
||||
assert_eq!(fun(op, Value::Number(2.0), Value::Number(3.0),), Value::Number(8.0));
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_logical_operation() {
|
||||
use oxc_syntax::operator::LogicalOperator;
|
||||
|
||||
let fun = calculate_logical_operation;
|
||||
|
||||
// "&&"
|
||||
let op = LogicalOperator::And;
|
||||
assert_eq!(fun(op, Value::Boolean(true), Value::Boolean(true)), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Boolean(true), Value::Boolean(false)), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Boolean(false), Value::Boolean(true)), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Boolean(false), Value::Boolean(false)), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Unknown, Value::Boolean(true)), Value::Unknown);
|
||||
assert_eq!(fun(op, Value::Boolean(true), Value::Unknown), Value::Unknown);
|
||||
assert_eq!(fun(op, Value::Unknown, Value::Unknown), Value::Unknown);
|
||||
|
||||
// "||"
|
||||
let op = LogicalOperator::Or;
|
||||
assert_eq!(fun(op, Value::Boolean(true), Value::Boolean(true)), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Boolean(true), Value::Boolean(false)), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Boolean(false), Value::Boolean(true)), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Boolean(false), Value::Boolean(false)), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Unknown, Value::Boolean(true)), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Boolean(true), Value::Unknown), Value::Boolean(true));
|
||||
assert_eq!(fun(op, Value::Unknown, Value::Unknown), Value::Unknown);
|
||||
|
||||
// "??"
|
||||
let op = LogicalOperator::Coalesce;
|
||||
assert_eq!(fun(op, Value::Boolean(true), Value::Boolean(true)), Value::Unknown);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_unary_operation() {
|
||||
use oxc_syntax::operator::UnaryOperator;
|
||||
|
||||
let fun = calculate_unary_operation;
|
||||
|
||||
// "-"
|
||||
let op = UnaryOperator::UnaryNegation;
|
||||
assert_eq!(fun(op, Value::Number(1.0)), Value::Number(-1.0));
|
||||
assert_eq!(fun(op, Value::Boolean(true)), Value::Unknown);
|
||||
|
||||
// "+"
|
||||
let op = UnaryOperator::UnaryPlus;
|
||||
assert_eq!(fun(op, Value::Number(1.0)), Value::Number(1.0));
|
||||
assert_eq!(fun(op, Value::Boolean(true)), Value::Unknown);
|
||||
|
||||
// "!"
|
||||
let op = UnaryOperator::LogicalNot;
|
||||
assert_eq!(fun(op, Value::Boolean(true)), Value::Boolean(false));
|
||||
assert_eq!(fun(op, Value::Number(1.0)), Value::Unknown);
|
||||
|
||||
// "~"
|
||||
let op = UnaryOperator::BitwiseNot;
|
||||
assert_eq!(fun(op, Value::Number(1.0)), Value::Number(-2.0));
|
||||
assert_eq!(fun(op, Value::Boolean(true)), Value::Unknown);
|
||||
|
||||
// "typeof"
|
||||
let op = UnaryOperator::Typeof;
|
||||
assert_eq!(fun(op, Value::Number(1.0)), Value::String(StringValue::NonEmpty));
|
||||
assert_eq!(fun(op, Value::Boolean(true)), Value::String(StringValue::NonEmpty));
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue