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