oxc_syntax/
symbol.rs

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