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
use std::num::NonZeroU32;

use bitflags::bitflags;
#[cfg(feature = "serialize")]
use serde::Serialize;

use oxc_index::Idx;

#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
#[cfg_attr(feature = "serialize", derive(Serialize))]
pub struct SymbolId(NonZeroU32);

impl Idx for SymbolId {
    #[allow(clippy::cast_possible_truncation)]
    fn from_usize(idx: usize) -> Self {
        // SAFETY: + 1 is always non-zero.
        #[allow(unsafe_code)]
        unsafe {
            Self(NonZeroU32::new_unchecked(idx as u32 + 1))
        }
    }

    fn index(self) -> usize {
        self.0.get() as usize - 1
    }
}

#[cfg(feature = "serialize")]
#[wasm_bindgen::prelude::wasm_bindgen(typescript_custom_section)]
const TS_APPEND_CONTENT: &'static str = r#"
export type SymbolId = number;
export type SymbolFlags = unknown;
"#;

bitflags! {
    #[derive(Debug, Clone, Copy)]
    #[cfg_attr(feature = "serialize", derive(Serialize))]
    pub struct SymbolFlags: u32 {
        const None                    = 0;
        /// Variable (var) or parameter
        const FunctionScopedVariable  = 1 << 0;
        /// A block-scoped variable (let or const)
        const BlockScopedVariable     = 1 << 1;
        /// A const variable (const)
        const ConstVariable           = 1 << 2;
        /// Is this symbol inside an export declaration
        const Export                  = 1 << 4;
        const Class                   = 1 << 5;
        const CatchVariable           = 1 << 6; // try {} catch(catch_variable) {}
        const Function                = 1 << 7;
        const ImportBinding           = 1 << 8; // Imported ESM binding
        // Type specific symbol flags
        const TypeAlias               = 1 << 9;
        const Interface               = 1 << 10;
        const RegularEnum             = 1 << 11;
        const ConstEnum               = 1 << 12;
        const EnumMember              = 1 << 13;
        const TypeLiteral             = 1 << 14;
        const TypeParameter           = 1 << 15;
        const NameSpaceModule         = 1 << 16;
        const ValueModule             = 1 << 17;
        // In a dts file or there is a declare flag
        const Ambient                 = 1 << 18;

        const Enum = Self::ConstEnum.bits() | Self::RegularEnum.bits();

        const Variable = Self::FunctionScopedVariable.bits() | Self::BlockScopedVariable.bits();
        const Value = Self::Variable.bits() | Self::Class.bits() | Self::Enum.bits() | Self::ValueModule.bits();
        const Type =  Self::Class.bits() | Self::Interface.bits() | Self::Enum.bits() | Self::TypeLiteral.bits() | Self::TypeParameter.bits()  |  Self::TypeAlias.bits();

        /// Variables can be redeclared, but can not redeclare a block-scoped declaration with the
        /// same name, or any other value that is not a variable, e.g. ValueModule or Class
        const FunctionScopedVariableExcludes = Self::Value.bits() - Self::FunctionScopedVariable.bits();

        /// Block-scoped declarations are not allowed to be re-declared
        /// they can not merge with anything in the value space
        const BlockScopedVariableExcludes = Self::Value.bits();

        const ClassExcludes = (Self::Value.bits() | Self::TypeAlias.bits()) & !Self::ValueModule.bits() ;
        const ImportBindingExcludes = Self::ImportBinding.bits();
        // Type specific excludes
        const TypeAliasExcludes = Self::Type.bits();
        const InterfaceExcludes = Self::Type.bits() & !(Self::Interface.bits() | Self::Class.bits());
        const TypeParameterExcludes = Self::Type.bits() & !Self::TypeParameter.bits();
        const ConstEnumExcludes = (Self::Type.bits() | Self::Value.bits()) & !Self::ConstEnum.bits();
        // TODO: include value module in regular enum excludes
        const RegularEnumExcludes = (Self::Value.bits() | Self::Type.bits()) & !(Self::RegularEnum.bits() | Self::ValueModule.bits() );
        const EnumMemberExcludes = Self::EnumMember.bits();

    }
}

impl SymbolFlags {
    pub fn is_variable(&self) -> bool {
        self.intersects(Self::Variable)
    }

    pub fn is_type(&self) -> bool {
        !self.intersects(Self::Value)
    }

    pub fn is_const_variable(&self) -> bool {
        self.contains(Self::ConstVariable)
    }

    pub fn is_function(&self) -> bool {
        self.contains(Self::Function)
    }

    pub fn is_class(&self) -> bool {
        self.contains(Self::Class)
    }

    pub fn is_catch_variable(&self) -> bool {
        self.contains(Self::CatchVariable)
    }

    pub fn is_function_scoped_declaration(&self) -> bool {
        self.contains(Self::FunctionScopedVariable)
    }

    pub fn is_export(&self) -> bool {
        self.contains(Self::Export)
    }

    pub fn is_import_binding(&self) -> bool {
        self.contains(Self::ImportBinding)
    }
}