@@ -5,6 +5,8 @@ use std::{
55 mem,
66} ;
77
8+ use num_bigint:: BigInt ;
9+
810use rustc_hash:: FxHashMap ;
911
1012use oxc_allocator:: Address ;
@@ -27,6 +29,7 @@ use oxc_syntax::{
2729#[ cfg( feature = "linter" ) ]
2830use crate :: jsdoc:: JSDocBuilder ;
2931use crate :: {
32+ const_enum:: { ConstEnumTable , ConstEnumMemberValue , ConstEnumMemberInfo , ConstEnumInfo } ,
3033 Semantic ,
3134 binder:: { Binder , ModuleInstanceState } ,
3235 checker,
@@ -108,6 +111,9 @@ pub struct SemanticBuilder<'a> {
108111
109112 pub ( crate ) class_table_builder : ClassTableBuilder < ' a > ,
110113
114+ /// Table for storing const enum information
115+ pub ( crate ) const_enum_table : ConstEnumTable < ' a > ,
116+
111117 #[ cfg( feature = "cfg" ) ]
112118 ast_node_records : Vec < NodeId > ,
113119}
@@ -154,6 +160,7 @@ impl<'a> SemanticBuilder<'a> {
154160 #[ cfg( not( feature = "cfg" ) ) ]
155161 cfg : ( ) ,
156162 class_table_builder : ClassTableBuilder :: new ( ) ,
163+ const_enum_table : ConstEnumTable :: new ( ) ,
157164 #[ cfg( feature = "cfg" ) ]
158165 ast_node_records : Vec :: new ( ) ,
159166 }
@@ -296,6 +303,7 @@ impl<'a> SemanticBuilder<'a> {
296303 nodes : self . nodes ,
297304 scoping : self . scoping ,
298305 classes : self . class_table_builder . build ( ) ,
306+ const_enums : self . const_enum_table ,
299307 #[ cfg( feature = "linter" ) ]
300308 jsdoc,
301309 unused_labels : self . unused_labels . labels ,
@@ -2146,7 +2154,11 @@ impl<'a> SemanticBuilder<'a> {
21462154 }
21472155 AstKind :: TSEnumDeclaration ( enum_declaration) => {
21482156 enum_declaration. bind ( self ) ;
2149- // TODO: const enum?
2157+
2158+ // Process const enums
2159+ if enum_declaration. r#const {
2160+ self . process_const_enum ( enum_declaration) ;
2161+ }
21502162 }
21512163 AstKind :: TSEnumMember ( enum_member) => {
21522164 enum_member. bind ( self ) ;
@@ -2232,4 +2244,209 @@ impl<'a> SemanticBuilder<'a> {
22322244 mem:: take ( & mut self . current_reference_flags )
22332245 }
22342246 }
2247+
2248+ /// Process a const enum declaration and evaluate its members
2249+ fn process_const_enum ( & mut self , enum_declaration : & TSEnumDeclaration < ' a > ) {
2250+ // Get the symbol ID for this enum
2251+ let symbol_id = enum_declaration. id . symbol_id . get ( ) . expect ( "enum should have symbol ID" ) ;
2252+
2253+ let mut members = std:: collections:: HashMap :: new ( ) ;
2254+ let mut current_value: f64 = -1.0 ; // Start at -1, first auto-increment will make it 0
2255+
2256+ for member in & enum_declaration. body . members {
2257+ let member_name = match & member. id {
2258+ TSEnumMemberName :: Identifier ( ident) => ident. name . as_str ( ) ,
2259+ TSEnumMemberName :: String ( string) => string. value . as_str ( ) ,
2260+ TSEnumMemberName :: ComputedString ( string) => string. value . as_str ( ) ,
2261+ TSEnumMemberName :: ComputedTemplateString ( template) => {
2262+ // For computed template strings, we need to evaluate them
2263+ if template. expressions . is_empty ( ) {
2264+ if let Some ( quasi) = template. quasis . first ( ) {
2265+ quasi. value . raw . as_str ( )
2266+ } else {
2267+ continue ;
2268+ }
2269+ } else {
2270+ // Skip template literals with expressions for now
2271+ continue ;
2272+ }
2273+ }
2274+ } ;
2275+
2276+ let value = if let Some ( initializer) = & member. initializer {
2277+ // Evaluate the initializer expression
2278+ let mut visited = std:: vec:: Vec :: new ( ) ;
2279+ if let Some ( evaluated_value) = self . evaluate_const_enum_member ( initializer, Some ( symbol_id) , & mut visited) {
2280+ // Update current_value based on the evaluated value
2281+ match & evaluated_value {
2282+ ConstEnumMemberValue :: Number ( n) => current_value = * n,
2283+ _ => { } // Don't change current_value for non-numeric values
2284+ }
2285+ evaluated_value
2286+ } else {
2287+ // If evaluation fails, fall back to current_value + 1
2288+ current_value += 1.0 ;
2289+ ConstEnumMemberValue :: Number ( current_value)
2290+ }
2291+ } else {
2292+ // Auto-increment the value
2293+ current_value += 1.0 ;
2294+ ConstEnumMemberValue :: Number ( current_value)
2295+ } ;
2296+
2297+ let member_info = ConstEnumMemberInfo {
2298+ name : member_name,
2299+ value,
2300+ span : member. span ,
2301+ has_initializer : member. initializer . is_some ( ) ,
2302+ } ;
2303+
2304+ members. insert ( member_name, member_info) ;
2305+ }
2306+
2307+ let enum_info = ConstEnumInfo {
2308+ symbol_id,
2309+ members,
2310+ span : enum_declaration. span ,
2311+ } ;
2312+
2313+ self . const_enum_table . add_enum ( symbol_id, enum_info) ;
2314+ }
2315+
2316+ /// Evaluate a const enum member's value with improved JavaScript semantics
2317+ fn evaluate_const_enum_member (
2318+ & self ,
2319+ expression : & Expression < ' a > ,
2320+ current_enum : Option < SymbolId > ,
2321+ _visited : & mut std:: vec:: Vec < & ' a str > ,
2322+ ) -> Option < ConstEnumMemberValue < ' a > > {
2323+ match expression {
2324+ Expression :: StringLiteral ( string) => Some ( ConstEnumMemberValue :: String ( string. value . as_str ( ) ) ) ,
2325+ Expression :: NumericLiteral ( number) => Some ( ConstEnumMemberValue :: Number ( number. value ) ) ,
2326+ Expression :: BooleanLiteral ( boolean) => Some ( ConstEnumMemberValue :: Boolean ( boolean. value ) ) ,
2327+ Expression :: BigIntLiteral ( bigint) => {
2328+ bigint. value . parse :: < BigInt > ( ) . ok ( ) . map ( ConstEnumMemberValue :: BigInt )
2329+ }
2330+ Expression :: UnaryExpression ( unary) => {
2331+ if let Some ( argument) = self . evaluate_const_enum_member ( & unary. argument , current_enum, _visited) {
2332+ self . evaluate_unary_operation ( unary, argument)
2333+ } else {
2334+ None
2335+ }
2336+ }
2337+ Expression :: BinaryExpression ( binary) => {
2338+ if let ( Some ( left) , Some ( right) ) = (
2339+ self . evaluate_const_enum_member ( & binary. left , current_enum, _visited) ,
2340+ self . evaluate_const_enum_member ( & binary. right , current_enum, _visited) ,
2341+ ) {
2342+ self . evaluate_binary_operation ( binary, left, right)
2343+ } else {
2344+ None
2345+ }
2346+ }
2347+ Expression :: Identifier ( ident) => {
2348+ // Try to resolve this as a reference to another const enum member
2349+ let name = ident. name . as_str ( ) ;
2350+
2351+ if let Some ( current_enum_id) = current_enum {
2352+ if let Some ( enum_info) = self . const_enum_table . get_enum ( current_enum_id) {
2353+ if let Some ( member_info) = enum_info. members . get ( name) {
2354+ return Some ( member_info. value . clone ( ) ) ;
2355+ }
2356+ }
2357+ }
2358+ None
2359+ }
2360+ _ => None ,
2361+ }
2362+ }
2363+
2364+ /// Evaluate unary operations with proper JavaScript semantics
2365+ fn evaluate_unary_operation (
2366+ & self ,
2367+ unary : & UnaryExpression < ' a > ,
2368+ argument : ConstEnumMemberValue < ' a > ,
2369+ ) -> Option < ConstEnumMemberValue < ' a > > {
2370+ match unary. operator {
2371+ UnaryOperator :: UnaryNegation => {
2372+ match argument {
2373+ ConstEnumMemberValue :: Number ( n) => Some ( ConstEnumMemberValue :: Number ( -n) ) ,
2374+ _ => None ,
2375+ }
2376+ }
2377+ UnaryOperator :: UnaryPlus => {
2378+ match argument {
2379+ ConstEnumMemberValue :: Number ( n) => Some ( ConstEnumMemberValue :: Number ( n) ) ,
2380+ _ => None ,
2381+ }
2382+ }
2383+ UnaryOperator :: LogicalNot => {
2384+ match argument {
2385+ ConstEnumMemberValue :: Boolean ( b) => Some ( ConstEnumMemberValue :: Boolean ( !b) ) ,
2386+ _ => None ,
2387+ }
2388+ }
2389+ _ => None ,
2390+ }
2391+ }
2392+
2393+ /// Evaluate binary operations with proper JavaScript semantics
2394+ fn evaluate_binary_operation (
2395+ & self ,
2396+ binary : & BinaryExpression < ' a > ,
2397+ left : ConstEnumMemberValue < ' a > ,
2398+ right : ConstEnumMemberValue < ' a > ,
2399+ ) -> Option < ConstEnumMemberValue < ' a > > {
2400+ match binary. operator {
2401+ BinaryOperator :: Addition => {
2402+ match ( & left, & right) {
2403+ ( ConstEnumMemberValue :: Number ( l) , ConstEnumMemberValue :: Number ( r) ) => {
2404+ Some ( ConstEnumMemberValue :: Number ( l + r) )
2405+ }
2406+ _ => None ,
2407+ }
2408+ }
2409+ BinaryOperator :: Subtraction => {
2410+ match ( & left, & right) {
2411+ ( ConstEnumMemberValue :: Number ( l) , ConstEnumMemberValue :: Number ( r) ) => {
2412+ Some ( ConstEnumMemberValue :: Number ( l - r) )
2413+ }
2414+ _ => None ,
2415+ }
2416+ }
2417+ BinaryOperator :: Multiplication => {
2418+ match ( & left, & right) {
2419+ ( ConstEnumMemberValue :: Number ( l) , ConstEnumMemberValue :: Number ( r) ) => {
2420+ Some ( ConstEnumMemberValue :: Number ( l * r) )
2421+ }
2422+ _ => None ,
2423+ }
2424+ }
2425+ BinaryOperator :: Division => {
2426+ match ( & left, & right) {
2427+ ( ConstEnumMemberValue :: Number ( l) , ConstEnumMemberValue :: Number ( r) ) => {
2428+ if * r == 0.0 { None } else { Some ( ConstEnumMemberValue :: Number ( l / r) ) }
2429+ }
2430+ _ => None ,
2431+ }
2432+ }
2433+ BinaryOperator :: ShiftLeft => {
2434+ match ( & left, & right) {
2435+ ( ConstEnumMemberValue :: Number ( l) , ConstEnumMemberValue :: Number ( r) ) => {
2436+ Some ( ConstEnumMemberValue :: Number ( ( ( * l as i64 ) << ( * r as i64 ) ) as f64 ) )
2437+ }
2438+ _ => None ,
2439+ }
2440+ }
2441+ BinaryOperator :: BitwiseOR => {
2442+ match ( & left, & right) {
2443+ ( ConstEnumMemberValue :: Number ( l) , ConstEnumMemberValue :: Number ( r) ) => {
2444+ Some ( ConstEnumMemberValue :: Number ( ( ( * l as i64 ) | ( * r as i64 ) ) as f64 ) )
2445+ }
2446+ _ => None ,
2447+ }
2448+ }
2449+ _ => None ,
2450+ }
2451+ }
22352452}
0 commit comments