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