oxc_span/cmp.rs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
//! Specialized comparison traits
/// This trait works similarly to [PartialEq] but it gives the liberty of checking the equality of the
/// content loosely. This would mean the implementor can skip some parts of the content while doing
/// equality checks.
/// As an example, In AST types we ignore fields such as [crate::Span].
///
/// One should always prefer using the [PartialEq] over this since implementations of this trait
/// inherently are slower or in the best-case scenario as fast as the [PartialEq] comparison.
pub trait ContentEq {
/// This method tests for contents of `self` and `other` to be equal.
#[must_use]
fn content_eq(&self, other: &Self) -> bool;
/// This method tests for contents of `self` and `other` not to be equal.
/// The default implementation is almost always
/// sufficient, and should not be overridden without very good reason.
#[inline]
#[must_use]
fn content_ne(&self, other: &Self) -> bool {
!self.content_eq(other)
}
}
impl ContentEq for () {
#[inline]
fn content_eq(&self, _other: &()) -> bool {
true
}
#[inline]
fn content_ne(&self, _other: &()) -> bool {
false
}
}
/// Compare `f64` as bits instead of using `==`.
///
/// Result is the same as `partial_eq` (`==`), with the following exceptions:
///
/// * `+0` and `-0` are not `content_eq` (they are `partial_eq`).
/// * `f64::NAN` and `f64::NAN` are `content_eq` (they are not `partial_eq`).
///
/// <https://play.rust-lang.org/?version=stable&mode=release&edition=2021&gist=5f9ec4b26128363a660e27582d1de7cd>
///
/// ### NaN
///
/// Comparison of `NaN` is complicated. From Rust's docs for `f64`:
///
/// > Note that IEEE 754 doesn’t define just a single NaN value;
/// > a plethora of bit patterns are considered to be NaN.
///
/// <https://doc.rust-lang.org/std/primitive.f64.html#associatedconstant.NAN>
///
/// If either value is `NaN`, `f64::content_eq` only returns `true` if both are the *same* `NaN`,
/// with the same bit pattern. This means, for example:
///
/// ```
/// f64::NAN.content_eq(f64::NAN) == true
/// f64::NAN.content_eq(-f64::NAN) == false
/// f64::NAN.content_eq(--f64::NAN) == true
/// ```
///
/// Any other `NaN`s which are created through an arithmetic operation, rather than explicitly
/// with `f64::NAN`, are not guaranteed to equal `f64::NAN`.
///
/// ```
/// // This results in `false` on at least some flavors of `x84_64`,
/// // but that's not specified - could also result in `true`!
/// (-1f64).sqrt().content_eq(f64::NAN) == false
/// ```
impl ContentEq for f64 {
#[inline]
fn content_eq(&self, other: &Self) -> bool {
self.to_bits() == other.to_bits()
}
}
/// Blanket implementation for [Option] types
impl<T: ContentEq> ContentEq for Option<T> {
#[inline]
fn content_eq(&self, other: &Self) -> bool {
// NOTE: based on the standard library
// Spelling out the cases explicitly optimizes better than
// `_ => false`
#[expect(clippy::match_same_arms)]
match (self, other) {
(Some(lhs), Some(rhs)) => lhs.content_eq(rhs),
(Some(_), None) => false,
(None, Some(_)) => false,
(None, None) => true,
}
}
}
/// Blanket implementation for [oxc_allocator::Box] types
impl<T: ContentEq> ContentEq for oxc_allocator::Box<'_, T> {
#[inline]
fn content_eq(&self, other: &Self) -> bool {
self.as_ref().content_eq(other.as_ref())
}
}
/// Blanket implementation for [oxc_allocator::Vec] types
///
/// # Warning
/// This implementation is slow compared to [PartialEq] for native types which are [Copy] (e.g. `u32`).
/// Prefer comparing the 2 vectors using `==` if they contain such native types (e.g. `Vec<u32>`).
/// <https://godbolt.org/z/54on5sMWc>
impl<T: ContentEq> ContentEq for oxc_allocator::Vec<'_, T> {
#[inline]
fn content_eq(&self, other: &Self) -> bool {
if self.len() == other.len() {
!self.iter().zip(other).any(|(lhs, rhs)| lhs.content_ne(rhs))
} else {
false
}
}
}
mod content_eq_auto_impls {
use super::ContentEq;
macro_rules! content_eq_impl {
($($t:ty)*) => ($(
impl ContentEq for $t {
#[inline]
fn content_eq(&self, other: &$t) -> bool { (*self) == (*other) }
#[inline]
fn content_ne(&self, other: &$t) -> bool { (*self) != (*other) }
}
)*)
}
content_eq_impl! {
char &str
bool isize usize
u8 u16 u32 u64 u128
i8 i16 i32 i64 i128
}
}