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
    }
}