oxc_syntax/
scope.rs

1#![expect(missing_docs)] // fixme
2use bitflags::bitflags;
3use nonmax::NonMaxU32;
4use oxc_allocator::{Allocator, CloneIn};
5use oxc_index::Idx;
6#[cfg(feature = "serialize")]
7use serde::{Serialize, Serializer};
8
9use oxc_ast_macros::ast;
10
11#[ast]
12#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
13#[builder(default)]
14#[clone_in(default)]
15#[content_eq(skip)]
16#[estree(skip)]
17pub struct ScopeId(NonMaxU32);
18
19impl<'alloc> CloneIn<'alloc> for ScopeId {
20    type Cloned = Self;
21
22    fn clone_in(&self, _: &'alloc Allocator) -> Self {
23        // `clone_in` should never reach this, because `CloneIn` skips scope_id field
24        unreachable!();
25    }
26
27    #[expect(clippy::inline_always)]
28    #[inline(always)]
29    fn clone_in_with_semantic_ids(&self, _: &'alloc Allocator) -> Self {
30        *self
31    }
32}
33
34impl ScopeId {
35    /// Create `ScopeId` from `u32`.
36    ///
37    /// # Panics
38    /// Panics if `idx` is `u32::MAX`.
39    pub const fn new(idx: u32) -> Self {
40        if let Some(idx) = NonMaxU32::new(idx) {
41            return Self(idx);
42        }
43        panic!();
44    }
45
46    /// Create `ScopeId` from `u32` unchecked.
47    ///
48    /// # SAFETY
49    /// `idx` must not be `u32::MAX`.
50    pub const unsafe fn new_unchecked(idx: u32) -> Self {
51        // SAFETY: Caller must ensure `idx` is not `u32::MAX`
52        unsafe { Self(NonMaxU32::new_unchecked(idx)) }
53    }
54}
55
56impl Idx for ScopeId {
57    #[expect(clippy::cast_possible_truncation)]
58    fn from_usize(idx: usize) -> Self {
59        assert!(idx < u32::MAX as usize);
60        // SAFETY: We just checked `idx` is a legal value for `NonMaxU32`
61        Self(unsafe { NonMaxU32::new_unchecked(idx as u32) })
62    }
63
64    fn index(self) -> usize {
65        self.0.get() as usize
66    }
67}
68
69#[cfg(feature = "serialize")]
70impl Serialize for ScopeId {
71    fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
72        serializer.serialize_u32(self.0.get())
73    }
74}
75
76bitflags! {
77    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
78    pub struct ScopeFlags: u16 {
79        const StrictMode       = 1 << 0;
80        const Top              = 1 << 1;
81        const Function         = 1 << 2;
82        const Arrow            = 1 << 3;
83        const ClassStaticBlock = 1 << 4;
84        const TsModuleBlock    = 1 << 5; // `declare namespace`
85        const Constructor      = 1 << 6;
86        const GetAccessor      = 1 << 7;
87        const SetAccessor      = 1 << 8;
88        const CatchClause      = 1 << 9;
89        const DirectEval       = 1 << 10; // <https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval#direct_and_indirect_eval>
90        const TsConditional    = 1 << 11;
91        const Var = Self::Top.bits() | Self::Function.bits() | Self::ClassStaticBlock.bits() | Self::TsModuleBlock.bits();
92    }
93}
94
95impl ScopeFlags {
96    #[must_use]
97    #[inline]
98    pub fn with_strict_mode(self, yes: bool) -> Self {
99        if yes { self | Self::StrictMode } else { self }
100    }
101
102    #[inline]
103    pub fn is_strict_mode(&self) -> bool {
104        self.contains(Self::StrictMode)
105    }
106
107    #[inline]
108    pub fn is_block(&self) -> bool {
109        self.is_empty() || *self == Self::StrictMode
110    }
111
112    #[inline]
113    pub fn is_top(&self) -> bool {
114        self.contains(Self::Top)
115    }
116
117    #[inline]
118    pub fn is_function(&self) -> bool {
119        self.contains(Self::Function)
120    }
121
122    #[inline]
123    pub fn is_arrow(&self) -> bool {
124        self.contains(Self::Arrow)
125    }
126
127    #[inline]
128    pub fn is_constructor(&self) -> bool {
129        self.contains(Self::Constructor)
130    }
131
132    #[inline]
133    pub fn is_class_static_block(&self) -> bool {
134        self.contains(Self::ClassStaticBlock)
135    }
136
137    #[inline]
138    pub fn is_ts_module_block(&self) -> bool {
139        self.contains(Self::TsModuleBlock)
140    }
141
142    #[inline]
143    pub fn is_var(&self) -> bool {
144        self.intersects(Self::Var)
145    }
146
147    #[inline]
148    pub fn is_set_accessor(&self) -> bool {
149        self.contains(Self::SetAccessor)
150    }
151
152    #[inline]
153    pub fn is_set_or_get_accessor(&self) -> bool {
154        self.intersects(Self::SetAccessor | Self::GetAccessor)
155    }
156
157    #[inline]
158    pub fn is_catch_clause(&self) -> bool {
159        self.contains(Self::CatchClause)
160    }
161
162    pub fn is_ts_conditional(&self) -> bool {
163        self.contains(Self::TsConditional)
164    }
165
166    #[inline]
167    pub fn contains_direct_eval(&self) -> bool {
168        self.contains(Self::DirectEval)
169    }
170}