1#![expect(missing_docs)] use 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 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 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 pub const unsafe fn new_unchecked(idx: u32) -> Self {
51 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 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; const Constructor = 1 << 6;
86 const GetAccessor = 1 << 7;
87 const SetAccessor = 1 << 8;
88 const CatchClause = 1 << 9;
89 const DirectEval = 1 << 10; 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}