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 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168
use bitflags::bitflags;
use nonmax::NonMaxU32;
use oxc_ast_macros::CloneIn;
#[cfg(feature = "serialize")]
use serde::{Serialize, Serializer};
use oxc_index::Idx;
#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
pub struct ReferenceId(NonMaxU32);
impl Idx for ReferenceId {
#[allow(clippy::cast_possible_truncation)]
fn from_usize(idx: usize) -> Self {
assert!(idx < u32::MAX as usize);
// SAFETY: We just checked `idx` is valid for `NonMaxU32`
Self(unsafe { NonMaxU32::new_unchecked(idx as u32) })
}
fn index(self) -> usize {
self.0.get() as usize
}
}
#[cfg(feature = "serialize")]
impl Serialize for ReferenceId {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u32(self.0.get())
}
}
#[cfg(feature = "serialize")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export type ReferenceId = number;
export type ReferenceFlags = {
None: 0,
Read: 0b1,
Write: 0b10,
Type: 0b100,
Value: 0b11
}
"#;
bitflags! {
/// Describes how a symbol is being referenced in the AST.
///
/// There are three general categories of references:
/// 1. Values being referenced as values
/// 2. Types being referenced as types
/// 3. Values being referenced as types
///
/// ## Values
/// Reading a value is indicated by [`Read`], writing a value
/// is indicated by [`Write`]. References can be both a read
/// and a write, such as in this scenario:
///
/// ```js
/// let a = 1;
/// a++;
/// ```
///
/// When a value symbol is used as a type, such as in `typeof a`, it has
/// [`TSTypeQuery`] added to its flags. It is, however, still
/// considered a read. A good rule of thumb is that if a reference has [`Read`]
/// or [`Write`] in its flags, it is referencing a value symbol.
///
/// ## Types
/// Type references are indicated by [`Type`]. These are used primarily in
/// type definitions and signatures. Types can never be re-assigned, so
/// there is no read/write distinction for type references.
///
/// [`Read`]: ReferenceFlags::Read
/// [`Write`]: ReferenceFlags::Write
/// [`TSTypeQuery`]: ReferenceFlags::TSTypeQuery
#[derive(Debug, Default, Clone, Copy, Eq, PartialEq, CloneIn)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct ReferenceFlags: u8 {
const None = 0;
/// A symbol is being read as a Value
const Read = 1 << 0;
/// A symbol is being written to in a Value context.
const Write = 1 << 1;
// Used in type definitions.
const Type = 1 << 2;
// Used in `typeof xx`
const TSTypeQuery = 1 << 3;
/// The symbol being referenced is a value.
///
/// Note that this does not necessarily indicate the reference is used
/// in a value context, since type queries are also flagged as [`Read`]
///
/// [`Read`]: ReferenceFlags::Read
const Value = Self::Read.bits() | Self::Write.bits();
}
}
impl ReferenceFlags {
#[inline]
pub const fn read() -> Self {
Self::Read
}
#[inline]
pub const fn write() -> Self {
Self::Write
}
#[inline]
pub const fn read_write() -> Self {
Self::Value
}
/// The identifier is read from. It may also be written to.
#[inline]
pub const fn is_read(&self) -> bool {
self.intersects(Self::Read)
}
/// The identifier is only read from.
#[inline]
pub const fn is_read_only(&self) -> bool {
self.contains(Self::Read)
}
/// The identifier is written to. It may also be read from.
#[inline]
pub const fn is_write(&self) -> bool {
self.intersects(Self::Write)
}
/// The identifier is only written to. It is not read from in this reference.
#[inline]
pub const fn is_write_only(&self) -> bool {
self.contains(Self::Write)
}
/// The identifier is both read from and written to, e.g `a += 1`.
#[inline]
pub fn is_read_write(&self) -> bool {
self.contains(Self::Read | Self::Write)
}
/// The identifier is used in a type referenced
#[inline]
pub fn is_ts_type_query(&self) -> bool {
self.contains(Self::TSTypeQuery)
}
/// The identifier is used in a type definition.
#[inline]
pub const fn is_type(&self) -> bool {
self.contains(Self::Type)
}
#[inline]
pub const fn is_type_only(self) -> bool {
matches!(self, Self::Type)
}
#[inline]
pub const fn is_value(&self) -> bool {
self.intersects(Self::Value)
}
}