oxc/crates/oxc_syntax/src/operator.rs
Dunqing 6f0fe38bff fix(semantic)!: correct all ReferenceFlags::Write according to the spec (#7388)
close #7323

According to the specification re-design the JavaScript-part ReferenceFlags inferring approach.

* https://tc39.es/ecma262/#sec-assignment-operators-runtime-semantics-evaluation
* https://tc39.es/ecma262/#sec-postfix-increment-operator-runtime-semantics-evaluation
* https://tc39.es/ecma262/#sec-runtime-semantics-restdestructuringassignmentevaluation
* ... See references of https://tc39.es/ecma262/#sec-putvalue

### Changes

1. The left-hand of `AssignmentExpression` is always `ReferenceFlags::Write`
```js
let a = 0;
console.log(a = 0);
            ^ Write only
```

2. The `argument` of `UpdateExpression` is always `ReferenceFlags::Read | Write`
```js
let a = 0;
a++;
^ Read and Write
```

This change might cause some trouble for `Minfier` to remove this code, because ‘a’ is not used elsewhere. I have taken a look at `esbuild` and `Terser`. Only the `Terser` can remove this code.
2024-11-22 06:08:30 +00:00

538 lines
16 KiB
Rust

//! ECMAScript operators.
//!
//! Not all operators are punctuation - some, such as `delete`, are keywords.
// Silence erroneous warnings from Rust Analyser for `#[derive(Tsify)]`
#![allow(non_snake_case)]
use oxc_allocator::CloneIn;
use oxc_ast_macros::ast;
use oxc_estree::ESTree;
use oxc_span::{cmp::ContentEq, hash::ContentHash};
use crate::precedence::{GetPrecedence, Precedence};
/// Operators that may be used in assignment epxressions.
///
/// ## References
/// - [13.15 Assignment Operators](https://tc39.es/ecma262/#sec-assignment-operators)
#[ast]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[generate_derive(CloneIn, ContentEq, ContentHash, ESTree)]
pub enum AssignmentOperator {
/// `=`
#[estree(rename = "=")]
Assign = 0,
/// `+=`
#[estree(rename = "+=")]
Addition = 1,
/// `-=`
#[estree(rename = "-=")]
Subtraction = 2,
/// `*=`
#[estree(rename = "*=")]
Multiplication = 3,
/// `/=`
#[estree(rename = "/=")]
Division = 4,
/// `%=`
#[estree(rename = "%=")]
Remainder = 5,
/// `**=`
#[estree(rename = "**=")]
Exponential = 6,
/// `<<=`
#[estree(rename = "<<=")]
ShiftLeft = 7,
/// `>>=`
#[estree(rename = ">>=")]
ShiftRight = 8,
/// `>>>=`
#[estree(rename = ">>>=")]
ShiftRightZeroFill = 9,
/// `|=`
#[estree(rename = "|=")]
BitwiseOR = 10,
/// `^=`
#[estree(rename = "^=")]
BitwiseXOR = 11,
/// `&=`
#[estree(rename = "&=")]
BitwiseAnd = 12,
/// `||=`
#[estree(rename = "||=")]
LogicalOr = 13,
/// `&&=`
#[estree(rename = "&&=")]
LogicalAnd = 14,
/// `??=`
#[estree(rename = "??=")]
LogicalNullish = 15,
}
impl AssignmentOperator {
/// Returns `true` for `=`.
pub fn is_assign(self) -> bool {
self == Self::Assign
}
/// Returns `true` for '||=`, `&&=`, and `??=`.
pub fn is_logical(self) -> bool {
matches!(self, Self::LogicalOr | Self::LogicalAnd | Self::LogicalNullish)
}
/// Returns `true` for `+=`, `-=`, `*=`, `/=`, `%=`, and `**=`.
#[rustfmt::skip]
pub fn is_arithmetic(self) -> bool {
matches!(
self,
Self::Addition | Self::Subtraction | Self::Multiplication
| Self::Division | Self::Remainder | Self::Exponential
)
}
/// Returns `true` for `|=`, `^=`, `&=`, `<<=`, `>>=`, and `>>>=`.
#[rustfmt::skip]
pub fn is_bitwise(self) -> bool {
matches!(
self,
Self::ShiftLeft | Self::ShiftRight | Self::ShiftRightZeroFill
| Self::BitwiseOR | Self::BitwiseXOR | Self::BitwiseAnd
)
}
/// Get [`LogicalOperator`] corresponding to this [`AssignmentOperator`].
pub fn to_logical_operator(self) -> Option<LogicalOperator> {
match self {
Self::LogicalOr => Some(LogicalOperator::Or),
Self::LogicalAnd => Some(LogicalOperator::And),
Self::LogicalNullish => Some(LogicalOperator::Coalesce),
_ => None,
}
}
/// Get [`BinaryOperator`] corresponding to this [`AssignmentOperator`].
pub fn to_binary_operator(self) -> Option<BinaryOperator> {
match self {
Self::Addition => Some(BinaryOperator::Addition),
Self::Subtraction => Some(BinaryOperator::Subtraction),
Self::Multiplication => Some(BinaryOperator::Multiplication),
Self::Division => Some(BinaryOperator::Division),
Self::Remainder => Some(BinaryOperator::Remainder),
Self::Exponential => Some(BinaryOperator::Exponential),
Self::ShiftLeft => Some(BinaryOperator::ShiftLeft),
Self::ShiftRight => Some(BinaryOperator::ShiftRight),
Self::ShiftRightZeroFill => Some(BinaryOperator::ShiftRightZeroFill),
Self::BitwiseOR => Some(BinaryOperator::BitwiseOR),
Self::BitwiseXOR => Some(BinaryOperator::BitwiseXOR),
Self::BitwiseAnd => Some(BinaryOperator::BitwiseAnd),
_ => None,
}
}
/// Get the string representation of this operator.
///
/// This is the same as how the operator appears in source code.
pub fn as_str(&self) -> &'static str {
match self {
Self::Assign => "=",
Self::Addition => "+=",
Self::Subtraction => "-=",
Self::Multiplication => "*=",
Self::Division => "/=",
Self::Remainder => "%=",
Self::Exponential => "**=",
Self::ShiftLeft => "<<=",
Self::ShiftRight => ">>=",
Self::ShiftRightZeroFill => ">>>=",
Self::BitwiseOR => "|=",
Self::BitwiseXOR => "^=",
Self::BitwiseAnd => "&=",
Self::LogicalOr => "||=",
Self::LogicalAnd => "&&=",
Self::LogicalNullish => "??=",
}
}
}
/// Operators used in binary expressions. Does not include logical binary
/// operators, which are in [`LogicalOperator`].
///
/// ## References
/// - [12.10 Binary Logical Operators](https://tc39.es/ecma262/#sec-binary-logical-operators)
#[ast]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[generate_derive(CloneIn, ContentEq, ContentHash, ESTree)]
pub enum BinaryOperator {
/// `==`
#[estree(rename = "==")]
Equality = 0,
/// `!=`
#[estree(rename = "!=")]
Inequality = 1,
/// `===`
#[estree(rename = "===")]
StrictEquality = 2,
/// `!==`
#[estree(rename = "!==")]
StrictInequality = 3,
/// `<`
#[estree(rename = "<")]
LessThan = 4,
/// `<=`
#[estree(rename = "<=")]
LessEqualThan = 5,
/// `>`
#[estree(rename = ">")]
GreaterThan = 6,
/// `>=`
#[estree(rename = ">=")]
GreaterEqualThan = 7,
/// `+`
#[estree(rename = "+")]
Addition = 8,
/// `-`
#[estree(rename = "-")]
Subtraction = 9,
/// `*`
#[estree(rename = "*")]
Multiplication = 10,
/// `/`
#[estree(rename = "/")]
Division = 11,
/// `%`
#[estree(rename = "%")]
Remainder = 12,
/// `**`
#[estree(rename = "**")]
Exponential = 13,
/// `<<`
#[estree(rename = "<<")]
ShiftLeft = 14,
/// `>>`
#[estree(rename = ">>")]
ShiftRight = 15,
/// `>>>`
#[estree(rename = ">>>")]
ShiftRightZeroFill = 16,
/// `|`
#[estree(rename = "|")]
BitwiseOR = 17,
/// `^`
#[estree(rename = "^")]
BitwiseXOR = 18,
/// `&`
#[estree(rename = "&")]
BitwiseAnd = 19,
/// `in`
#[estree(rename = "in")]
In = 20,
/// `instanceof`
#[estree(rename = "instanceof")]
Instanceof = 21,
}
impl BinaryOperator {
/// Returns `true` for inequality or inequality operarors
#[rustfmt::skip]
pub fn is_equality(self) -> bool {
matches!(self, Self::Equality | Self::Inequality | Self::StrictEquality | Self::StrictInequality)
}
/// Returns `true` for logical comparison operators
#[rustfmt::skip]
pub fn is_compare(self) -> bool {
matches!(self, Self::LessThan | Self::LessEqualThan | Self::GreaterThan | Self::GreaterEqualThan)
}
/// Returns `true` for arithmetic operators
#[rustfmt::skip]
pub fn is_arithmetic(self) -> bool {
matches!(
self,
Self::Addition | Self::Subtraction | Self::Multiplication
| Self::Division | Self::Remainder | Self::Exponential
)
}
/// Returns `true` for multiplication (`*`), division (`/`), and remainder
/// (`%`) operators
pub fn is_multiplicative(self) -> bool {
matches!(self, Self::Multiplication | Self::Division | Self::Remainder)
}
/// Returns `true` for object relation operators
pub fn is_relational(self) -> bool {
matches!(self, Self::In | Self::Instanceof)
}
/// Returns `true` if this is an [`In`](BinaryOperator::In) operator.
pub fn is_in(self) -> bool {
self == Self::In
}
/// Returns `true` for any bitwise operator
#[rustfmt::skip]
pub fn is_bitwise(self) -> bool {
self.is_bitshift() || matches!(self, Self::BitwiseOR | Self::BitwiseXOR | Self::BitwiseAnd)
}
/// Returns `true` for any bitshift operator
pub fn is_bitshift(self) -> bool {
matches!(self, Self::ShiftLeft | Self::ShiftRight | Self::ShiftRightZeroFill)
}
/// Returns `true` for any numeric or string binary operator
pub fn is_numeric_or_string_binary_operator(self) -> bool {
self.is_arithmetic() || self.is_bitwise()
}
/// Returns `true` if this operator is a keyword instead of punctuation.
pub fn is_keyword(self) -> bool {
matches!(self, Self::In | Self::Instanceof)
}
/// Try to get the operator that performs the inverse comparison operation.
/// [`None`] if this is not a comparison operator.
pub fn compare_inverse_operator(self) -> Option<Self> {
match self {
Self::LessThan => Some(Self::GreaterThan),
Self::LessEqualThan => Some(Self::GreaterEqualThan),
Self::GreaterThan => Some(Self::LessThan),
Self::GreaterEqualThan => Some(Self::LessEqualThan),
_ => None,
}
}
/// Try to get the operator that performs the inverse equality operation.
/// [`None`] if this is not an equality operator.
pub fn equality_inverse_operator(self) -> Option<Self> {
match self {
Self::Equality => Some(Self::Inequality),
Self::Inequality => Some(Self::Equality),
Self::StrictEquality => Some(Self::StrictInequality),
Self::StrictInequality => Some(Self::StrictEquality),
_ => None,
}
}
/// The string representation of this operator as it appears in source code.
pub fn as_str(&self) -> &'static str {
match self {
Self::Equality => "==",
Self::Inequality => "!=",
Self::StrictEquality => "===",
Self::StrictInequality => "!==",
Self::LessThan => "<",
Self::LessEqualThan => "<=",
Self::GreaterThan => ">",
Self::GreaterEqualThan => ">=",
Self::Addition => "+",
Self::Subtraction => "-",
Self::Multiplication => "*",
Self::Division => "/",
Self::Remainder => "%",
Self::Exponential => "**",
Self::ShiftLeft => "<<",
Self::ShiftRight => ">>",
Self::ShiftRightZeroFill => ">>>",
Self::BitwiseOR => "|",
Self::BitwiseXOR => "^",
Self::BitwiseAnd => "&",
Self::In => "in",
Self::Instanceof => "instanceof",
}
}
/// Get the operator that has a lower precedence than this operator by a
/// single level. Use [`BinaryOperator::precedence`] to get the operator
/// with a higher precedence.
pub fn lower_precedence(&self) -> Precedence {
match self {
Self::BitwiseOR => Precedence::LogicalAnd,
Self::BitwiseXOR => Precedence::BitwiseOr,
Self::BitwiseAnd => Precedence::BitwiseXor,
Self::Equality | Self::Inequality | Self::StrictEquality | Self::StrictInequality => {
Precedence::BitwiseAnd
}
Self::LessThan
| Self::LessEqualThan
| Self::GreaterThan
| Self::GreaterEqualThan
| Self::Instanceof
| Self::In => Precedence::Equals,
Self::ShiftLeft | Self::ShiftRight | Self::ShiftRightZeroFill => Precedence::Compare,
Self::Addition | Self::Subtraction => Precedence::Shift,
Self::Multiplication | Self::Remainder | Self::Division => Precedence::Add,
Self::Exponential => Precedence::Multiply,
}
}
}
impl GetPrecedence for BinaryOperator {
fn precedence(&self) -> Precedence {
match self {
Self::BitwiseOR => Precedence::BitwiseOr,
Self::BitwiseXOR => Precedence::BitwiseXor,
Self::BitwiseAnd => Precedence::BitwiseAnd,
Self::Equality | Self::Inequality | Self::StrictEquality | Self::StrictInequality => {
Precedence::Equals
}
Self::LessThan
| Self::LessEqualThan
| Self::GreaterThan
| Self::GreaterEqualThan
| Self::Instanceof
| Self::In => Precedence::Compare,
Self::ShiftLeft | Self::ShiftRight | Self::ShiftRightZeroFill => Precedence::Shift,
Self::Subtraction | Self::Addition => Precedence::Add,
Self::Multiplication | Self::Remainder | Self::Division => Precedence::Multiply,
Self::Exponential => Precedence::Exponentiation,
}
}
}
/// Logical binary operators
#[ast]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[generate_derive(CloneIn, ContentEq, ContentHash, ESTree)]
pub enum LogicalOperator {
/// `||`
#[estree(rename = "||")]
Or = 0,
/// `&&`
#[estree(rename = "&&")]
And = 1,
/// `??`
#[estree(rename = "??")]
Coalesce = 2,
}
impl LogicalOperator {
/// Get the string representation of this operator as it appears in source code.
pub fn as_str(&self) -> &'static str {
match self {
Self::Or => "||",
Self::And => "&&",
Self::Coalesce => "??",
}
}
/// Get the operator that has a lower precedence than this operator by a
/// single level. Use [`BinaryOperator::precedence`] to get the operator
/// with a higher precedence.
pub fn lower_precedence(&self) -> Precedence {
match self {
Self::Or => Precedence::NullishCoalescing,
Self::And => Precedence::LogicalOr,
Self::Coalesce => Precedence::Conditional,
}
}
}
impl GetPrecedence for LogicalOperator {
fn precedence(&self) -> Precedence {
match self {
Self::Or => Precedence::LogicalOr,
Self::And => Precedence::LogicalAnd,
Self::Coalesce => Precedence::NullishCoalescing,
}
}
}
/// Operators used in unary operators.
///
/// Does not include self-modifying operators, which are in [`UpdateOperator`].
///
/// ## References
/// - [12.5 Unary Operators](https://tc39.es/ecma262/#sec-unary-operators)
#[ast]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[generate_derive(CloneIn, ContentEq, ContentHash, ESTree)]
pub enum UnaryOperator {
/// `+`
#[estree(rename = "+")]
UnaryPlus = 0,
/// `-`
#[estree(rename = "-")]
UnaryNegation = 1,
/// `!`
#[estree(rename = "!")]
LogicalNot = 2,
/// `~`
#[estree(rename = "~")]
BitwiseNot = 3,
/// `typeof`
#[estree(rename = "typeof")]
Typeof = 4,
/// `void`
#[estree(rename = "void")]
Void = 5,
/// `delete`
#[estree(rename = "delete")]
Delete = 6,
}
impl UnaryOperator {
/// Returns `true` if this operator is a unary arithmetic operator.
pub fn is_arithmetic(self) -> bool {
matches!(self, Self::UnaryPlus | Self::UnaryNegation)
}
/// Returns `true` if this operator is a [`LogicalNot`].
///
/// [`LogicalNot`]: UnaryOperator::LogicalNot
pub fn is_not(self) -> bool {
self == Self::LogicalNot
}
/// Returns `true` if this operator is a bitwise operator.
pub fn is_bitwise(self) -> bool {
self == Self::BitwiseNot
}
/// Returns `true` if this is the [`void`](UnaryOperator::Void) operator.
pub fn is_void(self) -> bool {
self == Self::Void
}
/// Returns `true` if this operator is a keyword instead of punctuation.
pub fn is_keyword(self) -> bool {
matches!(self, Self::Typeof | Self::Void | Self::Delete)
}
/// Get the string representation of this operator as it appears in source code.
pub fn as_str(&self) -> &'static str {
match self {
Self::UnaryPlus => "+",
Self::UnaryNegation => "-",
Self::LogicalNot => "!",
Self::BitwiseNot => "~",
Self::Typeof => "typeof",
Self::Void => "void",
Self::Delete => "delete",
}
}
}
/// Unary update operators.
#[ast]
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
#[generate_derive(CloneIn, ContentEq, ContentHash, ESTree)]
pub enum UpdateOperator {
/// `++`
#[estree(rename = "++")]
Increment = 0,
/// `--`
#[estree(rename = "--")]
Decrement = 1,
}
impl UpdateOperator {
/// Get the string representation of this operator as it appears in source code.
pub fn as_str(&self) -> &'static str {
match self {
Self::Increment => "++",
Self::Decrement => "--",
}
}
}