Skip to main content

oxc_syntax/
symbol.rs

1use bitflags::bitflags;
2use oxc_allocator::{Allocator, CloneIn, CloneInSemanticIds};
3use oxc_index::define_nonmax_u32_index_type;
4#[cfg(feature = "serialize")]
5use serde::Serialize;
6
7use oxc_ast_macros::ast;
8
9use crate::semantic_id::SemanticId;
10
11define_nonmax_u32_index_type! {
12    #[ast]
13    #[builder(default)]
14    #[clone_in(semantic_id)]
15    #[content_eq(skip)]
16    #[estree(skip)]
17    pub struct SymbolId;
18}
19
20impl<'alloc> CloneIn<'alloc> for SymbolId {
21    type Cloned = Self;
22
23    #[expect(clippy::inline_always)]
24    #[inline(always)] // Because this method only delegates
25    fn clone_in_impl(&self, with_semantic_ids: CloneInSemanticIds, _: &'alloc Allocator) -> Self {
26        self.clone_id(with_semantic_ids)
27    }
28}
29
30impl SemanticId for SymbolId {}
31
32define_nonmax_u32_index_type! {
33    pub struct RedeclarationId;
34}
35
36bitflags! {
37    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
38    #[cfg_attr(feature = "serialize", derive(Serialize))]
39    pub struct SymbolFlags: u32 {
40        const None                    = 0;
41        /// Variable (var) or parameter
42        const FunctionScopedVariable  = 1 << 0;
43        /// A block-scoped variable (let or const)
44        const BlockScopedVariable     = 1 << 1;
45        /// A const variable (const)
46        const ConstVariable           = 1 << 2;
47        const Class                   = 1 << 3;
48        /// `try {} catch(catch_variable) {}`
49        const CatchVariable           = 1 << 4;
50        /// A function declaration or expression
51        const Function                = 1 << 5;
52        /// Imported ESM binding
53        const Import                  = 1 << 6;
54        /// Imported ESM type-only binding
55        const TypeImport              = 1 << 7;
56        // Type specific symbol flags
57        const TypeAlias               = 1 << 8;
58        const Interface               = 1 << 9;
59        const RegularEnum             = 1 << 10;
60        const ConstEnum               = 1 << 11;
61        const EnumMember              = 1 << 12;
62        const TypeParameter           = 1 << 13;
63        /// Uninstantiated module
64        const NamespaceModule         = 1 << 14;
65        /// Instantiated module
66        const ValueModule             = 1 << 15;
67        /// Declared with `declare` modifier, like `declare function x() {}`.
68        //
69        // This flag is not part of TypeScript's `SymbolFlags`, it comes from TypeScript's `NodeFlags`. We introduced it into
70        // here because `NodeFlags` is incomplete and we only can access to `NodeFlags` in the Semantic, but we also need to
71        // access it in the Transformer.
72        // https://github.com/microsoft/TypeScript/blob/15392346d05045742e653eab5c87538ff2a3c863/src/compiler/types.ts#L819-L820
73        const Ambient                 = 1 << 16;
74
75        // The following are not part of TypeScript's `SymbolFlags`. They record the syntactic kind
76        // of a function binding so the semantic builder's redeclaration checks can consult them
77        // (on `symbol_flags`, and per-declaration via `Redeclaration::flags`) instead of reading the
78        // declaration's AST node. The transformer keeps them in sync when it rewrites functions.
79
80        /// A named function expression, e.g. `n` in `(function n() {})`.
81        const FunctionExpression       = 1 << 17;
82        /// An `async` and/or generator function.
83        const AsyncOrGeneratorFunction = 1 << 18;
84
85        const Enum = Self::ConstEnum.bits() | Self::RegularEnum.bits();
86        const Variable = Self::FunctionScopedVariable.bits() | Self::BlockScopedVariable.bits();
87
88        const BlockScoped = Self::BlockScopedVariable.bits() | Self::Enum.bits() | Self::Class.bits();
89
90        const Value = Self::Variable.bits() | Self::Class.bits() | Self::Function.bits() | Self::Enum.bits() | Self::EnumMember.bits() | Self::ValueModule.bits();
91        const Type = Self::Class.bits() | Self::Interface.bits() | Self::Enum.bits() | Self::EnumMember.bits() | Self::TypeParameter.bits()  |  Self::TypeAlias.bits();
92        const Namespace = Self::ValueModule.bits() | Self::NamespaceModule.bits() | Self::Enum.bits();
93
94
95        /// Variables can be redeclared, but can not redeclare a block-scoped declaration with the
96        /// same name, or any other value that is not a variable, e.g. ValueModule or Class
97        const FunctionScopedVariableExcludes = Self::Value.bits() - Self::FunctionScopedVariable.bits() - Self::Function.bits();
98
99        /// Block-scoped declarations are not allowed to be re-declared
100        /// they can not merge with anything in the value space
101        const BlockScopedVariableExcludes = Self::Value.bits();
102        const FunctionExcludes = Self::Value.bits() & !(Self::Function.bits() | Self::ValueModule.bits() | Self::Class.bits());
103        const ClassExcludes = (Self::Value.bits() | Self::Type.bits()) & !(Self::ValueModule.bits() | Self::Function.bits() | Self::Interface.bits());
104
105        const ImportBindingExcludes = Self::Import.bits() | Self::TypeImport.bits();
106        // Type specific excludes
107        const TypeAliasExcludes = Self::Type.bits();
108        const InterfaceExcludes = Self::Type.bits() & !(Self::Interface.bits() | Self::Class.bits());
109        const TypeParameterExcludes = Self::Type.bits() & !Self::TypeParameter.bits();
110        const ConstEnumExcludes = (Self::Type.bits() | Self::Value.bits()) & !Self::ConstEnum.bits();
111        const ValueModuleExcludes = Self::Value.bits() & !(Self::Function.bits() | Self::Class.bits() | Self::RegularEnum.bits() | Self::ValueModule.bits());
112        const NamespaceModuleExcludes = 0;
113        // TODO: include value module in regular enum excludes
114        const RegularEnumExcludes = (Self::Value.bits() | Self::Type.bits()) & !(Self::RegularEnum.bits() | Self::ValueModule.bits() );
115        const EnumMemberExcludes = Self::EnumMember.bits();
116
117    }
118}
119
120impl SymbolFlags {
121    #[inline]
122    pub fn is_variable(self) -> bool {
123        self.intersects(Self::Variable)
124    }
125
126    #[inline]
127    pub fn is_type_parameter(self) -> bool {
128        self.contains(Self::TypeParameter)
129    }
130
131    /// If true, then the symbol is a type, such as a TypeAlias, Interface, or Enum
132    #[inline]
133    pub fn is_type(self) -> bool {
134        self.intersects((Self::TypeImport | Self::Type) - Self::Value)
135    }
136
137    /// If true, then the symbol is a value, such as a Variable, Function, or Class
138    #[inline]
139    pub fn is_value(self) -> bool {
140        self.intersects(Self::Value | Self::Import)
141    }
142
143    #[inline]
144    pub fn is_const_variable(self) -> bool {
145        self.contains(Self::ConstVariable)
146    }
147
148    /// Returns `true` if this symbol is a function declaration or expression.
149    #[inline]
150    pub fn is_function(self) -> bool {
151        self.contains(Self::Function)
152    }
153
154    #[inline]
155    pub fn is_class(self) -> bool {
156        self.contains(Self::Class)
157    }
158
159    /// Returns `true` if this symbol is a block-scoped lexical declaration
160    /// (`let`/`const`, `class`, or `enum`), i.e. a binding that has a Temporal
161    /// Dead Zone.
162    #[inline]
163    pub fn is_block_scoped(self) -> bool {
164        self.intersects(Self::BlockScoped)
165    }
166
167    #[inline]
168    pub fn is_interface(self) -> bool {
169        self.contains(Self::Interface)
170    }
171
172    #[inline]
173    pub fn is_type_alias(self) -> bool {
174        self.contains(Self::TypeAlias)
175    }
176
177    #[inline]
178    pub fn is_enum(self) -> bool {
179        self.intersects(Self::Enum)
180    }
181
182    #[inline]
183    pub fn is_const_enum(self) -> bool {
184        self.intersects(Self::ConstEnum)
185    }
186
187    #[inline]
188    pub fn is_enum_member(self) -> bool {
189        self.contains(Self::EnumMember)
190    }
191
192    #[inline]
193    pub fn is_catch_variable(self) -> bool {
194        self.contains(Self::CatchVariable)
195    }
196
197    #[inline]
198    pub fn is_function_scoped_declaration(self) -> bool {
199        self.contains(Self::FunctionScopedVariable)
200    }
201
202    #[inline]
203    pub fn is_import(self) -> bool {
204        self.intersects(Self::Import | Self::TypeImport)
205    }
206
207    #[inline]
208    pub fn is_type_import(self) -> bool {
209        self.contains(Self::TypeImport)
210    }
211
212    #[inline]
213    pub fn is_ambient(self) -> bool {
214        self.contains(Self::Ambient)
215    }
216
217    #[inline]
218    pub fn is_namespace_module(self) -> bool {
219        self.contains(Self::NamespaceModule)
220    }
221
222    #[inline]
223    pub fn is_value_module(self) -> bool {
224        self.contains(Self::ValueModule)
225    }
226
227    /// If true, then the symbol can be referenced by a type reference
228    #[inline]
229    pub fn can_be_referenced_by_type(self) -> bool {
230        self.intersects(Self::Type | Self::TypeImport | Self::Import | Self::Namespace)
231    }
232
233    /// If true, then the symbol can be referenced by a value reference
234    #[inline]
235    pub fn can_be_referenced_by_value(self) -> bool {
236        self.is_value()
237    }
238
239    /// If true, then the symbol can be referenced by a value_as_type reference
240    #[inline]
241    pub fn can_be_referenced_by_value_as_type(self) -> bool {
242        self.intersects(Self::Value | Self::Import | Self::Function | Self::TypeImport)
243    }
244
245    /// If true, then the symbol can be referenced as a namespace.
246    ///
247    /// Used for the left side of qualified names (e.g., `NS` in `NS.Type`).
248    /// Modules, namespaces, enums, and namespace imports can have member access.
249    #[inline]
250    pub fn can_be_referenced_as_namespace(self) -> bool {
251        self.intersects(Self::Namespace | Self::Import | Self::TypeImport)
252    }
253}