diff --git a/crates/oxc_allocator/src/hash_map.rs b/crates/oxc_allocator/src/hash_map.rs index fae05db123482..3b2b342547fb4 100644 --- a/crates/oxc_allocator/src/hash_map.rs +++ b/crates/oxc_allocator/src/hash_map.rs @@ -8,7 +8,7 @@ #![expect(clippy::inline_always)] use std::{ - hash::Hash, + hash::{BuildHasher, Hash}, mem::ManuallyDrop, ops::{Deref, DerefMut}, }; @@ -28,14 +28,14 @@ pub use hashbrown::{ use crate::Allocator; -type FxHashMap<'alloc, K, V> = hashbrown::HashMap; +type InnerHashMap<'alloc, K, V, H> = hashbrown::HashMap; -/// A hash map without `Drop`, that uses [`FxHasher`] to hash keys, and stores data in arena allocator. +/// A hash map without `Drop`, that stores data in arena allocator. /// /// Just a thin wrapper around [`hashbrown::HashMap`], which disables the `Drop` implementation. /// -/// All APIs are the same, except create a [`HashMap`] with -/// either [`new_in`](HashMap::new_in) or [`with_capacity_in`](HashMap::with_capacity_in). +/// All APIs are the same, except create a [`HashMapImpl`] with +/// either [`new_in`](HashMapImpl::new_in) or [`with_capacity_in`](HashMapImpl::with_capacity_in). /// /// # No `Drop`s /// @@ -45,26 +45,42 @@ type FxHashMap<'alloc, K, V> = hashbrown::HashMap( + pub(crate) ManuallyDrop>, +); + +impl std::fmt::Debug for HashMapImpl<'_, K, V, H> +where + K: std::fmt::Debug, + V: std::fmt::Debug, +{ + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_map().entries(self.0.iter()).finish() + } +} + +/// A hash map without `Drop`, that uses [`FxHasher`] to hash keys, and stores data in arena allocator. +/// +/// This is a type alias for [`HashMapImpl`] with [`FxBuildHasher`]. /// /// [`FxHasher`]: rustc_hash::FxHasher -#[derive(Debug)] -pub struct HashMap<'alloc, K, V>(pub(crate) ManuallyDrop>); +pub type HashMap<'alloc, K, V> = HashMapImpl<'alloc, K, V, FxBuildHasher>; -/// SAFETY: Even though `Bump` is not `Sync`, we can make `HashMap` `Sync` if both `K` and `V` +/// SAFETY: Even though `Bump` is not `Sync`, we can make `HashMapImpl` `Sync` if both `K` and `V` /// are `Sync` because: /// -/// 1. No public methods allow access to the `&Bump` that `HashMap` contains (in `hashbrown::HashMap`), -/// so user cannot illegally obtain 2 `&Bump`s on different threads via `HashMap`. +/// 1. No public methods allow access to the `&Bump` that `HashMapImpl` contains (in `hashbrown::HashMap`), +/// so user cannot illegally obtain 2 `&Bump`s on different threads via `HashMapImpl`. /// /// 2. All internal methods which access the `&Bump` take a `&mut self`. -/// `&mut HashMap` cannot be transferred across threads, and nor can an owned `HashMap` -/// (`HashMap` is not `Send`). -/// Therefore these methods taking `&mut self` can be sure they're not operating on a `HashMap` +/// `&mut HashMapImpl` cannot be transferred across threads, and nor can an owned `HashMapImpl` +/// (`HashMapImpl` is not `Send`). +/// Therefore these methods taking `&mut self` can be sure they're not operating on a `HashMapImpl` /// which has been moved across threads. /// -/// Note: `HashMap` CANNOT be `Send`, even if `K` and `V` are `Send`, because that would allow 2 `HashMap`s +/// Note: `HashMapImpl` CANNOT be `Send`, even if `K` and `V` are `Send`, because that would allow 2 `HashMapImpl`s /// on different threads to both allocate into same arena simultaneously. `Bump` is not thread-safe, /// and this would be undefined behavior. /// @@ -72,40 +88,40 @@ pub struct HashMap<'alloc, K, V>(pub(crate) ManuallyDrop /// /// This is not actually fully sound. There are 2 holes I (@overlookmotel) am aware of: /// -/// 1. `allocator` method, which does allow access to the `&Bump` that `HashMap` contains. +/// 1. `allocator` method, which does allow access to the `&Bump` that `HashMapImpl` contains. /// 2. `Clone` impl on `hashbrown::HashMap`, which may perform allocations in the arena, given only a /// `&self` reference. /// -/// [`HashMap::allocator`] prevents accidental access to the underlying method of `hashbrown::HashMap`, -/// and `clone` called on a `&HashMap` clones the `&HashMap` reference, not the `HashMap` itself (harmless). +/// [`HashMapImpl::allocator`] prevents accidental access to the underlying method of `hashbrown::HashMap`, +/// and `clone` called on a `&HashMapImpl` clones the `&HashMapImpl` reference, not the `HashMapImpl` itself (harmless). /// But both can be accessed via explicit `Deref` (`hash_map.deref().allocator()` or `hash_map.deref().clone()`), /// so we don't have complete soundness. /// -/// To close these holes we need to remove `Deref` and `DerefMut` impls on `HashMap`, and instead add -/// methods to `HashMap` itself which pass on calls to the inner `hashbrown::HashMap`. +/// To close these holes we need to remove `Deref` and `DerefMut` impls on `HashMapImpl`, and instead add +/// methods to `HashMapImpl` itself which pass on calls to the inner `hashbrown::HashMap`. /// /// TODO: Fix these holes. /// TODO: Remove any other methods that currently allow performing allocations with only a `&self` reference. -unsafe impl Sync for HashMap<'_, K, V> {} +unsafe impl Sync for HashMapImpl<'_, K, V, H> {} // TODO: `IntoIter`, `Drain`, and other consuming iterators provided by `hashbrown` are `Drop`. // Wrap them in `ManuallyDrop` to prevent that. -impl<'alloc, K, V> HashMap<'alloc, K, V> { +impl<'alloc, K, V, H: BuildHasher + Default> HashMapImpl<'alloc, K, V, H> { /// Const assertions that `K` and `V` are not `Drop`. - /// Must be referenced in all methods which create a `HashMap`. + /// Must be referenced in all methods which create a `HashMapImpl`. const ASSERT_K_AND_V_ARE_NOT_DROP: () = { assert!( !std::mem::needs_drop::(), - "Cannot create a HashMap where K is a Drop type" + "Cannot create a HashMapImpl where K is a Drop type" ); assert!( !std::mem::needs_drop::(), - "Cannot create a HashMap where V is a Drop type" + "Cannot create a HashMapImpl where V is a Drop type" ); }; - /// Creates an empty [`HashMap`]. It will be allocated with the given allocator. + /// Creates an empty [`HashMapImpl`]. It will be allocated with the given allocator. /// /// The hash map is initially created with a capacity of 0, so it will not allocate /// until it is first inserted into. @@ -113,11 +129,11 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> { pub fn new_in(allocator: &'alloc Allocator) -> Self { const { Self::ASSERT_K_AND_V_ARE_NOT_DROP }; - let inner = FxHashMap::with_hasher_in(FxBuildHasher, allocator.bump()); + let inner = InnerHashMap::with_hasher_in(H::default(), allocator.bump()); Self(ManuallyDrop::new(inner)) } - /// Creates an empty [`HashMap`] with the specified capacity. It will be allocated with the given allocator. + /// Creates an empty [`HashMapImpl`] with the specified capacity. It will be allocated with the given allocator. /// /// The hash map will be able to hold at least capacity elements without reallocating. /// If capacity is 0, the hash map will not allocate. @@ -126,11 +142,11 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> { const { Self::ASSERT_K_AND_V_ARE_NOT_DROP }; let inner = - FxHashMap::with_capacity_and_hasher_in(capacity, FxBuildHasher, allocator.bump()); + InnerHashMap::with_capacity_and_hasher_in(capacity, H::default(), allocator.bump()); Self(ManuallyDrop::new(inner)) } - /// Create a new [`HashMap`] whose elements are taken from an iterator and + /// Create a new [`HashMapImpl`] whose elements are taken from an iterator and /// allocated in the given `allocator`. /// /// This is behaviorally identical to [`FromIterator::from_iter`]. @@ -150,13 +166,14 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> { // This follows `hashbrown::HashMap`'s `from_iter` implementation. // // This is a trade-off: - // * Negative: If lower bound is too low, the `HashMap` may have to grow and reallocate during `for_each` loop. + // * Negative: If lower bound is too low, the `HashMapImpl` may have to grow and reallocate during `for_each` loop. // * Positive: Avoids potential large over-allocation for iterators where upper bound may be a large over-estimate // e.g. filter iterators. let capacity = iter.size_hint().0; - let map = FxHashMap::with_capacity_and_hasher_in(capacity, FxBuildHasher, allocator.bump()); + let map = + InnerHashMap::with_capacity_and_hasher_in(capacity, H::default(), allocator.bump()); // Wrap in `ManuallyDrop` *before* calling `for_each`, so compiler doesn't insert unnecessary code - // to drop the `FxHashMap` in case of a panic in iterator's `next` method + // to drop the `InnerHashMap` in case of a panic in iterator's `next` method let mut map = ManuallyDrop::new(map); iter.for_each(|(k, v)| { @@ -165,7 +182,9 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> { Self(map) } +} +impl<'alloc, K, V, H: BuildHasher> HashMapImpl<'alloc, K, V, H> { /// Creates a consuming iterator visiting all the keys in arbitrary order. /// /// The map cannot be used after calling this. The iterator element type is `K`. @@ -186,7 +205,7 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> { /// Calling this method produces a compile-time panic. /// - /// This method would be unsound, because [`HashMap`] is `Sync`, and the underlying allocator + /// This method would be unsound, because [`HashMapImpl`] is `Sync`, and the underlying allocator /// (`Bump`) is not `Sync`. /// /// This method exists only to block access as much as possible to the underlying @@ -203,8 +222,8 @@ impl<'alloc, K, V> HashMap<'alloc, K, V> { } // Provide access to all `hashbrown::HashMap`'s methods via deref -impl<'alloc, K, V> Deref for HashMap<'alloc, K, V> { - type Target = FxHashMap<'alloc, K, V>; +impl<'alloc, K, V, H: BuildHasher> Deref for HashMapImpl<'alloc, K, V, H> { + type Target = InnerHashMap<'alloc, K, V, H>; #[inline] fn deref(&self) -> &Self::Target { @@ -212,14 +231,14 @@ impl<'alloc, K, V> Deref for HashMap<'alloc, K, V> { } } -impl<'alloc, K, V> DerefMut for HashMap<'alloc, K, V> { +impl<'alloc, K, V, H: BuildHasher> DerefMut for HashMapImpl<'alloc, K, V, H> { #[inline] - fn deref_mut(&mut self) -> &mut FxHashMap<'alloc, K, V> { + fn deref_mut(&mut self) -> &mut InnerHashMap<'alloc, K, V, H> { &mut self.0 } } -impl<'alloc, K, V> IntoIterator for HashMap<'alloc, K, V> { +impl<'alloc, K, V, H: BuildHasher> IntoIterator for HashMapImpl<'alloc, K, V, H> { type IntoIter = IntoIter; type Item = (K, V); @@ -236,38 +255,38 @@ impl<'alloc, K, V> IntoIterator for HashMap<'alloc, K, V> { } } -impl<'alloc, 'i, K, V> IntoIterator for &'i HashMap<'alloc, K, V> { - type IntoIter = <&'i FxHashMap<'alloc, K, V> as IntoIterator>::IntoIter; +impl<'alloc, 'i, K, V, H: BuildHasher> IntoIterator for &'i HashMapImpl<'alloc, K, V, H> { + type IntoIter = <&'i InnerHashMap<'alloc, K, V, H> as IntoIterator>::IntoIter; type Item = (&'i K, &'i V); - /// Creates an iterator over the entries of a `HashMap` in arbitrary order. + /// Creates an iterator over the entries of a `HashMapImpl` in arbitrary order. /// /// The iterator element type is `(&'a K, &'a V)`. /// - /// Return the same [`Iter`] struct as by the `iter` method on [`HashMap`]. + /// Return the same [`Iter`] struct as by the `iter` method on [`HashMapImpl`]. #[inline(always)] fn into_iter(self) -> Self::IntoIter { self.0.iter() } } -impl<'alloc, 'i, K, V> IntoIterator for &'i mut HashMap<'alloc, K, V> { - type IntoIter = <&'i mut FxHashMap<'alloc, K, V> as IntoIterator>::IntoIter; +impl<'alloc, 'i, K, V, H: BuildHasher> IntoIterator for &'i mut HashMapImpl<'alloc, K, V, H> { + type IntoIter = <&'i mut InnerHashMap<'alloc, K, V, H> as IntoIterator>::IntoIter; type Item = (&'i K, &'i mut V); - /// Creates an iterator over the entries of a `HashMap` in arbitrary order + /// Creates an iterator over the entries of a `HashMapImpl` in arbitrary order /// with mutable references to the values. /// /// The iterator element type is `(&'a K, &'a mut V)`. /// - /// Return the same [`IterMut`] struct as by the `iter_mut` method on [`HashMap`]. + /// Return the same [`IterMut`] struct as by the `iter_mut` method on [`HashMapImpl`]. #[inline(always)] fn into_iter(self) -> Self::IntoIter { self.0.iter_mut() } } -impl PartialEq for HashMap<'_, K, V> +impl PartialEq for HashMapImpl<'_, K, V, H> where K: Eq + Hash, V: PartialEq, @@ -278,7 +297,7 @@ where } } -impl Eq for HashMap<'_, K, V> +impl Eq for HashMapImpl<'_, K, V, H> where K: Eq + Hash, V: Eq, diff --git a/crates/oxc_allocator/src/lib.rs b/crates/oxc_allocator/src/lib.rs index ed88c123a567c..ca795293895ad 100644 --- a/crates/oxc_allocator/src/lib.rs +++ b/crates/oxc_allocator/src/lib.rs @@ -71,7 +71,7 @@ pub use bitset::BitSet; pub use boxed::Box; pub use clone_in::CloneIn; pub use convert::{FromIn, IntoIn}; -pub use hash_map::HashMap; +pub use hash_map::{HashMap, HashMapImpl}; pub use hash_set::HashSet; #[cfg(feature = "pool")] pub use pool::*; diff --git a/crates/oxc_ast/src/ast_builder_impl.rs b/crates/oxc_ast/src/ast_builder_impl.rs index 904a3979aa103..5246a7383d914 100644 --- a/crates/oxc_ast/src/ast_builder_impl.rs +++ b/crates/oxc_ast/src/ast_builder_impl.rs @@ -3,7 +3,7 @@ use std::borrow::Cow; use oxc_allocator::{Allocator, AllocatorAccessor, Box, FromIn, IntoIn, Vec}; -use oxc_span::{Atom, SPAN, Span}; +use oxc_span::{Atom, Ident, SPAN, Span}; use oxc_syntax::{ comment_node::CommentNodeId, number::NumberBase, operator::UnaryOperator, scope::ScopeId, }; @@ -104,6 +104,12 @@ impl<'a> AstBuilder<'a> { Atom::from_in(value, self.allocator) } + /// Allocate an [`Ident`] from a string slice. + #[inline] + pub fn ident(self, value: &str) -> Ident<'a> { + Ident::from_in(value, self.allocator) + } + /// Allocate an [`Atom`] from an array of string slices. #[inline] pub fn atom_from_strs_array(self, strings: [&str; N]) -> Atom<'a> { diff --git a/crates/oxc_isolated_declarations/src/declaration.rs b/crates/oxc_isolated_declarations/src/declaration.rs index 4e4f1b7af6c92..201df31ae2fa3 100644 --- a/crates/oxc_isolated_declarations/src/declaration.rs +++ b/crates/oxc_isolated_declarations/src/declaration.rs @@ -48,7 +48,7 @@ impl<'a> IsolatedDeclarations<'a> { ) -> Option> { if decl.id.is_destructuring_pattern() { decl.id.bound_names(&mut |id| { - if !check_binding || self.scope.has_value_reference(&id.name) { + if !check_binding || self.scope.has_value_reference(id.name) { self.error(binding_element_export(id.span)); } }); @@ -57,7 +57,7 @@ impl<'a> IsolatedDeclarations<'a> { if check_binding && let Some(name) = decl.id.get_identifier_name() - && !self.scope.has_value_reference(&name) + && !self.scope.has_value_reference(name) { return None; } @@ -167,7 +167,7 @@ impl<'a> IsolatedDeclarations<'a> { match decl { Declaration::FunctionDeclaration(func) => { let needs_transform = !check_binding - || func.id.as_ref().is_some_and(|id| self.scope.has_value_reference(&id.name)); + || func.id.as_ref().is_some_and(|id| self.scope.has_value_reference(id.name)); needs_transform .then(|| Declaration::FunctionDeclaration(self.transform_function(func, None))) } @@ -176,12 +176,12 @@ impl<'a> IsolatedDeclarations<'a> { .map(Declaration::VariableDeclaration), Declaration::ClassDeclaration(decl) => { let needs_transform = !check_binding - || decl.id.as_ref().is_some_and(|id| self.scope.has_reference(&id.name)); + || decl.id.as_ref().is_some_and(|id| self.scope.has_reference(id.name)); needs_transform .then(|| Declaration::ClassDeclaration(self.transform_class(decl, None))) } Declaration::TSTypeAliasDeclaration(alias_decl) => { - if !check_binding || self.scope.has_reference(&alias_decl.id.name) { + if !check_binding || self.scope.has_reference(alias_decl.id.name) { let mut decl = decl.clone_in(self.ast.allocator); self.visit_declaration(&mut decl); Some(decl) @@ -190,7 +190,7 @@ impl<'a> IsolatedDeclarations<'a> { } } Declaration::TSInterfaceDeclaration(interface_decl) => { - if !check_binding || self.scope.has_reference(&interface_decl.id.name) { + if !check_binding || self.scope.has_reference(interface_decl.id.name) { let mut decl = decl.clone_in(self.ast.allocator); self.visit_declaration(&mut decl); Some(decl) @@ -199,7 +199,7 @@ impl<'a> IsolatedDeclarations<'a> { } } Declaration::TSEnumDeclaration(enum_decl) => { - if !check_binding || self.scope.has_reference(&enum_decl.id.name) { + if !check_binding || self.scope.has_reference(enum_decl.id.name) { Some(self.transform_ts_enum_declaration(enum_decl)) } else { None @@ -210,7 +210,7 @@ impl<'a> IsolatedDeclarations<'a> { || matches!( &decl.id, TSModuleDeclarationName::Identifier(ident) - if self.scope.has_reference(&ident.name) + if self.scope.has_reference(ident.name) ) { Some(Declaration::TSModuleDeclaration( @@ -224,7 +224,7 @@ impl<'a> IsolatedDeclarations<'a> { Some(Declaration::TSGlobalDeclaration(decl.clone_in(self.ast.allocator))) } Declaration::TSImportEqualsDeclaration(decl) => { - if !check_binding || self.scope.has_reference(&decl.id.name) { + if !check_binding || self.scope.has_reference(decl.id.name) { Some(Declaration::TSImportEqualsDeclaration(decl.clone_in(self.ast.allocator))) } else { None diff --git a/crates/oxc_isolated_declarations/src/enum.rs b/crates/oxc_isolated_declarations/src/enum.rs index e003ab1b88b03..e9a5168259150 100644 --- a/crates/oxc_isolated_declarations/src/enum.rs +++ b/crates/oxc_isolated_declarations/src/enum.rs @@ -1,9 +1,7 @@ -use rustc_hash::FxHashMap; - use oxc_allocator::CloneIn; use oxc_ast::ast::*; use oxc_ecmascript::{ToInt32, ToUint32}; -use oxc_span::{Atom, GetSpan, SPAN}; +use oxc_span::{GetSpan, Ident, IdentHashMap, SPAN}; use oxc_syntax::{ number::{NumberBase, ToJsString}, operator::{BinaryOperator, UnaryOperator}, @@ -21,7 +19,7 @@ impl<'a> IsolatedDeclarations<'a> { pub fn transform_ts_enum_declaration(&self, decl: &TSEnumDeclaration<'a>) -> Declaration<'a> { let mut members = self.ast.vec(); let mut prev_initializer_value = Some(ConstantValue::Number(-1.0)); - let mut prev_members = FxHashMap::default(); + let mut prev_members = IdentHashMap::default(); for member in &decl.body.members { let value = if let Some(initializer) = &member.initializer { let computed_value = @@ -42,7 +40,7 @@ impl<'a> IsolatedDeclarations<'a> { if let Some(value) = &value { let member_name = member.id.static_name(); - prev_members.insert(member_name, value.clone()); + prev_members.insert(Ident::from(member_name), value.clone()); } let member = self.ast.ts_enum_member( @@ -95,7 +93,7 @@ impl<'a> IsolatedDeclarations<'a> { &self, expr: &Expression<'a>, enum_name: &str, - prev_members: &FxHashMap, ConstantValue>, + prev_members: &IdentHashMap<'a, ConstantValue>, ) -> Option { self.evaluate(expr, enum_name, prev_members) } @@ -103,7 +101,7 @@ impl<'a> IsolatedDeclarations<'a> { fn evaluate_ref( expr: &Expression<'a>, enum_name: &str, - prev_members: &FxHashMap, ConstantValue>, + prev_members: &IdentHashMap<'a, ConstantValue>, ) -> Option { match expr { match_member_expression!(Expression) => { @@ -111,7 +109,8 @@ impl<'a> IsolatedDeclarations<'a> { let Expression::Identifier(ident) = expr.object() else { return None }; if ident.name == enum_name { let property = expr.static_property_name()?; - prev_members.get(property).cloned() + let property = Ident::from(property); + prev_members.get(&property).cloned() } else { None } @@ -123,7 +122,7 @@ impl<'a> IsolatedDeclarations<'a> { return Some(ConstantValue::Number(f64::NAN)); } - if let Some(value) = prev_members.get(ident.name.as_str()) { + if let Some(value) = prev_members.get(&ident.name) { return Some(value.clone()); } @@ -137,7 +136,7 @@ impl<'a> IsolatedDeclarations<'a> { &self, expr: &Expression<'a>, enum_name: &str, - prev_members: &FxHashMap, ConstantValue>, + prev_members: &IdentHashMap<'a, ConstantValue>, ) -> Option { match expr { Expression::Identifier(_) @@ -172,7 +171,7 @@ impl<'a> IsolatedDeclarations<'a> { &self, expr: &BinaryExpression<'a>, enum_name: &str, - prev_members: &FxHashMap, ConstantValue>, + prev_members: &IdentHashMap<'a, ConstantValue>, ) -> Option { let left = self.evaluate(&expr.left, enum_name, prev_members)?; let right = self.evaluate(&expr.right, enum_name, prev_members)?; @@ -237,7 +236,7 @@ impl<'a> IsolatedDeclarations<'a> { &self, expr: &UnaryExpression<'a>, enum_name: &str, - prev_members: &FxHashMap, ConstantValue>, + prev_members: &IdentHashMap<'a, ConstantValue>, ) -> Option { let value = self.evaluate(&expr.argument, enum_name, prev_members)?; diff --git a/crates/oxc_isolated_declarations/src/lib.rs b/crates/oxc_isolated_declarations/src/lib.rs index 3ff2b29d04216..ab0f7bdb1554a 100644 --- a/crates/oxc_isolated_declarations/src/lib.rs +++ b/crates/oxc_isolated_declarations/src/lib.rs @@ -13,7 +13,7 @@ use oxc_allocator::{Allocator, CloneIn, Vec as ArenaVec}; use oxc_ast::{AstBuilder, NONE, ast::*}; use oxc_ast_visit::Visit; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::{Atom, GetSpan, SPAN, SourceType}; +use oxc_span::{Atom, GetSpan, IdentHashSet, SPAN, SourceType}; use crate::{diagnostics::function_with_assigning_properties, scope::ScopeTree}; @@ -575,7 +575,7 @@ impl<'a> IsolatedDeclarations<'a> { let assignable_properties_for_namespace = IsolatedDeclarations::get_assignable_properties_for_namespaces(stmts); - let mut can_expando_function_names = FxHashSet::default(); + let mut can_expando_function_names = IdentHashSet::default(); for stmt in stmts { match stmt { Statement::ExportNamedDeclaration(decl) => match decl.declaration.as_ref() { @@ -610,7 +610,7 @@ impl<'a> IsolatedDeclarations<'a> { Statement::FunctionDeclaration(func) => { if func.body.is_some() && let Some(name) = func.name() - && self.scope.has_value_reference(&name) + && self.scope.has_value_reference(name) { can_expando_function_names.insert(name); } @@ -620,7 +620,7 @@ impl<'a> IsolatedDeclarations<'a> { if declarator.type_annotation.is_none() && declarator.init.as_ref().is_some_and(Expression::is_function) && let Some(name) = declarator.id.get_identifier_name() - && self.scope.has_value_reference(&name) + && self.scope.has_value_reference(name) { can_expando_function_names.insert(name); } diff --git a/crates/oxc_isolated_declarations/src/module.rs b/crates/oxc_isolated_declarations/src/module.rs index eb9cb43e5658d..45629d279dc28 100644 --- a/crates/oxc_isolated_declarations/src/module.rs +++ b/crates/oxc_isolated_declarations/src/module.rs @@ -1,6 +1,6 @@ use oxc_allocator::{Box as ArenaBox, CloneIn, TakeIn, Vec as ArenaVec}; use oxc_ast::{NONE, ast::*}; -use oxc_span::{Atom, GetSpan, SPAN}; +use oxc_span::{GetSpan, Ident, SPAN}; use crate::{IsolatedDeclarations, diagnostics::default_export_inferred}; @@ -21,11 +21,11 @@ impl<'a> IsolatedDeclarations<'a> { )) } - pub(crate) fn create_unique_name(&self, name: &str) -> Atom<'a> { - let mut binding = self.ast.atom(name); + pub(crate) fn create_unique_name(&self, name: &str) -> Ident<'a> { + let mut binding = self.ast.ident(name); let mut i = 1; - while self.scope.has_reference(&binding) { - binding = self.ast.atom(format!("{name}_{i}").as_str()); + while self.scope.has_reference(binding) { + binding = self.ast.ident(format!("{name}_{i}").as_str()); i += 1; } binding @@ -144,17 +144,7 @@ impl<'a> IsolatedDeclarations<'a> { let mut new_specifiers = self.ast.vec_with_capacity(specifiers.len()); specifiers.iter().for_each(|specifier| { - let is_referenced = match specifier { - ImportDeclarationSpecifier::ImportSpecifier(specifier) => { - self.scope.has_reference(&specifier.local.name) - } - ImportDeclarationSpecifier::ImportDefaultSpecifier(specifier) => { - self.scope.has_reference(&specifier.local.name) - } - ImportDeclarationSpecifier::ImportNamespaceSpecifier(_) => { - self.scope.has_reference(&specifier.name()) - } - }; + let is_referenced = self.scope.has_reference(specifier.local().name); if is_referenced { new_specifiers.push(specifier.clone_in(self.ast.allocator)); } diff --git a/crates/oxc_isolated_declarations/src/scope.rs b/crates/oxc_isolated_declarations/src/scope.rs index 938418e9bd0ab..2019824febf49 100644 --- a/crates/oxc_isolated_declarations/src/scope.rs +++ b/crates/oxc_isolated_declarations/src/scope.rs @@ -1,11 +1,10 @@ use std::cell::Cell; use bitflags::bitflags; -use rustc_hash::FxHashMap; use oxc_ast::ast::*; use oxc_ast_visit::{Visit, walk::*}; -use oxc_span::Atom; +use oxc_span::{Ident, IdentHashMap}; use oxc_syntax::scope::{ScopeFlags, ScopeId}; bitflags! { @@ -20,14 +19,14 @@ bitflags! { /// Declaration scope. #[derive(Debug)] struct Scope<'a> { - bindings: FxHashMap, KindFlags>, - references: FxHashMap, KindFlags>, + bindings: IdentHashMap<'a, KindFlags>, + references: IdentHashMap<'a, KindFlags>, flags: ScopeFlags, } impl Scope<'_> { fn new(flags: ScopeFlags) -> Self { - Self { bindings: FxHashMap::default(), references: FxHashMap::default(), flags } + Self { bindings: IdentHashMap::default(), references: IdentHashMap::default(), flags } } } @@ -48,23 +47,23 @@ impl<'a> ScopeTree<'a> { scope.flags.contains(ScopeFlags::TsModuleBlock) } - pub fn has_reference(&self, name: &str) -> bool { + pub fn has_reference(&self, name: Ident) -> bool { let scope = self.levels.last().unwrap(); - scope.references.contains_key(name) + scope.references.contains_key(&name) } /// Check if the current scope has a value reference for the given name. - pub fn has_value_reference(&self, name: &str) -> bool { + pub fn has_value_reference(&self, name: Ident) -> bool { let scope = self.levels.last().unwrap(); - scope.references.get(name).iter().any(|flags| flags.contains(KindFlags::Value)) + scope.references.get(&name).iter().any(|flags| flags.contains(KindFlags::Value)) } - fn add_binding(&mut self, name: Atom<'a>, flags: KindFlags) { + fn add_binding(&mut self, name: Ident<'a>, flags: KindFlags) { let scope = self.levels.last_mut().unwrap(); scope.bindings.insert(name, flags); } - fn add_reference(&mut self, name: Atom<'a>, flags: KindFlags) { + fn add_reference(&mut self, name: Ident<'a>, flags: KindFlags) { let scope = self.levels.last_mut().unwrap(); scope.references.entry(name).and_modify(|f| *f |= flags).or_insert(flags); } @@ -102,19 +101,19 @@ impl<'a> Visit<'a> for ScopeTree<'a> { } fn visit_identifier_reference(&mut self, ident: &IdentifierReference<'a>) { - self.add_reference(ident.name.into(), KindFlags::Value); + self.add_reference(ident.name, KindFlags::Value); } fn visit_binding_pattern(&mut self, pattern: &BindingPattern<'a>) { if let BindingPattern::BindingIdentifier(ident) = pattern { - self.add_binding(ident.name.into(), KindFlags::Value); + self.add_binding(ident.name, KindFlags::Value); } walk_binding_pattern(self, pattern); } fn visit_ts_type_name(&mut self, name: &TSTypeName<'a>) { if let TSTypeName::IdentifierReference(ident) = name { - self.add_reference(ident.name.into(), KindFlags::Type); + self.add_reference(ident.name, KindFlags::Type); } else { walk_ts_type_name(self, name); } @@ -124,7 +123,7 @@ impl<'a> Visit<'a> for ScopeTree<'a> { fn visit_ts_type_query(&mut self, ty: &TSTypeQuery<'a>) { if let Some(type_name) = ty.expr_name.as_ts_type_name() { if let Some(ident) = TSTypeName::get_identifier_reference(type_name) { - self.add_reference(ident.name.into(), KindFlags::Value); + self.add_reference(ident.name, KindFlags::Value); // `typeof Type` // ^^^^^^^^^^^ if let Some(type_parameters) = &ty.type_arguments { @@ -143,7 +142,7 @@ impl<'a> Visit<'a> for ScopeTree<'a> { // export { ... } for specifier in &decl.specifiers { if let Some(name) = specifier.local.identifier_name() { - self.add_reference(name.into(), KindFlags::All); + self.add_reference(name, KindFlags::All); } } } @@ -151,7 +150,7 @@ impl<'a> Visit<'a> for ScopeTree<'a> { fn visit_export_default_declaration(&mut self, decl: &ExportDefaultDeclaration<'a>) { if let ExportDefaultDeclarationKind::Identifier(ident) = &decl.declaration { - self.add_reference(ident.name.into(), KindFlags::All); + self.add_reference(ident.name, KindFlags::All); } else { walk_export_default_declaration(self, decl); } @@ -165,33 +164,33 @@ impl<'a> Visit<'a> for ScopeTree<'a> { } Declaration::FunctionDeclaration(decl) => { if let Some(id) = decl.id.as_ref() { - self.add_binding(id.name.into(), KindFlags::Value); + self.add_binding(id.name, KindFlags::Value); } } Declaration::ClassDeclaration(decl) => { if let Some(id) = decl.id.as_ref() { - self.add_binding(id.name.into(), KindFlags::Value); + self.add_binding(id.name, KindFlags::Value); } } Declaration::TSTypeAliasDeclaration(decl) => { - self.add_binding(decl.id.name.into(), KindFlags::Type); + self.add_binding(decl.id.name, KindFlags::Type); } Declaration::TSInterfaceDeclaration(decl) => { - self.add_binding(decl.id.name.into(), KindFlags::Type); + self.add_binding(decl.id.name, KindFlags::Type); } Declaration::TSEnumDeclaration(decl) => { - self.add_binding(decl.id.name.into(), KindFlags::All); + self.add_binding(decl.id.name, KindFlags::All); } Declaration::TSModuleDeclaration(decl) => { if let TSModuleDeclarationName::Identifier(ident) = &decl.id { - self.add_binding(ident.name.into(), KindFlags::All); + self.add_binding(ident.name, KindFlags::All); } } Declaration::TSGlobalDeclaration(_) => { // no binding } Declaration::TSImportEqualsDeclaration(decl) => { - self.add_binding(decl.id.name.into(), KindFlags::Value); + self.add_binding(decl.id.name, KindFlags::Value); } } walk_declaration(self, declaration); diff --git a/crates/oxc_linter/src/context/mod.rs b/crates/oxc_linter/src/context/mod.rs index e34b78edc97f6..cd8dec95aa677 100644 --- a/crates/oxc_linter/src/context/mod.rs +++ b/crates/oxc_linter/src/context/mod.rs @@ -200,13 +200,13 @@ impl<'a> LintContext<'a> { /// Checks if the provided identifier is a reference to a global variable. pub fn is_reference_to_global_variable(&self, ident: &IdentifierReference) -> bool { let name = ident.name.as_str(); - self.scoping().root_unresolved_references().contains_key(name) + self.scoping().root_unresolved_references_contains_by_name(name) && !self.globals().get(name).is_some_and(|value| *value == GlobalValue::Off) } /// Checks if the provided identifier is a reference to a global variable. pub fn get_global_variable_value(&self, name: &str) -> Option { - if !self.scoping().root_unresolved_references().contains_key(name) { + if !self.scoping().root_unresolved_references_contains_by_name(name) { return None; } diff --git a/crates/oxc_linter/src/rules/eslint/block_scoped_var.rs b/crates/oxc_linter/src/rules/eslint/block_scoped_var.rs index 8e3788fe53eb5..6eb2555b15458 100644 --- a/crates/oxc_linter/src/rules/eslint/block_scoped_var.rs +++ b/crates/oxc_linter/src/rules/eslint/block_scoped_var.rs @@ -199,7 +199,7 @@ fn run_for_declaration(pattern: &BindingPattern, node_scope_id: ScopeId, ctx: &L // e.g. "var [a, b] = [1, 2]" for ident in pattern.get_binding_identifiers() { let name = ident.name.as_str(); - let Some(symbol) = ctx.scoping().find_binding(node_scope_id, name) else { + let Some(symbol) = ctx.scoping().find_binding_by_name(node_scope_id, name) else { continue; }; diff --git a/crates/oxc_linter/src/rules/eslint/func_names.rs b/crates/oxc_linter/src/rules/eslint/func_names.rs index 630a3c4cb1e28..c45603c4b738d 100644 --- a/crates/oxc_linter/src/rules/eslint/func_names.rs +++ b/crates/oxc_linter/src/rules/eslint/func_names.rs @@ -264,7 +264,7 @@ fn is_recursive_function(func: &Function, func_name: &str, ctx: &LintContext) -> return false; }; - if let Some(binding) = ctx.scoping().find_binding(func_scope_id, func_name) { + if let Some(binding) = ctx.scoping().find_binding_by_name(func_scope_id, func_name) { return ctx.semantic().symbol_references(binding).any(|reference| { let parent = ctx.nodes().parent_node(reference.node_id()); // Check if this reference is the callee of a call expression (direct recursive call) @@ -431,7 +431,7 @@ fn is_invalid_function( /// Returns whether it's safe to insert a function name without breaking shadowing rules fn can_safely_apply_fix(func: &Function, name: &str, ctx: &LintContext) -> bool { - !ctx.scoping().find_binding(func.scope_id(), name).is_some_and(|shadowed_var| { + !ctx.scoping().find_binding_by_name(func.scope_id(), name).is_some_and(|shadowed_var| { ctx.semantic().symbol_references(shadowed_var).any(|reference| { func.span.contains_inclusive(ctx.nodes().get_node(reference.node_id()).kind().span()) }) diff --git a/crates/oxc_linter/src/rules/eslint/no_alert.rs b/crates/oxc_linter/src/rules/eslint/no_alert.rs index 9fb1505a90ef3..3c887235b6df9 100644 --- a/crates/oxc_linter/src/rules/eslint/no_alert.rs +++ b/crates/oxc_linter/src/rules/eslint/no_alert.rs @@ -86,7 +86,7 @@ fn is_global_this_ref_or_global_window<'a>( } fn is_shadowed<'a>(scope_id: ScopeId, name: &'a str, ctx: &LintContext<'a>) -> bool { - ctx.scoping().find_binding(scope_id, name).is_some() + ctx.scoping().find_binding_by_name(scope_id, name).is_some() } impl Rule for NoAlert { diff --git a/crates/oxc_linter/src/rules/eslint/no_console.rs b/crates/oxc_linter/src/rules/eslint/no_console.rs index 9018074d7a921..3f8d14f034592 100644 --- a/crates/oxc_linter/src/rules/eslint/no_console.rs +++ b/crates/oxc_linter/src/rules/eslint/no_console.rs @@ -105,7 +105,7 @@ impl Rule for NoConsole { }; if ident.name != "console" - || !ctx.scoping().root_unresolved_references().contains_key("console") + || !ctx.scoping().root_unresolved_references_contains_by_name("console") { return; } diff --git a/crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs b/crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs index 3f9ced1667340..d9569427d6f17 100644 --- a/crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs +++ b/crates/oxc_linter/src/rules/eslint/no_constant_binary_expression.rs @@ -181,8 +181,7 @@ impl NoConstantBinaryExpression { return ["Boolean", "String", "Number"].contains(&ident.name.as_str()) && ctx .scoping() - .root_unresolved_references() - .contains_key(ident.name.as_str()); + .root_unresolved_references_contains_by_name(ident.name.as_str()); } false } @@ -305,16 +304,14 @@ impl NoConstantBinaryExpression { }, Expression::CallExpression(call_expr) => { if let Expression::Identifier(ident) = &call_expr.callee { - let unresolved_references = ctx.scoping().root_unresolved_references(); - if (ident.name == "String" || ident.name == "Number") - && unresolved_references.contains_key(ident.name.as_str()) - { + let is_unresolved = ctx + .scoping() + .root_unresolved_references_contains_by_name(ident.name.as_str()); + if (ident.name == "String" || ident.name == "Number") && is_unresolved { return true; } - if ident.name == "Boolean" - && unresolved_references.contains_key(ident.name.as_str()) - { + if ident.name == "Boolean" && is_unresolved { return call_expr .arguments .iter() @@ -358,8 +355,7 @@ impl NoConstantBinaryExpression { return ctx.env_contains_var(ident.name.as_str()) && ctx .scoping() - .root_unresolved_references() - .contains_key(ident.name.as_str()); + .root_unresolved_references_contains_by_name(ident.name.as_str()); } false } diff --git a/crates/oxc_linter/src/rules/eslint/no_eval.rs b/crates/oxc_linter/src/rules/eslint/no_eval.rs index c0d8d187bb7b4..7b047ae7f8c83 100644 --- a/crates/oxc_linter/src/rules/eslint/no_eval.rs +++ b/crates/oxc_linter/src/rules/eslint/no_eval.rs @@ -107,13 +107,14 @@ impl Rule for NoEval { }); for name in globals { - let Some(references) = ctx.scoping().root_unresolved_references().get(name) + let Some(references) = + ctx.scoping().get_root_unresolved_reference_by_name(name) else { continue; }; for reference_id in references { - let reference = ctx.scoping().get_reference(*reference_id); + let reference = ctx.scoping().get_reference(reference_id); let node = ctx.nodes().get_node(reference.node_id()); if name == "eval" { diff --git a/crates/oxc_linter/src/rules/eslint/no_new_native_nonconstructor.rs b/crates/oxc_linter/src/rules/eslint/no_new_native_nonconstructor.rs index 26e176a8658e1..aff322be780d3 100644 --- a/crates/oxc_linter/src/rules/eslint/no_new_native_nonconstructor.rs +++ b/crates/oxc_linter/src/rules/eslint/no_new_native_nonconstructor.rs @@ -57,7 +57,7 @@ impl Rule for NoNewNativeNonconstructor { return; }; if matches!(ident.name.as_str(), "Symbol" | "BigInt") - && ctx.scoping().root_unresolved_references().contains_key(ident.name.as_str()) + && ctx.scoping().root_unresolved_references_contains_by_name(ident.name.as_str()) { let start = expr.span.start; let end = start + 3; diff --git a/crates/oxc_linter/src/rules/eslint/no_restricted_globals.rs b/crates/oxc_linter/src/rules/eslint/no_restricted_globals.rs index 8b0413cfdef0b..2ce8923834a9f 100644 --- a/crates/oxc_linter/src/rules/eslint/no_restricted_globals.rs +++ b/crates/oxc_linter/src/rules/eslint/no_restricted_globals.rs @@ -92,7 +92,7 @@ impl Rule for NoRestrictedGlobals { return; }; - if ctx.scoping().root_unresolved_references().contains_key(ident.name.as_str()) { + if ctx.scoping().root_unresolved_references_contains_by_name(ident.name.as_str()) { let reference = ctx.scoping().get_reference(ident.reference_id()); if !reference.is_type() { ctx.diagnostic(no_restricted_globals(&ident.name, message, ident.span)); diff --git a/crates/oxc_linter/src/rules/eslint/no_unused_vars/fixers/fix_vars.rs b/crates/oxc_linter/src/rules/eslint/no_unused_vars/fixers/fix_vars.rs index 78dbb2fe9e469..3c3bc583871a4 100644 --- a/crates/oxc_linter/src/rules/eslint/no_unused_vars/fixers/fix_vars.rs +++ b/crates/oxc_linter/src/rules/eslint/no_unused_vars/fixers/fix_vars.rs @@ -138,7 +138,7 @@ impl NoUnusedVars { let scope_id = symbol.scope_id(); let mut i = 0; let mut new_name = ignored_name.clone(); - while scopes.scope_has_binding(scope_id, &new_name) { + while scopes.scope_has_binding_by_name(scope_id, &new_name) { new_name = format!("{ignored_name}{i}"); i += 1; } diff --git a/crates/oxc_linter/src/rules/eslint/prefer_object_has_own.rs b/crates/oxc_linter/src/rules/eslint/prefer_object_has_own.rs index 5efdeddc2db0e..746159a4e8fe5 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_object_has_own.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_object_has_own.rs @@ -75,7 +75,8 @@ impl Rule for PreferObjectHasOwn { let object_property_name = object.static_property_name(); let is_object = has_left_hand_object(object); - let is_global_scope = ctx.scoping().find_binding(node.scope_id(), "Object").is_none(); + let is_global_scope = + ctx.scoping().find_binding_by_name(node.scope_id(), "Object").is_none(); if is_method_call(call_expr, None, Some(&["call"]), Some(2), Some(2)) && object_property_name == Some("hasOwnProperty") diff --git a/crates/oxc_linter/src/rules/eslint/prefer_object_spread.rs b/crates/oxc_linter/src/rules/eslint/prefer_object_spread.rs index f65f2fd759289..d9e235c2e8fa9 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_object_spread.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_object_spread.rs @@ -87,12 +87,12 @@ impl Rule for PreferObjectSpread { return; }; - let unresolved_references = ctx.scoping().root_unresolved_references(); - match callee.object().get_inner_expression() { Expression::Identifier(ident) => { if ident.name != "Object" - || !unresolved_references.contains_key(ident.name.as_str()) + || !ctx + .scoping() + .root_unresolved_references_contains_by_name(ident.name.as_str()) { return; } @@ -100,7 +100,9 @@ impl Rule for PreferObjectSpread { Expression::StaticMemberExpression(member_expr) => { if let Expression::Identifier(ident) = member_expr.object.get_inner_expression() { if ident.name != "globalThis" - || !unresolved_references.contains_key(ident.name.as_str()) + || !ctx + .scoping() + .root_unresolved_references_contains_by_name(ident.name.as_str()) { return; } diff --git a/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs b/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs index 924d6ddae20aa..5e695781c0a29 100644 --- a/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs +++ b/crates/oxc_linter/src/rules/eslint/prefer_rest_params.rs @@ -73,7 +73,7 @@ impl Rule for PreferRestParams { { return; } - let binding = ctx.scoping().find_binding(node.scope_id(), "arguments"); + let binding = ctx.scoping().find_binding_by_name(node.scope_id(), "arguments"); if binding.is_none() { ctx.diagnostic(prefer_rest_params_diagnostic(node.span())); } diff --git a/crates/oxc_linter/src/rules/eslint/symbol_description.rs b/crates/oxc_linter/src/rules/eslint/symbol_description.rs index 61487eef10664..fb4e8218519fa 100644 --- a/crates/oxc_linter/src/rules/eslint/symbol_description.rs +++ b/crates/oxc_linter/src/rules/eslint/symbol_description.rs @@ -64,7 +64,7 @@ impl Rule for SymbolDescription { if ident.name == "Symbol" && call_expr.arguments.is_empty() - && ctx.scoping().root_unresolved_references().contains_key(ident.name.as_str()) + && ctx.scoping().root_unresolved_references_contains_by_name(ident.name.as_str()) { ctx.diagnostic(symbol_description_diagnostic(call_expr.span)); } diff --git a/crates/oxc_linter/src/rules/eslint/valid_typeof.rs b/crates/oxc_linter/src/rules/eslint/valid_typeof.rs index c050e861060a8..f719df8c143b1 100644 --- a/crates/oxc_linter/src/rules/eslint/valid_typeof.rs +++ b/crates/oxc_linter/src/rules/eslint/valid_typeof.rs @@ -135,7 +135,7 @@ impl Rule for ValidTypeof { if let Expression::Identifier(ident) = sibling && ident.name == "undefined" - && ctx.scoping().root_unresolved_references().contains_key(ident.name.as_str()) + && ctx.scoping().root_unresolved_references_contains_by_name(ident.name.as_str()) { ctx.diagnostic_with_fix( if self.require_string_literals { diff --git a/crates/oxc_linter/src/rules/import/namespace.rs b/crates/oxc_linter/src/rules/import/namespace.rs index 0bd23a3a0ac11..fb75568525001 100644 --- a/crates/oxc_linter/src/rules/import/namespace.rs +++ b/crates/oxc_linter/src/rules/import/namespace.rs @@ -158,7 +158,8 @@ impl Rule for Namespace { return; } - let Some(symbol_id) = ctx.scoping().get_root_binding(entry.local_name.name()) else { + let Some(symbol_id) = ctx.scoping().get_root_binding_by_name(entry.local_name.name()) + else { return; }; diff --git a/crates/oxc_linter/src/rules/import/no_commonjs.rs b/crates/oxc_linter/src/rules/import/no_commonjs.rs index 4e9475be06b4b..49bb2e576c718 100644 --- a/crates/oxc_linter/src/rules/import/no_commonjs.rs +++ b/crates/oxc_linter/src/rules/import/no_commonjs.rs @@ -233,7 +233,11 @@ impl Rule for NoCommonjs { return; } - if ctx.scoping().find_binding(ctx.scoping().root_scope_id(), "require").is_some() { + if ctx + .scoping() + .find_binding_by_name(ctx.scoping().root_scope_id(), "require") + .is_some() + { return; } diff --git a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs index 5f8672f5b9f6f..4f3efdba26226 100644 --- a/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs +++ b/crates/oxc_linter/src/rules/import/no_named_as_default_member.rs @@ -91,7 +91,8 @@ impl Rule for NoNamedAsDefaultMember { continue; } - let Some(symbol_id) = ctx.scoping().get_root_binding(import_entry.local_name.name()) + let Some(symbol_id) = + ctx.scoping().get_root_binding_by_name(import_entry.local_name.name()) else { return; }; diff --git a/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs b/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs index 37e34226f67ee..b0e5510d3f02c 100644 --- a/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs +++ b/crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs @@ -79,7 +79,7 @@ impl Rule for NoJasmineGlobals { .scoping() .root_unresolved_references() .iter() - .filter(|(key, _)| NON_JASMINE_PROPERTY_NAMES.contains(key)); + .filter(|(key, _)| NON_JASMINE_PROPERTY_NAMES.contains(&key.as_str())); for (name, reference_ids) in jasmine_references { for &reference_id in reference_ids { diff --git a/crates/oxc_linter/src/rules/jest/no_mocks_import.rs b/crates/oxc_linter/src/rules/jest/no_mocks_import.rs index 71cd1926dfaf8..253ce3b8fb0f1 100644 --- a/crates/oxc_linter/src/rules/jest/no_mocks_import.rs +++ b/crates/oxc_linter/src/rules/jest/no_mocks_import.rs @@ -70,12 +70,13 @@ impl Rule for NoMocksImport { } } - let Some(require_reference_ids) = ctx.scoping().root_unresolved_references().get("require") + let Some(require_reference_ids) = + ctx.scoping().get_root_unresolved_reference_by_name("require") else { return; }; - for &reference_id in require_reference_ids { + for reference_id in require_reference_ids { let reference = ctx.scoping().get_reference(reference_id); let AstKind::CallExpression(call_expr) = ctx.nodes().parent_kind(reference.node_id()) else { diff --git a/crates/oxc_linter/src/rules/oxc/approx_constant.rs b/crates/oxc_linter/src/rules/oxc/approx_constant.rs index 4f57d48fa1f4c..8ebe3010573b0 100644 --- a/crates/oxc_linter/src/rules/oxc/approx_constant.rs +++ b/crates/oxc_linter/src/rules/oxc/approx_constant.rs @@ -60,7 +60,7 @@ impl Rule for ApproxConstant { ctx.diagnostic_with_suggestion( approx_constant_diagnostic(number_literal.span, name), |fixer| { - if ctx.scoping().find_binding(node.scope_id(), "Math").is_some() { + if ctx.scoping().find_binding_by_name(node.scope_id(), "Math").is_some() { fixer.noop() } else { Self::fix_with_math_constant(fixer, number_literal.span, name) diff --git a/crates/oxc_linter/src/rules/promise/avoid_new.rs b/crates/oxc_linter/src/rules/promise/avoid_new.rs index 691b4f050df57..bc75c60fc87f7 100644 --- a/crates/oxc_linter/src/rules/promise/avoid_new.rs +++ b/crates/oxc_linter/src/rules/promise/avoid_new.rs @@ -54,7 +54,7 @@ impl Rule for AvoidNew { }; if ident.name == "Promise" - && ctx.scoping().root_unresolved_references().contains_key(ident.name.as_str()) + && ctx.scoping().root_unresolved_references_contains_by_name(ident.name.as_str()) { ctx.diagnostic(avoid_new_promise_diagnostic(expr.span)); } diff --git a/crates/oxc_linter/src/rules/react/display_name.rs b/crates/oxc_linter/src/rules/react/display_name.rs index 2ebe9423c03be..2b5ab3f862475 100644 --- a/crates/oxc_linter/src/rules/react/display_name.rs +++ b/crates/oxc_linter/src/rules/react/display_name.rs @@ -209,9 +209,10 @@ impl Rule for DisplayName { } // Check for CommonJS module.exports by looking at references to 'module' global - if let Some(module_reference_ids) = ctx.scoping().root_unresolved_references().get("module") + if let Some(module_reference_ids) = + ctx.scoping().get_root_unresolved_reference_by_name("module") { - for &reference_id in module_reference_ids { + for reference_id in module_reference_ids { let reference = ctx.scoping().get_reference(reference_id); let node = ctx.nodes().get_node(reference.node_id()); diff --git a/crates/oxc_linter/src/rules/react/no_danger_with_children.rs b/crates/oxc_linter/src/rules/react/no_danger_with_children.rs index 7c9b33e7f4acf..72960cbb25a09 100644 --- a/crates/oxc_linter/src/rules/react/no_danger_with_children.rs +++ b/crates/oxc_linter/src/rules/react/no_danger_with_children.rs @@ -327,7 +327,7 @@ fn find_var_in_scope<'c>( name: &str, ) -> Option<&'c AstNode<'c>> { ctx.scoping() - .find_binding(node.scope_id(), name) + .find_binding_by_name(node.scope_id(), name) .map(|symbol_id| ctx.semantic().symbol_declaration(symbol_id)) } diff --git a/crates/oxc_linter/src/rules/react/react_in_jsx_scope.rs b/crates/oxc_linter/src/rules/react/react_in_jsx_scope.rs index 66181d0b69f6a..85e3c7c30426f 100644 --- a/crates/oxc_linter/src/rules/react/react_in_jsx_scope.rs +++ b/crates/oxc_linter/src/rules/react/react_in_jsx_scope.rs @@ -65,11 +65,11 @@ impl Rule for ReactInJsxScope { }; let scope = ctx.scoping(); let react_name = "React"; - if scope.get_binding(scope.root_scope_id(), react_name).is_some() { + if scope.get_binding_by_name(scope.root_scope_id(), react_name).is_some() { return; } - if scope.find_binding(node.scope_id(), react_name).is_none() { + if scope.find_binding_by_name(node.scope_id(), react_name).is_none() { ctx.diagnostic(react_in_jsx_scope_diagnostic(node_span)); } } diff --git a/crates/oxc_linter/src/rules/typescript/no_require_imports.rs b/crates/oxc_linter/src/rules/typescript/no_require_imports.rs index 8d62fff577cff..def95561439af 100644 --- a/crates/oxc_linter/src/rules/typescript/no_require_imports.rs +++ b/crates/oxc_linter/src/rules/typescript/no_require_imports.rs @@ -170,7 +170,11 @@ impl Rule for NoRequireImports { } } - if ctx.scoping().find_binding(ctx.scoping().root_scope_id(), "require").is_some() { + if ctx + .scoping() + .find_binding_by_name(ctx.scoping().root_scope_id(), "require") + .is_some() + { return; } diff --git a/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs b/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs index 525444ef05501..7b641f6b9909e 100644 --- a/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs +++ b/crates/oxc_linter/src/rules/unicorn/new_for_builtins.rs @@ -94,7 +94,7 @@ fn is_expr_global_builtin<'a, 'b>( let expr = expr.without_parentheses(); if let Expression::Identifier(ident) = expr { let name = ident.name.as_str(); - if !ctx.scoping().root_unresolved_references().contains_key(name) { + if !ctx.scoping().root_unresolved_references_contains_by_name(name) { return None; } diff --git a/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs b/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs index b3ad720fc6f7c..99ed3e2f2f10b 100644 --- a/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs +++ b/crates/oxc_linter/src/rules/unicorn/prefer_global_this.rs @@ -65,7 +65,7 @@ impl Rule for PreferGlobalThis { if !matches!(ident.name.as_str(), "window" | "self" | "global") || is_computed_member_expression_object(node, ctx) - || !ctx.scoping().root_unresolved_references().contains_key(&ident.name.as_str()) + || !ctx.scoping().root_unresolved_references_contains_by_name(ident.name.as_str()) { return; } diff --git a/crates/oxc_linter/src/rules/vue/no_lifecycle_after_await.rs b/crates/oxc_linter/src/rules/vue/no_lifecycle_after_await.rs index fb2b47a1346f2..9709cdd80b660 100644 --- a/crates/oxc_linter/src/rules/vue/no_lifecycle_after_await.rs +++ b/crates/oxc_linter/src/rules/vue/no_lifecycle_after_await.rs @@ -153,7 +153,7 @@ fn check_setup_in_object<'a>(obj_expr: &ObjectExpression<'a>, ctx: &LintContext< if !LIFECYCLE_HOOKS.contains(&import_name) { continue; } - if let Some(symbol_id) = scoping.get_root_binding(import_entry.local_name.name()) { + if let Some(symbol_id) = scoping.get_root_binding_by_name(import_entry.local_name.name()) { symbol_to_import_entry.insert(symbol_id, import_entry); } } diff --git a/crates/oxc_linter/src/snapshots/jest_no_confusing_set_timeout.snap b/crates/oxc_linter/src/snapshots/jest_no_confusing_set_timeout.snap index 7d708a56205b0..327e44bce3723 100644 --- a/crates/oxc_linter/src/snapshots/jest_no_confusing_set_timeout.snap +++ b/crates/oxc_linter/src/snapshots/jest_no_confusing_set_timeout.snap @@ -17,22 +17,22 @@ source: crates/oxc_linter/src/tester.rs 11 │ ╰──── - ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods. + ⚠ eslint-plugin-jest(no-confusing-set-timeout): Do not call `jest.setTimeout` multiple times ╭─[no_confusing_set_timeout.tsx:10:17] 9 │ }); 10 │ jest.setTimeout(800); · ─────────────── 11 │ ╰──── + help: Only the last call to `jest.setTimeout` will have an effect. - ⚠ eslint-plugin-jest(no-confusing-set-timeout): Do not call `jest.setTimeout` multiple times + ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods. ╭─[no_confusing_set_timeout.tsx:10:17] 9 │ }); 10 │ jest.setTimeout(800); · ─────────────── 11 │ ╰──── - help: Only the last call to `jest.setTimeout` will have an effect. ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods. ╭─[no_confusing_set_timeout.tsx:10:17] @@ -58,7 +58,7 @@ source: crates/oxc_linter/src/tester.rs 4 │ beforeEach(async () => { await new Promise(resolve => { setTimeout(resolve, 10000).unref(); });}); ╰──── - ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods. + ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should only be called in a global scope ╭─[no_confusing_set_timeout.tsx:5:25] 4 │ await new Promise((resolve) => { 5 │ jest.setTimeout(1000); @@ -66,7 +66,7 @@ source: crates/oxc_linter/src/tester.rs 6 │ setTimeout(resolve, 10000).unref(); ╰──── - ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should only be called in a global scope + ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods. ╭─[no_confusing_set_timeout.tsx:5:25] 4 │ await new Promise((resolve) => { 5 │ jest.setTimeout(1000); @@ -82,7 +82,7 @@ source: crates/oxc_linter/src/tester.rs 6 │ setTimeout(resolve, 10000).unref(); ╰──── - ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods. + ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should only be called in a global scope ╭─[no_confusing_set_timeout.tsx:3:21] 2 │ test('test-suite', () => { 3 │ jest.setTimeout(1000); @@ -90,7 +90,7 @@ source: crates/oxc_linter/src/tester.rs 4 │ }); ╰──── - ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should only be called in a global scope + ⚠ eslint-plugin-jest(no-confusing-set-timeout): `jest.setTimeout` should be placed before any other jest methods. ╭─[no_confusing_set_timeout.tsx:3:21] 2 │ test('test-suite', () => { 3 │ jest.setTimeout(1000); diff --git a/crates/oxc_linter/src/snapshots/jest_no_identical_title.snap b/crates/oxc_linter/src/snapshots/jest_no_identical_title.snap index bdc5629fe1d1e..e56105a5ec003 100644 --- a/crates/oxc_linter/src/snapshots/jest_no_identical_title.snap +++ b/crates/oxc_linter/src/snapshots/jest_no_identical_title.snap @@ -29,11 +29,11 @@ source: crates/oxc_linter/src/tester.rs help: Change the title of test. ⚠ eslint-plugin-jest(no-identical-title): Test title is used multiple times in the same describe block. - ╭─[no_identical_title.tsx:3:20] + ╭─[no_identical_title.tsx:2:21] + 1 │ 2 │ xtest('this', () => {}); + · ────── 3 │ test('this', () => {}); - · ────── - 4 │ ╰──── help: Change the title of test. @@ -83,11 +83,11 @@ source: crates/oxc_linter/src/tester.rs help: Change the title of describe block. ⚠ eslint-plugin-jest(no-identical-title): Describe block title is used multiple times in the same describe block. - ╭─[no_identical_title.tsx:2:25] - 1 │ + ╭─[no_identical_title.tsx:3:24] 2 │ fdescribe('foo', () => {}); - · ───── 3 │ describe('foo', () => {}); + · ───── + 4 │ ╰──── help: Change the title of describe block. diff --git a/crates/oxc_linter/src/snapshots/jest_padding_around_test_blocks.snap b/crates/oxc_linter/src/snapshots/jest_padding_around_test_blocks.snap index 9d0d66647747b..be65da7003ff5 100644 --- a/crates/oxc_linter/src/snapshots/jest_padding_around_test_blocks.snap +++ b/crates/oxc_linter/src/snapshots/jest_padding_around_test_blocks.snap @@ -16,15 +16,6 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Make sure there is an empty new line before the test block - ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before fit block - ╭─[padding_around_test_blocks.tsx:2:1] - 1 │ it('foo', () => {}); - 2 │ fit('bar', () => {}); - · ▲ - 3 │ test('baz', () => {}); - ╰──── - help: Make sure there is an empty new line before the fit block - ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before test block ╭─[padding_around_test_blocks.tsx:3:1] 2 │ fit('bar', () => {}); @@ -34,11 +25,11 @@ source: crates/oxc_linter/src/tester.rs help: Make sure there is an empty new line before the test block ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before fit block - ╭─[padding_around_test_blocks.tsx:7:1] - 6 │ }); - 7 │ fit('bar', () => { + ╭─[padding_around_test_blocks.tsx:2:1] + 1 │ it('foo', () => {}); + 2 │ fit('bar', () => {}); · ▲ - 8 │ // stuff + 3 │ test('baz', () => {}); ╰──── help: Make sure there is an empty new line before the fit block @@ -51,6 +42,51 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Make sure there is an empty new line before the xit block + ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before it block + ╭─[padding_around_test_blocks.tsx:4:1] + 3 │ const bar = 'baz'; + 4 │ it('foo', () => { + · ▲ + 5 │ // stuff + ╰──── + help: Make sure there is an empty new line before the it block + + ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before it block + ╭─[padding_around_test_blocks.tsx:18:6] + 17 │ }); + 18 │ // With a comment + · ▲ + 19 │ it('is another bar w/ it', () => { + ╰──── + help: Make sure there is an empty new line before the it block + + ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before it block + ╭─[padding_around_test_blocks.tsx:22:6] + 21 │ test.skip('skipping', () => {}); // Another comment + 22 │ it.skip('skipping too', () => {}); + · ▲ + 23 │ });xtest('weird', () => {}); + ╰──── + help: Make sure there is an empty new line before the it block + + ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before xtest block + ╭─[padding_around_test_blocks.tsx:23:5] + 22 │ it.skip('skipping too', () => {}); + 23 │ });xtest('weird', () => {}); + · ▲ + 24 │ test + ╰──── + help: Make sure there is an empty new line before the xtest block + + ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before fit block + ╭─[padding_around_test_blocks.tsx:7:1] + 6 │ }); + 7 │ fit('bar', () => { + · ▲ + 8 │ // stuff + ╰──── + help: Make sure there is an empty new line before the fit block + ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before test block ╭─[padding_around_test_blocks.tsx:10:1] 9 │ }); @@ -96,42 +132,6 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: Make sure there is an empty new line before the test block - ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before xtest block - ╭─[padding_around_test_blocks.tsx:23:5] - 22 │ it.skip('skipping too', () => {}); - 23 │ });xtest('weird', () => {}); - · ▲ - 24 │ test - ╰──── - help: Make sure there is an empty new line before the xtest block - - ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before it block - ╭─[padding_around_test_blocks.tsx:4:1] - 3 │ const bar = 'baz'; - 4 │ it('foo', () => { - · ▲ - 5 │ // stuff - ╰──── - help: Make sure there is an empty new line before the it block - - ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before it block - ╭─[padding_around_test_blocks.tsx:18:6] - 17 │ }); - 18 │ // With a comment - · ▲ - 19 │ it('is another bar w/ it', () => { - ╰──── - help: Make sure there is an empty new line before the it block - - ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before it block - ╭─[padding_around_test_blocks.tsx:22:6] - 21 │ test.skip('skipping', () => {}); // Another comment - 22 │ it.skip('skipping too', () => {}); - · ▲ - 23 │ });xtest('weird', () => {}); - ╰──── - help: Make sure there is an empty new line before the it block - ⚠ eslint-plugin-jest(padding-around-test-blocks): Missing padding before it block ╭─[padding_around_test_blocks.tsx:5:5] 4 │ }); diff --git a/crates/oxc_linter/src/snapshots/jest_prefer_lowercase_title@jest.snap b/crates/oxc_linter/src/snapshots/jest_prefer_lowercase_title@jest.snap index d15a6305ba5c3..ef007536e365b 100644 --- a/crates/oxc_linter/src/snapshots/jest_prefer_lowercase_title@jest.snap +++ b/crates/oxc_linter/src/snapshots/jest_prefer_lowercase_title@jest.snap @@ -179,22 +179,22 @@ source: crates/oxc_linter/src/tester.rs help: `"Works!"`s should begin with lowercase ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names - ╭─[prefer_lowercase_title.tsx:3:30] - 2 │ describe('MyClass', () => { + ╭─[prefer_lowercase_title.tsx:4:28] 3 │ describe('MyMethod', () => { - · ────────── 4 │ it('Does things', () => { + · ───────────── + 5 │ // ╰──── - help: `"MyMethod"`s should begin with lowercase + help: `"Does things"`s should begin with lowercase ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names - ╭─[prefer_lowercase_title.tsx:4:28] + ╭─[prefer_lowercase_title.tsx:3:30] + 2 │ describe('MyClass', () => { 3 │ describe('MyMethod', () => { + · ────────── 4 │ it('Does things', () => { - · ───────────── - 5 │ // ╰──── - help: `"Does things"`s should begin with lowercase + help: `"MyMethod"`s should begin with lowercase ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names ╭─[prefer_lowercase_title.tsx:4:29] @@ -214,6 +214,15 @@ source: crates/oxc_linter/src/tester.rs ╰──── help: `"Does things"`s should begin with lowercase + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names + ╭─[prefer_lowercase_title.tsx:4:28] + 3 │ describe('MyMethod', () => { + 4 │ it('Does things', () => { + · ───────────── + 5 │ // + ╰──── + help: `"Does things"`s should begin with lowercase + ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names ╭─[prefer_lowercase_title.tsx:2:26] 1 │ @@ -231,12 +240,3 @@ source: crates/oxc_linter/src/tester.rs 4 │ it('Does things', () => { ╰──── help: `"MyMethod"`s should begin with lowercase - - ⚠ eslint-plugin-jest(prefer-lowercase-title): Enforce lowercase test names - ╭─[prefer_lowercase_title.tsx:4:28] - 3 │ describe('MyMethod', () => { - 4 │ it('Does things', () => { - · ───────────── - 5 │ // - ╰──── - help: `"Does things"`s should begin with lowercase diff --git a/crates/oxc_linter/src/utils/jest.rs b/crates/oxc_linter/src/utils/jest.rs index ea58fd44e0e79..8bc17af4b07b2 100644 --- a/crates/oxc_linter/src/utils/jest.rs +++ b/crates/oxc_linter/src/utils/jest.rs @@ -260,7 +260,7 @@ fn collect_ids_referenced_to_global<'c>( .scoping() .root_unresolved_references() .iter() - .filter(|(name, _)| JEST_METHOD_NAMES.contains(name)) + .filter(|(name, _)| JEST_METHOD_NAMES.contains(&name.as_str())) .flat_map(|(_, reference_ids)| reference_ids.iter().copied()) } diff --git a/crates/oxc_mangler/src/lib.rs b/crates/oxc_mangler/src/lib.rs index 7b1182f48105e..3e1131bc0f472 100644 --- a/crates/oxc_mangler/src/lib.rs +++ b/crates/oxc_mangler/src/lib.rs @@ -333,8 +333,8 @@ impl<'t> Mangler<'t> { // Scopes with direct eval: collect binding names as reserved (they can be // accessed by eval at runtime) and skip slot assignment (keep original names). if scoping.scope_flags(scope_id).contains_direct_eval() { - for (&name, _) in bindings { - eval_reserved_names.insert(name); + for (name, _) in bindings { + eval_reserved_names.insert(name.as_str()); } continue; } @@ -449,8 +449,7 @@ impl<'t> Mangler<'t> { &slots, ); - let root_unresolved_references = scoping.root_unresolved_references(); - let root_bindings = scoping.get_bindings(scoping.root_scope_id()); + let root_scope_id = scoping.root_scope_id(); // Generate reserved names only for slots that have symbols (frequencies.len()) // instead of all slots. This avoids generating unused names. @@ -469,8 +468,8 @@ impl<'t> Mangler<'t> { let n = name.as_str(); if !oxc_syntax::keyword::is_reserved_keyword(n) && !is_special_name(n) - && !root_unresolved_references.contains_key(n) - && !(root_bindings.contains_key(n) + && !scoping.root_unresolved_references_contains_by_name(n) + && !(scoping.scope_has_binding_by_name(root_scope_id, n) && (!self.options.top_level || exported_names.contains(n))) // TODO: only skip the names that are kept in the current scope && !keep_name_names.contains(n) diff --git a/crates/oxc_semantic/src/binder.rs b/crates/oxc_semantic/src/binder.rs index 43a2f0d09043a..fced65405008c 100644 --- a/crates/oxc_semantic/src/binder.rs +++ b/crates/oxc_semantic/src/binder.rs @@ -3,6 +3,7 @@ use oxc_allocator::{GetAddress, UnstableAddress}; use oxc_ast::{AstKind, ast::*}; use oxc_ecmascript::{BoundNames, IsSimpleParameterList}; +use oxc_span::Ident; use oxc_syntax::{node::NodeId, scope::ScopeFlags, symbol::SymbolFlags}; use crate::{SemanticBuilder, checker::is_function_decl_part_of_if_statement}; @@ -38,7 +39,7 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> { if self.kind.is_lexical() { self.id.bound_names(&mut |ident| { - let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); + let symbol_id = builder.declare_symbol(ident.span, ident.name, includes, excludes); ident.symbol_id.set(Some(symbol_id)); }); } else { @@ -63,7 +64,7 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> { for &scope_id in &var_scope_ids { if let Some(symbol_id) = - builder.check_redeclaration(scope_id, span, &name, excludes, true) + builder.check_redeclaration(scope_id, span, name, excludes, true) { builder.add_redeclare_variable(symbol_id, includes, span); declared_symbol_id = Some(symbol_id); @@ -73,8 +74,8 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> { if !builder.scoping.scope_has_binding(target_scope_id, &name) { // remove current scope binding and add to target scope // avoid same symbols appear in multi-scopes - builder.scoping.remove_binding(scope_id, &name); - builder.scoping.add_binding(target_scope_id, &name, symbol_id); + builder.scoping.remove_binding(scope_id, name); + builder.scoping.add_binding(target_scope_id, name, symbol_id); builder.scoping.symbol_scope_ids[symbol_id] = target_scope_id; } break; @@ -85,24 +86,14 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> { // we don't need to create another symbol with the same name // to make sure they point to the same symbol. let symbol_id = declared_symbol_id.unwrap_or_else(|| { - builder.declare_symbol_on_scope( - span, - &name, - target_scope_id, - includes, - excludes, - ) + builder.declare_symbol_on_scope(span, name, target_scope_id, includes, excludes) }); ident.symbol_id.set(Some(symbol_id)); // Finally, add the variable to all hoisted scopes // to support redeclaration checks when declaring variables with the same name later. for &scope_id in &var_scope_ids { - builder - .hoisting_variables - .entry(scope_id) - .or_default() - .insert(name.into(), symbol_id); + builder.hoisting_variables.entry(scope_id).or_default().insert(name, symbol_id); } }); } @@ -123,7 +114,7 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> { } impl<'a> Binder<'a> for Class<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let includes = if self.declare { SymbolFlags::Class | SymbolFlags::Ambient } else { @@ -131,13 +122,13 @@ impl<'a> Binder<'a> for Class<'a> { }; let Some(ident) = &self.id else { return }; let symbol_id = - builder.declare_symbol(ident.span, &ident.name, includes, SymbolFlags::ClassExcludes); + builder.declare_symbol(ident.span, ident.name, includes, SymbolFlags::ClassExcludes); ident.symbol_id.set(Some(symbol_id)); } } impl<'a> Binder<'a> for Function<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let is_declaration = self.is_declaration(); if let Some(ident) = &self.id { @@ -158,7 +149,7 @@ impl<'a> Binder<'a> for Function<'a> { SymbolFlags::FunctionExcludes - SymbolFlags::FunctionScopedVariable }; - let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); + let symbol_id = builder.declare_symbol(ident.span, ident.name, includes, excludes); ident.symbol_id.set(Some(symbol_id)); // Save `@__NO_SIDE_EFFECTS__` @@ -191,7 +182,7 @@ impl<'a> Binder<'a> for Function<'a> { impl<'a> Binder<'a> for BindingRestElement<'a> { // Binds the FormalParameters's rest of a function or method. - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let parent_kind = builder.nodes.parent_kind(builder.current_node_id); let AstKind::FormalParameters(_) = parent_kind else { return; @@ -201,7 +192,7 @@ impl<'a> Binder<'a> for BindingRestElement<'a> { let excludes = SymbolFlags::FunctionScopedVariable | SymbolFlags::FunctionScopedVariableExcludes; self.bound_names(&mut |ident| { - let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); + let symbol_id = builder.declare_symbol(ident.span, ident.name, includes, excludes); ident.symbol_id.set(Some(symbol_id)); }); } @@ -209,7 +200,7 @@ impl<'a> Binder<'a> for BindingRestElement<'a> { impl<'a> Binder<'a> for FormalParameter<'a> { // Binds the FormalParameter of a function or method. - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let parent_kind = builder.nodes.parent_kind(builder.current_node_id); let AstKind::FormalParameters(parameters) = parent_kind else { unreachable!() }; @@ -236,7 +227,7 @@ impl<'a> Binder<'a> for FormalParameter<'a> { }; self.bound_names(&mut |ident| { - let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); + let symbol_id = builder.declare_symbol(ident.span, ident.name, includes, excludes); ident.symbol_id.set(Some(symbol_id)); }); } @@ -244,7 +235,7 @@ impl<'a> Binder<'a> for FormalParameter<'a> { impl<'a> Binder<'a> for FormalParameterRest<'a> { // Binds the FormalParameter of a function or method. - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let parent_kind = builder.nodes.parent_kind(builder.current_node_id); let AstKind::FormalParameters(parameters) = parent_kind else { unreachable!() }; @@ -271,14 +262,14 @@ impl<'a> Binder<'a> for FormalParameterRest<'a> { }; self.rest.argument.bound_names(&mut |ident| { - let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes); + let symbol_id = builder.declare_symbol(ident.span, ident.name, includes, excludes); ident.symbol_id.set(Some(symbol_id)); }); } } impl<'a> Binder<'a> for CatchParameter<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let current_scope_id = builder.current_scope_id; // https://tc39.es/ecma262/#sec-variablestatements-in-catch-blocks // It is a Syntax Error if any element of the BoundNames of CatchParameter also occurs in the VarDeclaredNames of Block @@ -286,13 +277,13 @@ impl<'a> Binder<'a> for CatchParameter<'a> { if let BindingPattern::BindingIdentifier(ident) = &self.pattern { let includes = SymbolFlags::FunctionScopedVariable | SymbolFlags::CatchVariable; let symbol_id = - builder.declare_shadow_symbol(&ident.name, ident.span, current_scope_id, includes); + builder.declare_shadow_symbol(ident.name, ident.span, current_scope_id, includes); ident.symbol_id.set(Some(symbol_id)); } else { self.pattern.bound_names(&mut |ident| { let symbol_id = builder.declare_symbol( ident.span, - &ident.name, + ident.name, SymbolFlags::BlockScopedVariable | SymbolFlags::CatchVariable, SymbolFlags::BlockScopedVariableExcludes, ); @@ -302,10 +293,10 @@ impl<'a> Binder<'a> for CatchParameter<'a> { } } -fn declare_symbol_for_import_specifier( - ident: &BindingIdentifier, +fn declare_symbol_for_import_specifier<'a>( + ident: &BindingIdentifier<'a>, is_type: bool, - builder: &mut SemanticBuilder, + builder: &mut SemanticBuilder<'a>, ) { let includes = if is_type || matches!(builder.nodes.parent_kind(builder.current_node_id), AstKind::ImportDeclaration(decl) if decl.import_kind.is_type(), @@ -317,7 +308,7 @@ fn declare_symbol_for_import_specifier( let symbol_id = builder.declare_symbol( ident.span, - &ident.name, + ident.name, includes, SymbolFlags::ImportBindingExcludes, ); @@ -325,31 +316,31 @@ fn declare_symbol_for_import_specifier( } impl<'a> Binder<'a> for ImportSpecifier<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { declare_symbol_for_import_specifier(&self.local, self.import_kind.is_type(), builder); } } impl<'a> Binder<'a> for ImportDefaultSpecifier<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { declare_symbol_for_import_specifier(&self.local, false, builder); } } impl<'a> Binder<'a> for ImportNamespaceSpecifier<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { declare_symbol_for_import_specifier(&self.local, false, builder); } } impl<'a> Binder<'a> for TSImportEqualsDeclaration<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { declare_symbol_for_import_specifier(&self.id, false, builder); } } impl<'a> Binder<'a> for TSTypeAliasDeclaration<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let includes = if self.declare { SymbolFlags::TypeAlias | SymbolFlags::Ambient } else { @@ -357,7 +348,7 @@ impl<'a> Binder<'a> for TSTypeAliasDeclaration<'a> { }; let symbol_id = builder.declare_symbol( self.id.span, - &self.id.name, + self.id.name, includes, SymbolFlags::TypeAliasExcludes, ); @@ -366,7 +357,7 @@ impl<'a> Binder<'a> for TSTypeAliasDeclaration<'a> { } impl<'a> Binder<'a> for TSInterfaceDeclaration<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let includes = if self.declare { SymbolFlags::Interface | SymbolFlags::Ambient } else { @@ -374,7 +365,7 @@ impl<'a> Binder<'a> for TSInterfaceDeclaration<'a> { }; let symbol_id = builder.declare_symbol( self.id.span, - &self.id.name, + self.id.name, includes, SymbolFlags::InterfaceExcludes, ); @@ -383,7 +374,7 @@ impl<'a> Binder<'a> for TSInterfaceDeclaration<'a> { } impl<'a> Binder<'a> for TSEnumDeclaration<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let is_const = self.r#const; let includes = if self.declare { SymbolFlags::Ambient } else { SymbolFlags::empty() }; let (includes, excludes) = if is_const { @@ -391,16 +382,28 @@ impl<'a> Binder<'a> for TSEnumDeclaration<'a> { } else { (SymbolFlags::RegularEnum | includes, SymbolFlags::RegularEnumExcludes) }; - let symbol_id = builder.declare_symbol(self.id.span, &self.id.name, includes, excludes); + let symbol_id = builder.declare_symbol(self.id.span, self.id.name, includes, excludes); self.id.symbol_id.set(Some(symbol_id)); } } impl<'a> Binder<'a> for TSEnumMember<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { + let name = match &self.id { + TSEnumMemberName::Identifier(ident) => ident.name, + TSEnumMemberName::String(lit) | TSEnumMemberName::ComputedString(lit) => { + Ident::from(lit.value.as_str()) + } + TSEnumMemberName::ComputedTemplateString(template) => { + let quasi = template.single_quasi().expect( + "`TSEnumMemberName::TemplateString` should have no substitution and at least one quasi", + ); + Ident::from(quasi.as_str()) + } + }; builder.declare_symbol( self.span, - self.id.static_name().as_str(), + name, SymbolFlags::EnumMember, SymbolFlags::EnumMemberExcludes, ); @@ -421,7 +424,7 @@ impl<'a> Binder<'a> for TSModuleDeclaration<'a> { if self.declare { includes |= SymbolFlags::Ambient; } - let symbol_id = builder.declare_symbol(id.span, &id.name, includes, excludes); + let symbol_id = builder.declare_symbol(id.span, id.name, includes, excludes); id.set_symbol_id(symbol_id); } @@ -685,7 +688,7 @@ fn get_module_instance_state_for_alias_target<'a>( } impl<'a> Binder<'a> for TSTypeParameter<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let scope_id = if matches!( builder.nodes.parent_kind(builder.current_node_id), AstKind::TSInferType(_) @@ -700,7 +703,7 @@ impl<'a> Binder<'a> for TSTypeParameter<'a> { let symbol_id = builder.declare_symbol_on_scope( self.name.span, - &self.name.name, + self.name.name, scope_id.unwrap_or(builder.current_scope_id), SymbolFlags::TypeParameter, SymbolFlags::TypeParameterExcludes, @@ -710,10 +713,10 @@ impl<'a> Binder<'a> for TSTypeParameter<'a> { } impl<'a> Binder<'a> for TSMappedType<'a> { - fn bind(&self, builder: &mut SemanticBuilder) { + fn bind(&self, builder: &mut SemanticBuilder<'a>) { let symbol_id = builder.declare_symbol_on_scope( self.key.span, - &self.key.name, + self.key.name, builder.current_scope_id, SymbolFlags::TypeParameter, SymbolFlags::TypeParameterExcludes, diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 3de7ae8e950e1..bbfc9377dc8cc 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -16,7 +16,7 @@ use oxc_cfg::{ IterationInstructionKind, ReturnInstructionKind, }; use oxc_diagnostics::OxcDiagnostic; -use oxc_span::{Atom, SourceType, Span}; +use oxc_span::{Ident, IdentHashMap, SourceType, Span}; use oxc_syntax::{ node::{NodeFlags, NodeId}, reference::{Reference, ReferenceFlags, ReferenceId}, @@ -84,7 +84,7 @@ pub struct SemanticBuilder<'a> { pub(crate) current_function_node_id: NodeId, pub(crate) module_instance_state_cache: FxHashMap, current_reference_flags: ReferenceFlags, - pub(crate) hoisting_variables: FxHashMap, SymbolId>>, + pub(crate) hoisting_variables: FxHashMap>, // builders pub(crate) nodes: AstNodes<'a>, @@ -276,9 +276,8 @@ impl<'a> SemanticBuilder<'a> { } debug_assert_eq!(self.unresolved_references.scope_depth(), 1); - self.scoping.set_root_unresolved_references( - self.unresolved_references.into_root().into_iter().map(|(k, v)| (k.as_str(), v)), - ); + self.scoping + .set_root_unresolved_references(self.unresolved_references.into_root().into_iter()); #[cfg(feature = "linter")] let jsdoc = self.jsdoc.build(); @@ -401,7 +400,7 @@ impl<'a> SemanticBuilder<'a> { pub(crate) fn declare_symbol_on_scope( &mut self, span: Span, - name: &str, + name: Ident<'a>, scope_id: ScopeId, includes: SymbolFlags, excludes: SymbolFlags, @@ -412,8 +411,13 @@ impl<'a> SemanticBuilder<'a> { return symbol_id; } - let symbol_id = - self.scoping.create_symbol(span, name, includes, scope_id, self.current_node_id); + let symbol_id = self.scoping.create_symbol( + span, + name.as_str(), + includes, + scope_id, + self.current_node_id, + ); self.scoping.add_binding(scope_id, name, symbol_id); symbol_id @@ -423,7 +427,7 @@ impl<'a> SemanticBuilder<'a> { pub(crate) fn declare_symbol( &mut self, span: Span, - name: &str, + name: Ident<'a>, includes: SymbolFlags, excludes: SymbolFlags, ) -> SymbolId { @@ -438,12 +442,12 @@ impl<'a> SemanticBuilder<'a> { &self, scope_id: ScopeId, span: Span, - name: &str, + name: Ident<'_>, excludes: SymbolFlags, report_error: bool, ) -> Option { - let symbol_id = self.scoping.get_binding(scope_id, name).or_else(|| { - self.hoisting_variables.get(&scope_id).and_then(|symbols| symbols.get(name).copied()) + let symbol_id = self.scoping.get_binding(scope_id, &name).or_else(|| { + self.hoisting_variables.get(&scope_id).and_then(|symbols| symbols.get(&name).copied()) })?; // `(function n(n) {})()` @@ -466,7 +470,7 @@ impl<'a> SemanticBuilder<'a> { let flags = self.scoping.symbol_flags(symbol_id); if flags.intersects(excludes) { let symbol_span = self.scoping.symbol_span(symbol_id); - self.error(redeclaration(name, symbol_span, span)); + self.error(redeclaration(name.as_str(), symbol_span, span)); } } @@ -478,7 +482,7 @@ impl<'a> SemanticBuilder<'a> { /// # Panics pub(crate) fn declare_reference( &mut self, - name: Atom<'a>, + name: Ident<'a>, reference: Reference, ) -> ReferenceId { let reference_id = self.scoping.create_reference(reference); @@ -490,14 +494,14 @@ impl<'a> SemanticBuilder<'a> { /// Declares a `Symbol` for the node, shadowing previous declarations in the same scope. pub(crate) fn declare_shadow_symbol( &mut self, - name: &str, + name: Ident<'a>, span: Span, scope_id: ScopeId, includes: SymbolFlags, ) -> SymbolId { let symbol_id = self.scoping.create_symbol( span, - name, + name.as_str(), includes, self.current_scope_id, self.current_node_id, @@ -517,7 +521,7 @@ impl<'a> SemanticBuilder<'a> { // Try to resolve a reference. // If unresolved, transfer it to parent scope's unresolved references. let bindings = self.scoping.get_bindings(self.current_scope_id); - if let Some(symbol_id) = bindings.get(name.as_str()).copied() { + if let Some(symbol_id) = bindings.get(&name).copied() { let symbol_flags = self.scoping.symbol_flags(symbol_id); references.retain(|reference_id| { let reference_id = *reference_id; @@ -2518,7 +2522,7 @@ impl<'a> SemanticBuilder<'a> { fn reference_identifier(&mut self, ident: &IdentifierReference<'a>) { let flags = self.resolve_reference_usages(); let reference = Reference::new(self.current_node_id, self.current_scope_id, flags); - let reference_id = self.declare_reference(ident.name.into(), reference); + let reference_id = self.declare_reference(ident.name, reference); ident.reference_id.set(Some(reference_id)); } diff --git a/crates/oxc_semantic/src/checker/typescript.rs b/crates/oxc_semantic/src/checker/typescript.rs index 49b75bc4b157c..7a9b2190da3bd 100644 --- a/crates/oxc_semantic/src/checker/typescript.rs +++ b/crates/oxc_semantic/src/checker/typescript.rs @@ -1,11 +1,10 @@ use std::borrow::Cow; use itertools::Itertools; -use rustc_hash::FxHashMap; use oxc_ast::{AstKind, ast::*}; use oxc_ecmascript::BoundNames; -use oxc_span::{Atom, GetSpan, Span}; +use oxc_span::{GetSpan, IdentHashMap, Span}; use crate::{builder::SemanticBuilder, diagnostics}; @@ -91,9 +90,9 @@ pub fn check_formal_parameters(params: &FormalParameters, ctx: &SemanticBuilder< } fn check_duplicate_bound_names<'a, T: BoundNames<'a>>(bound_names: &T, ctx: &SemanticBuilder<'_>) { - let mut idents: FxHashMap, Span> = FxHashMap::default(); + let mut idents: IdentHashMap<'a, Span> = IdentHashMap::default(); bound_names.bound_names(&mut |ident| { - if let Some(old_span) = idents.insert(ident.name.into(), ident.span) { + if let Some(old_span) = idents.insert(ident.name, ident.span) { ctx.error(diagnostics::redeclaration(&ident.name, old_span, ident.span)); } }); diff --git a/crates/oxc_semantic/src/lib.rs b/crates/oxc_semantic/src/lib.rs index 4b7469feb9080..9b9de017c3d0d 100644 --- a/crates/oxc_semantic/src/lib.rs +++ b/crates/oxc_semantic/src/lib.rs @@ -215,7 +215,7 @@ impl<'a> Semantic<'a> { let AstKind::IdentifierReference(id) = reference_node.kind() else { return false; }; - self.scoping.root_unresolved_references().contains_key(id.name.as_str()) + self.scoping.root_unresolved_references().contains_key(&id.name) } /// Find which scope a symbol is declared in @@ -236,7 +236,7 @@ impl<'a> Semantic<'a> { } pub fn is_reference_to_global_variable(&self, ident: &IdentifierReference) -> bool { - self.scoping.root_unresolved_references().contains_key(ident.name.as_str()) + self.scoping.root_unresolved_references().contains_key(&ident.name) } pub fn reference_name(&self, reference: &Reference) -> &str { @@ -285,8 +285,10 @@ mod tests { let allocator = Allocator::default(); let semantic = get_semantic(&allocator, source, SourceType::default()); - let top_level_a = - semantic.scoping().get_binding(semantic.scoping().root_scope_id(), "a").unwrap(); + let top_level_a = semantic + .scoping() + .get_binding_by_name(semantic.scoping().root_scope_id(), "a") + .unwrap(); let decl = semantic.symbol_declaration(top_level_a); match decl.kind() { @@ -307,7 +309,7 @@ mod tests { let semantic = get_semantic(&allocator, source, SourceType::default()); let scopes = semantic.scoping(); - assert!(scopes.get_binding(scopes.root_scope_id(), "Fn").is_some()); + assert!(scopes.get_binding_by_name(scopes.root_scope_id(), "Fn").is_some()); } #[test] @@ -448,8 +450,10 @@ mod tests { for (source_type, source, flags) in sources { let semantic = get_semantic(&alloc, source, source_type); - let a_id = - semantic.scoping().get_root_binding(&target_symbol_name).unwrap_or_else(|| { + let a_id = semantic + .scoping() + .get_root_binding_by_name(target_symbol_name.as_str()) + .unwrap_or_else(|| { panic!("no references for '{target_symbol_name}' found"); }); let a_refs: Vec<_> = semantic.symbol_references(a_id).collect(); diff --git a/crates/oxc_semantic/src/scoping.rs b/crates/oxc_semantic/src/scoping.rs index 99837165076e9..6e948d279db0d 100644 --- a/crates/oxc_semantic/src/scoping.rs +++ b/crates/oxc_semantic/src/scoping.rs @@ -3,9 +3,9 @@ use std::{collections::hash_map::Entry, fmt, mem}; use rustc_hash::{FxHashMap, FxHashSet}; use self_cell::self_cell; -use oxc_allocator::{Allocator, CloneIn, FromIn, HashMap as ArenaHashMap, Vec as ArenaVec}; +use oxc_allocator::{Allocator, CloneIn, FromIn, Vec as ArenaVec}; use oxc_index::IndexVec; -use oxc_span::{Atom, Span}; +use oxc_span::{ArenaIdentHashMap, Atom, Ident, Span}; use oxc_syntax::{ node::NodeId, reference::{Reference, ReferenceId}, @@ -13,8 +13,8 @@ use oxc_syntax::{ symbol::{SymbolFlags, SymbolId}, }; -pub type Bindings<'a> = ArenaHashMap<'a, &'a str, SymbolId>; -pub type UnresolvedReferences<'a> = ArenaHashMap<'a, &'a str, ArenaVec<'a, ReferenceId>>; +pub type Bindings<'a> = ArenaIdentHashMap<'a, SymbolId>; +pub type UnresolvedReferences<'a> = ArenaIdentHashMap<'a, ArenaVec<'a, ReferenceId>>; #[derive(Clone, Debug)] pub struct Redeclaration { @@ -608,6 +608,28 @@ impl Scoping { &self.cell.borrow_dependent().root_unresolved_references } + /// Check if a name is in the root unresolved references. + /// + /// This is a convenience method that accepts `&str`. If you already have an `Ident`, + /// prefer using `root_unresolved_references().contains_key(&ident)` for better performance. + #[inline] + pub fn root_unresolved_references_contains_by_name(&self, name: &str) -> bool { + self.root_unresolved_references().contains_key(&Ident::from(name)) + } + + /// Get the reference IDs for an unresolved reference by name. + /// + /// This is a convenience method that accepts `&str`. If you already have an `Ident`, + /// prefer using `root_unresolved_references().get(&ident)` for better performance. + /// + /// Returns a `Vec` (copy) of the reference IDs since the lookup key is temporary. + #[inline] + pub fn get_root_unresolved_reference_by_name(&self, name: &str) -> Option> { + self.root_unresolved_references() + .get(&Ident::from(name)) + .map(|v| v.iter().copied().collect()) + } + pub fn root_unresolved_references_ids( &self, ) -> impl Iterator + '_> + '_ { @@ -616,11 +638,11 @@ impl Scoping { pub(crate) fn set_root_unresolved_references<'a>( &mut self, - entries: impl Iterator, + entries: impl Iterator, ReferenceIds)>, ) { self.cell.with_dependent_mut(|allocator, cell| { for (k, v) in entries { - let k = allocator.alloc_str(k); + let k = k.clone_in(allocator); let v = ArenaVec::from_iter_in(v, allocator); cell.root_unresolved_references.insert(k, v); } @@ -635,16 +657,17 @@ impl Scoping { /// # Panics /// Panics if there is no unresolved reference for provided `name` and `reference_id`. #[inline] - pub fn delete_root_unresolved_reference(&mut self, name: &str, reference_id: ReferenceId) { + pub fn delete_root_unresolved_reference(&mut self, name: Ident<'_>, reference_id: ReferenceId) { // It would be better to use `Entry` API to avoid 2 hash table lookups when deleting, - // but `map.entry` requires an owned key to be provided. Currently we use `CompactStr`s as keys - // which are not cheap to construct, so this is best we can do at present. - // TODO: Switch to `Entry` API once we use `&str`s or `Atom`s as keys. - self.cell.with_dependent_mut(|_allocator, cell| { - let reference_ids = cell.root_unresolved_references.get_mut(name).unwrap(); + // but `map.entry` requires an owned key to be provided. + let name_str = name.as_str(); + self.cell.with_dependent_mut(|allocator, cell| { + // Reconstruct Ident inside the closure with allocator lifetime + let name = Ident::from(allocator.alloc_str(name_str)); + let reference_ids = cell.root_unresolved_references.get_mut(&name).unwrap(); if reference_ids.len() == 1 { assert_eq!(reference_ids[0], reference_id); - cell.root_unresolved_references.remove(name); + cell.root_unresolved_references.remove(&name); } else { let index = reference_ids.iter().position(|&id| id == reference_id).unwrap(); reference_ids.swap_remove(index); @@ -686,13 +709,14 @@ impl Scoping { /// Get a variable binding by name that was declared in the top-level scope #[inline] - pub fn get_root_binding(&self, name: &str) -> Option { + pub fn get_root_binding(&self, name: &Ident) -> Option { self.get_binding(self.root_scope_id(), name) } - pub fn add_root_unresolved_reference(&mut self, name: &str, reference_id: ReferenceId) { + pub fn add_root_unresolved_reference(&mut self, name: Ident<'_>, reference_id: ReferenceId) { + let name_str = name.as_str(); self.cell.with_dependent_mut(|allocator, cell| { - let name = allocator.alloc_str(name); + let name = Ident::from(allocator.alloc_str(name_str)); cell.root_unresolved_references .entry(name) .or_insert_with(|| ArenaVec::new_in(allocator)) @@ -701,7 +725,7 @@ impl Scoping { } /// Check if a symbol is declared in a certain scope. - pub fn scope_has_binding(&self, scope_id: ScopeId, name: &str) -> bool { + pub fn scope_has_binding(&self, scope_id: ScopeId, name: &Ident) -> bool { self.cell.borrow_dependent().bindings[scope_id].contains_key(name) } @@ -713,7 +737,7 @@ impl Scoping { /// binding that might be declared in a parent scope, use [`find_binding`]. /// /// [`find_binding`]: Scoping::find_binding - pub fn get_binding(&self, scope_id: ScopeId, name: &str) -> Option { + pub fn get_binding(&self, scope_id: ScopeId, name: &Ident) -> Option { self.cell.borrow_dependent().bindings[scope_id].get(name).copied() } @@ -721,7 +745,7 @@ impl Scoping { /// /// Bindings are resolved by walking up the scope tree until a binding is /// found. If no binding is found, [`None`] is returned. - pub fn find_binding(&self, scope_id: ScopeId, name: &str) -> Option { + pub fn find_binding(&self, scope_id: ScopeId, name: &Ident) -> Option { for scope_id in self.scope_ancestors(scope_id) { if let Some(symbol_id) = self.get_binding(scope_id, name) { return Some(symbol_id); @@ -730,6 +754,43 @@ impl Scoping { None } + // ==================== Convenience methods for `&str` lookups ==================== + // These methods create an `Ident` from `&str` and use the fast-path methods above. + // Use these when you don't already have an `Ident` (e.g., in linter rules). + + /// Get a variable binding by name that was declared in the top-level scope. + /// + /// This is a convenience method that accepts `&str`. If you already have an `Ident`, + /// prefer using [`Self::get_root_binding`] for better performance. + #[inline] + pub fn get_root_binding_by_name(&self, name: &str) -> Option { + self.get_root_binding(&Ident::from(name)) + } + + /// Check if a symbol is declared in a certain scope. + /// + /// This is a convenience method that accepts `&str`. If you already have an `Ident`, + /// prefer using [`Self::scope_has_binding`] for better performance. + pub fn scope_has_binding_by_name(&self, scope_id: ScopeId, name: &str) -> bool { + self.scope_has_binding(scope_id, &Ident::from(name)) + } + + /// Get the symbol bound to an identifier name in a scope. + /// + /// This is a convenience method that accepts `&str`. If you already have an `Ident`, + /// prefer using [`Self::get_binding`] for better performance. + pub fn get_binding_by_name(&self, scope_id: ScopeId, name: &str) -> Option { + self.get_binding(scope_id, &Ident::from(name)) + } + + /// Find a binding by name in a scope or its ancestors. + /// + /// This is a convenience method that accepts `&str`. If you already have an `Ident`, + /// prefer using [`Self::find_binding`] for better performance. + pub fn find_binding_by_name(&self, scope_id: ScopeId, name: &str) -> Option { + self.find_binding(scope_id, &Ident::from(name)) + } + /// Get all bound identifiers in a scope. #[inline] pub fn get_bindings(&self, scope_id: ScopeId) -> &Bindings<'_> { @@ -759,14 +820,6 @@ impl Scoping { self.cell.borrow_dependent().bindings[scope_id].values().copied() } - #[inline] - pub(crate) fn insert_binding(&mut self, scope_id: ScopeId, name: &str, symbol_id: SymbolId) { - self.cell.with_dependent_mut(|allocator, cell| { - let name = allocator.alloc_str(name); - cell.bindings[scope_id].insert(name, symbol_id); - }); - } - /// Create a scope. #[inline] pub fn add_scope( @@ -786,25 +839,40 @@ impl Scoping { } /// Add a binding to a scope. - pub fn add_binding(&mut self, scope_id: ScopeId, name: &str, symbol_id: SymbolId) { + pub fn add_binding(&mut self, scope_id: ScopeId, name: Ident<'_>, symbol_id: SymbolId) { self.cell.with_dependent_mut(|allocator, cell| { - let name = allocator.alloc_str(name); + let name = name.clone_in(allocator); cell.bindings[scope_id].insert(name, symbol_id); }); } + /// Insert a binding directly into a scope (when name is already in the allocator). + #[inline] + pub(crate) fn insert_binding( + &mut self, + scope_id: ScopeId, + name: Ident<'_>, + symbol_id: SymbolId, + ) { + self.add_binding(scope_id, name, symbol_id); + } + /// Remove an existing binding from a scope. - pub fn remove_binding(&mut self, scope_id: ScopeId, name: &str) { - self.cell.with_dependent_mut(|_allocator, cell| { - cell.bindings[scope_id].remove(name); + pub fn remove_binding(&mut self, scope_id: ScopeId, name: Ident<'_>) { + let name_str = name.as_str(); + self.cell.with_dependent_mut(|allocator, cell| { + let name = Ident::from(allocator.alloc_str(name_str)); + cell.bindings[scope_id].remove(&name); }); } /// Move a binding from one scope to another. - pub fn move_binding(&mut self, from: ScopeId, to: ScopeId, name: &str) { - self.cell.with_dependent_mut(|_allocator, cell| { + pub fn move_binding(&mut self, from: ScopeId, to: ScopeId, name: Ident<'_>) { + let name_str = name.as_str(); + self.cell.with_dependent_mut(|allocator, cell| { + let name = Ident::from(allocator.alloc_str(name_str)); let from_map = &mut cell.bindings[from]; - if let Some((name, symbol_id)) = from_map.remove_entry(name) { + if let Some((name, symbol_id)) = from_map.remove_entry(&name) { cell.bindings[to].insert(name, symbol_id); } }); @@ -822,14 +890,17 @@ impl Scoping { &mut self, scope_id: ScopeId, symbol_id: SymbolId, - old_name: &str, - new_name: &str, + old_name: Ident<'_>, + new_name: Ident<'_>, ) { + let old_name_str = old_name.as_str(); + let new_name_str = new_name.as_str(); self.cell.with_dependent_mut(|allocator, cell| { let bindings = &mut cell.bindings[scope_id]; - let old_symbol_id = bindings.remove(old_name); + let old_name = Ident::from(allocator.alloc_str(old_name_str)); + let old_symbol_id = bindings.remove(&old_name); debug_assert_eq!(old_symbol_id, Some(symbol_id)); - let new_name = allocator.alloc_str(new_name); + let new_name = Ident::from(allocator.alloc_str(new_name_str)); let existing_symbol_id = bindings.insert(new_name, symbol_id); debug_assert!(existing_symbol_id.is_none()); }); @@ -842,18 +913,21 @@ impl Scoping { /// * No binding already exists in scope for `new_name`. /// /// Panics in debug mode if either of the above are not satisfied. - pub fn rename_symbol(&mut self, symbol_id: SymbolId, scope_id: ScopeId, new_name: &str) { + pub fn rename_symbol(&mut self, symbol_id: SymbolId, scope_id: ScopeId, new_name: Ident<'_>) { + let new_name_str = new_name.as_str(); self.cell.with_dependent_mut(|allocator, cell| { // Rename symbol - let new_name = Atom::from_in(new_name, allocator); - let old_name = mem::replace(&mut cell.symbol_names[symbol_id.index()], new_name); + let new_name_atom = Atom::from_in(new_name_str, allocator); + let old_name = mem::replace(&mut cell.symbol_names[symbol_id.index()], new_name_atom); // Rename binding, same as `Self::rename_binding`, we cannot call it directly // because the `old_name` borrowed `cell`. let bindings = &mut cell.bindings[scope_id]; - let old_symbol_id = bindings.remove(old_name.as_str()); + let old_name_ident = Ident::from(old_name.as_str()); + let old_symbol_id = bindings.remove(&old_name_ident); debug_assert_eq!(old_symbol_id, Some(symbol_id)); - let existing_symbol_id = bindings.insert(new_name.as_str(), symbol_id); + let new_name = Ident::from(allocator.alloc_str(new_name_str)); + let existing_symbol_id = bindings.insert(new_name, symbol_id); debug_assert!(existing_symbol_id.is_none()); }); } @@ -906,11 +980,24 @@ impl Scoping { bindings: cell .bindings .iter() - .map(|map| map.clone_in_with_semantic_ids(allocator)) + .map(|map| { + let mut new_map = Bindings::new_in(allocator); + for (k, v) in map { + new_map.insert(k.clone_in(allocator), *v); + } + new_map + }) .collect(), - root_unresolved_references: cell - .root_unresolved_references - .clone_in_with_semantic_ids(allocator), + root_unresolved_references: { + let mut new_map = UnresolvedReferences::new_in(allocator); + for (k, v) in &cell.root_unresolved_references { + new_map.insert( + k.clone_in(allocator), + v.clone_in_with_semantic_ids(allocator), + ); + } + new_map + }, }) }, } diff --git a/crates/oxc_semantic/src/unresolved_stack.rs b/crates/oxc_semantic/src/unresolved_stack.rs index fa79c9287110d..6e676a5ace98e 100644 --- a/crates/oxc_semantic/src/unresolved_stack.rs +++ b/crates/oxc_semantic/src/unresolved_stack.rs @@ -1,8 +1,7 @@ -use rustc_hash::FxHashMap; use smallvec::SmallVec; use oxc_data_structures::assert_unchecked; -use oxc_span::Atom; +use oxc_span::IdentHashMap; use oxc_syntax::reference::ReferenceId; /// Stores reference IDs for a single identifier name within a scope. @@ -10,9 +9,9 @@ use oxc_syntax::reference::ReferenceId; /// is referenced only a few times within a given scope. pub type ReferenceIds = SmallVec<[ReferenceId; 8]>; -/// Unlike `ScopeTree`'s `UnresolvedReferences`, this type uses `Atom` as the key, +/// Unlike `ScopeTree`'s `UnresolvedReferences`, this type uses `Ident` as the key, /// and uses a heap-allocated hashmap (not arena-allocated) -type TempUnresolvedReferences<'a> = FxHashMap, ReferenceIds>; +type TempUnresolvedReferences<'a> = IdentHashMap<'a, ReferenceIds>; // Stack used to accumulate unresolved refs while traversing scopes. // Indexed by scope depth. We recycle `UnresolvedReferences` instances during traversal diff --git a/crates/oxc_semantic/tests/integration/util/symbol_tester.rs b/crates/oxc_semantic/tests/integration/util/symbol_tester.rs index 3511d93b4c632..1bd654c4a79b2 100644 --- a/crates/oxc_semantic/tests/integration/util/symbol_tester.rs +++ b/crates/oxc_semantic/tests/integration/util/symbol_tester.rs @@ -45,7 +45,8 @@ impl<'a> SymbolTester<'a> { semantic: Semantic<'a>, target: &str, ) -> Self { - let decl = semantic.scoping().get_binding(semantic.scoping().root_scope_id(), target); + let decl = + semantic.scoping().get_binding_by_name(semantic.scoping().root_scope_id(), target); let data = decl.map_or_else( || Err(OxcDiagnostic::error(format!("Could not find declaration for {target}"))), Ok, @@ -67,7 +68,9 @@ impl<'a> SymbolTester<'a> { let mut symbols_with_target_name: Vec = semantic .scoping() .iter_bindings() - .filter_map(|(_, bindings)| bindings.get(target).copied()) + .filter_map(|(_, bindings)| { + bindings.iter().find(|(k, _)| k.as_str() == target).map(|(_, &v)| v) + }) .collect(); let data = match symbols_with_target_name.len() { diff --git a/crates/oxc_span/src/lib.rs b/crates/oxc_span/src/lib.rs index 74a7eb6fccddf..d6ce862f2deb9 100644 --- a/crates/oxc_span/src/lib.rs +++ b/crates/oxc_span/src/lib.rs @@ -10,8 +10,8 @@ mod span; pub use cmp::ContentEq; pub use oxc_str::{ - Atom, CompactStr, Ident, MAX_INLINE_LEN as ATOM_MAX_INLINE_LEN, format_atom, - format_compact_str, format_ident, + ArenaIdentHashMap, Atom, CompactStr, Ident, IdentHashMap, IdentHashSet, IdentHasher, + MAX_INLINE_LEN as ATOM_MAX_INLINE_LEN, format_atom, format_compact_str, format_ident, }; pub use source_type::{ FileExtension, Language, LanguageVariant, ModuleKind, SourceType, UnknownExtension, diff --git a/crates/oxc_str/src/ident.rs b/crates/oxc_str/src/ident.rs index a9aa368b46141..fcedf5e398ab8 100644 --- a/crates/oxc_str/src/ident.rs +++ b/crates/oxc_str/src/ident.rs @@ -529,6 +529,79 @@ macro_rules! format_ident { }} } +/// Hash map keyed by [`Ident`]. +/// +/// This uses [`IdentHasher`] which leverages the precomputed hash stored in `Ident`, +/// making hash map operations very fast. +#[expect(clippy::disallowed_types)] +pub type IdentHashMap<'a, V> = std::collections::HashMap, V, IdentHasher>; + +/// Arena-allocated hash map keyed by [`Ident`]. +/// +/// This uses [`IdentHasher`] which leverages the precomputed hash stored in `Ident`, +/// making hash map operations very fast, and stores entries in the arena allocator. +pub type ArenaIdentHashMap<'alloc, V> = + oxc_allocator::hash_map::HashMapImpl<'alloc, Ident<'alloc>, V, IdentHasher>; + +/// Hash set of [`Ident`]. +/// +/// This uses [`IdentHasher`] which leverages the precomputed hash stored in `Ident`, +/// making hash set operations very fast. +#[expect(clippy::disallowed_types)] +pub type IdentHashSet<'a> = std::collections::HashSet, IdentHasher>; + +/// Hasher for use in hash maps keyed by [`Ident`]. +/// +/// This hasher simply stores the precomputed hash from `Ident::hash()`, +/// making it essentially a no-op hasher for maximum performance. +#[derive(Debug)] +pub struct IdentHasher(u64); + +impl IdentHasher { + #[inline] + fn new() -> Self { + Self(0) + } +} + +impl hash::Hasher for IdentHasher { + #[inline] + fn write_u64(&mut self, n: u64) { + self.0 = n; + } + + #[inline] + fn write(&mut self, _: &[u8]) {} + + #[inline] + fn finish(&self) -> u64 { + self.0 + } +} + +impl hash::BuildHasher for IdentHasher { + type Hasher = Self; + + #[inline] + fn build_hasher(&self) -> Self::Hasher { + Self::new() + } +} + +impl Default for IdentHasher { + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl Clone for IdentHasher { + #[inline] + fn clone(&self) -> Self { + Self::new() + } +} + #[cfg(test)] mod test { use super::*; @@ -687,4 +760,21 @@ mod test { ); } } + + #[test] + fn ident_hashmap_lookup() { + let mut map: IdentHashMap = IdentHashMap::default(); + + // Insert with one Ident + let foo1 = Ident::new("foo"); + map.insert(foo1, 42); + + // Look up with a new Ident created from the same string + let foo2 = Ident::new("foo"); + assert_eq!(map.get(&foo2), Some(&42)); + + // Verify that a different key is not found + let bar = Ident::new("bar"); + assert_eq!(map.get(&bar), None); + } } diff --git a/crates/oxc_str/src/lib.rs b/crates/oxc_str/src/lib.rs index 540df449eb4e4..bf4b7f8f1547b 100644 --- a/crates/oxc_str/src/lib.rs +++ b/crates/oxc_str/src/lib.rs @@ -10,7 +10,7 @@ mod ident; pub use atom::Atom; pub use compact_str::{CompactStr, MAX_INLINE_LEN}; -pub use ident::Ident; +pub use ident::{ArenaIdentHashMap, Ident, IdentHashMap, IdentHashSet, IdentHasher}; #[doc(hidden)] pub mod __internal { diff --git a/crates/oxc_transformer/src/common/arrow_function_converter.rs b/crates/oxc_transformer/src/common/arrow_function_converter.rs index c3997c48106fd..c64457e9541da 100644 --- a/crates/oxc_transformer/src/common/arrow_function_converter.rs +++ b/crates/oxc_transformer/src/common/arrow_function_converter.rs @@ -96,7 +96,7 @@ use oxc_ast::{NONE, ast::*}; use oxc_ast_visit::{VisitMut, walk_mut::walk_expression}; use oxc_data_structures::stack::{NonEmptyStack, SparseStack}; use oxc_semantic::{ReferenceFlags, SymbolId}; -use oxc_span::{GetSpan, SPAN}; +use oxc_span::{GetSpan, Ident, SPAN}; use oxc_syntax::{ scope::{ScopeFlags, ScopeId}, symbol::SymbolFlags, @@ -879,7 +879,11 @@ impl<'a> ArrowFunctionConverter<'a> { let original_scope_id = ctx.scoping().symbol_scope_id(binding.symbol_id); if target_scope_id != original_scope_id { ctx.scoping_mut().set_symbol_scope_id(binding.symbol_id, target_scope_id); - ctx.scoping_mut().move_binding(original_scope_id, target_scope_id, &binding.name); + ctx.scoping_mut().move_binding( + original_scope_id, + target_scope_id, + Ident::from(binding.name.as_str()), + ); } } @@ -1023,7 +1027,7 @@ impl<'a> ArrowFunctionConverter<'a> { /// Rename the `arguments` symbol to a new name. fn rename_arguments_symbol(symbol_id: SymbolId, name: Atom<'a>, ctx: &mut TraverseCtx<'a>) { let scope_id = ctx.scoping().symbol_scope_id(symbol_id); - ctx.scoping_mut().rename_symbol(symbol_id, scope_id, name.as_str()); + ctx.scoping_mut().rename_symbol(symbol_id, scope_id, Ident::from(name.as_str())); } /// Transform the identifier reference for `arguments` if it's affected after transformation. @@ -1062,7 +1066,8 @@ impl<'a> ArrowFunctionConverter<'a> { if symbol_id.is_none() { let reference = ctx.scoping_mut().get_reference_mut(reference_id); reference.set_symbol_id(binding.symbol_id); - ctx.scoping_mut().delete_root_unresolved_reference(&ident.name, reference_id); + ctx.scoping_mut() + .delete_root_unresolved_reference(Ident::from(ident.name.as_str()), reference_id); ctx.scoping_mut().add_resolved_reference(binding.symbol_id, reference_id); } diff --git a/crates/oxc_transformer/src/common/helper_loader.rs b/crates/oxc_transformer/src/common/helper_loader.rs index 5abeec4ff5691..d24352bfc74a0 100644 --- a/crates/oxc_transformer/src/common/helper_loader.rs +++ b/crates/oxc_transformer/src/common/helper_loader.rs @@ -323,7 +323,7 @@ impl<'a> HelperLoaderStore<'a> { fn transform_for_external_helper(helper: Helper, ctx: &mut TraverseCtx<'a>) -> Expression<'a> { static HELPER_VAR: &str = "babelHelpers"; - let symbol_id = ctx.scoping().find_binding(ctx.current_scope_id(), HELPER_VAR); + let symbol_id = ctx.scoping().find_binding_by_name(ctx.current_scope_id(), HELPER_VAR); let object = ctx.create_ident_expr(SPAN, Atom::from(HELPER_VAR), symbol_id, ReferenceFlags::Read); let property = ctx.ast.identifier_name(SPAN, Atom::from(helper.name())); diff --git a/crates/oxc_transformer/src/common/module_imports.rs b/crates/oxc_transformer/src/common/module_imports.rs index 872480184eefb..0aa6703136dd0 100644 --- a/crates/oxc_transformer/src/common/module_imports.rs +++ b/crates/oxc_transformer/src/common/module_imports.rs @@ -182,7 +182,7 @@ impl<'a> ModuleImportsStore<'a> { return; } - let require_symbol_id = ctx.scoping().get_root_binding("require"); + let require_symbol_id = ctx.scoping().get_root_binding_by_name("require"); let stmts = imports .drain(..) .map(|(source, names)| Self::get_require(source, names, require_symbol_id, ctx)); diff --git a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs index a5b7c4b6abf4b..ee5da5b6f2953 100644 --- a/crates/oxc_transformer/src/es2016/exponentiation_operator.rs +++ b/crates/oxc_transformer/src/es2016/exponentiation_operator.rs @@ -537,7 +537,7 @@ impl<'a> ExponentiationOperator<'a, '_> { right: Expression<'a>, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { - let math_symbol_id = ctx.scoping().find_binding(ctx.current_scope_id(), "Math"); + let math_symbol_id = ctx.scoping().find_binding_by_name(ctx.current_scope_id(), "Math"); let object = ctx.create_ident_expr(SPAN, Atom::from("Math"), math_symbol_id, ReferenceFlags::Read); let property = ctx.ast.identifier_name(SPAN, "pow"); diff --git a/crates/oxc_transformer/src/es2017/async_to_generator.rs b/crates/oxc_transformer/src/es2017/async_to_generator.rs index 5fb8c27e0f7fe..297ba0a5b8c68 100644 --- a/crates/oxc_transformer/src/es2017/async_to_generator.rs +++ b/crates/oxc_transformer/src/es2017/async_to_generator.rs @@ -57,7 +57,7 @@ use oxc_allocator::{Box as ArenaBox, StringBuilder as ArenaStringBuilder, TakeIn use oxc_ast::{NONE, ast::*}; use oxc_ast_visit::Visit; use oxc_semantic::{ReferenceFlags, ScopeFlags, ScopeId, SymbolFlags}; -use oxc_span::{Atom, GetSpan, SPAN}; +use oxc_span::{Atom, GetSpan, Ident, SPAN}; use oxc_syntax::{ identifier::{is_identifier_name, is_identifier_part, is_identifier_start}, keyword::is_reserved_keyword, @@ -651,7 +651,7 @@ impl<'a, 'ctx> AsyncGeneratorExecutor<'a, 'ctx> { bound_ident: &BoundIdentifier<'a>, ctx: &mut TraverseCtx<'a>, ) -> Statement<'a> { - let symbol_id = ctx.scoping().find_binding(ctx.current_scope_id(), "arguments"); + let symbol_id = ctx.scoping().find_binding_by_name(ctx.current_scope_id(), "arguments"); let arguments_ident = Argument::from(ctx.create_ident_expr( SPAN, Atom::from("arguments"), @@ -889,7 +889,11 @@ impl<'a> Visit<'a> for BindingMover<'a, '_> { let symbol_id = ident.symbol_id(); let current_scope_id = symbols.symbol_scope_id(symbol_id); let scopes = self.ctx.scoping_mut(); - scopes.move_binding(current_scope_id, self.target_scope_id, ident.name.as_str()); + scopes.move_binding( + current_scope_id, + self.target_scope_id, + Ident::from(ident.name.as_str()), + ); let symbols = self.ctx.scoping_mut(); symbols.set_symbol_scope_id(symbol_id, self.target_scope_id); } diff --git a/crates/oxc_transformer/src/es2018/object_rest_spread.rs b/crates/oxc_transformer/src/es2018/object_rest_spread.rs index ab67a47af10c6..98e4c17f00b32 100644 --- a/crates/oxc_transformer/src/es2018/object_rest_spread.rs +++ b/crates/oxc_transformer/src/es2018/object_rest_spread.rs @@ -635,7 +635,7 @@ impl<'a> ObjectRestSpread<'a, '_> { // Move the bindings from the for init scope to scope of the loop body. for ident in bound_names { ctx.scoping_mut().set_symbol_scope_id(ident.symbol_id(), new_scope_id); - ctx.scoping_mut().move_binding(scope_id, new_scope_id, ident.name.into()); + ctx.scoping_mut().move_binding(scope_id, new_scope_id, ident.name); } } } diff --git a/crates/oxc_transformer/src/es2022/class_properties/class.rs b/crates/oxc_transformer/src/es2022/class_properties/class.rs index 6b70772885278..58964821a9263 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/class.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/class.rs @@ -888,15 +888,16 @@ fn create_new_weakmap<'a>( symbol_id: &mut Option>, ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { - let symbol_id = *symbol_id - .get_or_insert_with(|| ctx.scoping().find_binding(ctx.current_scope_id(), "WeakMap")); + let symbol_id = *symbol_id.get_or_insert_with(|| { + ctx.scoping().find_binding_by_name(ctx.current_scope_id(), "WeakMap") + }); let ident = ctx.create_ident_expr(SPAN, Atom::from("WeakMap"), symbol_id, ReferenceFlags::Read); ctx.ast.expression_new_with_pure(SPAN, ident, NONE, ctx.ast.vec(), true) } /// Create `new WeakSet()` expression. fn create_new_weakset<'a>(ctx: &mut TraverseCtx<'a>) -> Expression<'a> { - let symbol_id = ctx.scoping().find_binding(ctx.current_scope_id(), "WeakSet"); + let symbol_id = ctx.scoping().find_binding_by_name(ctx.current_scope_id(), "WeakSet"); let ident = ctx.create_ident_expr(SPAN, Atom::from("WeakSet"), symbol_id, ReferenceFlags::Read); ctx.ast.expression_new_with_pure(SPAN, ident, NONE, ctx.ast.vec(), true) } diff --git a/crates/oxc_transformer/src/es2022/class_properties/constructor.rs b/crates/oxc_transformer/src/es2022/class_properties/constructor.rs index 6e4305f4cc4d4..704e1f8484326 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/constructor.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/constructor.rs @@ -106,7 +106,7 @@ use rustc_hash::FxHashMap; use oxc_ast::{NONE, ast::*}; use oxc_ast_visit::{VisitMut, walk_mut}; -use oxc_span::SPAN; +use oxc_span::{Ident, SPAN}; use oxc_syntax::{ node::NodeId, scope::{ScopeFlags, ScopeId}, @@ -430,7 +430,11 @@ impl<'a> ClassProperties<'a, '_> { // Save replacement name in `clashing_symbols` *name = new_name; // Rename symbol and binding - ctx.scoping_mut().rename_symbol(symbol_id, constructor_scope_id, new_name.as_str()); + ctx.scoping_mut().rename_symbol( + symbol_id, + constructor_scope_id, + Ident::from(new_name.as_str()), + ); } // Rename identifiers for clashing symbols in constructor params and body diff --git a/crates/oxc_transformer/src/es2022/class_properties/prop_decl.rs b/crates/oxc_transformer/src/es2022/class_properties/prop_decl.rs index 40cda6394c5f1..b11a0b9daf733 100644 --- a/crates/oxc_transformer/src/es2022/class_properties/prop_decl.rs +++ b/crates/oxc_transformer/src/es2022/class_properties/prop_decl.rs @@ -341,7 +341,7 @@ impl<'a> ClassProperties<'a, '_> { ctx: &mut TraverseCtx<'a>, ) -> Expression<'a> { // `Object.defineProperty` - let object_symbol_id = ctx.scoping().find_binding(ctx.current_scope_id(), "Object"); + let object_symbol_id = ctx.scoping().find_binding_by_name(ctx.current_scope_id(), "Object"); let object = ctx.create_ident_expr( SPAN, Atom::from("Object"), diff --git a/crates/oxc_transformer/src/es2026/explicit_resource_management.rs b/crates/oxc_transformer/src/es2026/explicit_resource_management.rs index 19c8b38ed8a11..ff2e839bee72f 100644 --- a/crates/oxc_transformer/src/es2026/explicit_resource_management.rs +++ b/crates/oxc_transformer/src/es2026/explicit_resource_management.rs @@ -41,7 +41,7 @@ use oxc_allocator::{Address, Box as ArenaBox, GetAddress, TakeIn, Vec as ArenaVe use oxc_ast::{NONE, ast::*}; use oxc_ecmascript::BoundNames; use oxc_semantic::{ScopeFlags, ScopeId, SymbolFlags}; -use oxc_span::{Atom, SPAN}; +use oxc_span::{Atom, Ident, SPAN}; use oxc_traverse::{BoundIdentifier, Traverse}; use crate::{ @@ -127,7 +127,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for ExplicitResourceManagement<'a, '_> ), }; ctx.scoping_mut().set_symbol_scope_id(for_of_init_symbol_id, scope_id); - ctx.scoping_mut().move_binding(for_of_stmt_scope_id, scope_id, &for_of_init_name); + ctx.scoping_mut().move_binding(for_of_stmt_scope_id, scope_id, for_of_init_name); if let Statement::BlockStatement(body) = &mut for_of_stmt.body { // `for (const _x of y) { x(); }` -> `for (const _x of y) { using x = _x; x(); }` @@ -173,7 +173,11 @@ impl<'a> Traverse<'a, TransformState<'a>> for ExplicitResourceManagement<'a, '_> ); ctx.scoping_mut().set_symbol_scope_id(using_ctx.symbol_id, static_block_new_scope_id); - ctx.scoping_mut().move_binding(scope_id, static_block_new_scope_id, &using_ctx.name); + ctx.scoping_mut().move_binding( + scope_id, + static_block_new_scope_id, + Ident::from(using_ctx.name.as_str()), + ); *ctx.scoping_mut().scope_flags_mut(scope_id) = ScopeFlags::StrictMode; block.set_scope_id(static_block_new_scope_id); @@ -288,7 +292,11 @@ impl<'a> Traverse<'a, TransformState<'a>> for ExplicitResourceManagement<'a, '_> let current_hoist_scope_id = ctx.current_hoist_scope_id(); node.block.set_scope_id(block_stmt_scope_id); ctx.scoping_mut().set_symbol_scope_id(using_ctx.symbol_id, current_hoist_scope_id); - ctx.scoping_mut().move_binding(scope_id, current_hoist_scope_id, &using_ctx.name); + ctx.scoping_mut().move_binding( + scope_id, + current_hoist_scope_id, + Ident::from(using_ctx.name.as_str()), + ); ctx.scoping_mut().change_scope_parent_id(scope_id, Some(block_stmt_scope_id)); } diff --git a/crates/oxc_transformer/src/jsx/refresh.rs b/crates/oxc_transformer/src/jsx/refresh.rs index 556b2c89dc239..d8845d3993eae 100644 --- a/crates/oxc_transformer/src/jsx/refresh.rs +++ b/crates/oxc_transformer/src/jsx/refresh.rs @@ -351,7 +351,7 @@ impl<'a> Traverse<'a, TransformState<'a>> for ReactRefresh<'a, '_> { if let Some(binding_name) = binding_name { self.non_builtin_hooks_callee.entry(current_scope_id).or_default().push( ctx.scoping() - .find_binding( + .find_binding_by_name( ctx.scoping().scope_parent_id(ctx.current_scope_id()).unwrap(), binding_name.as_str(), ) diff --git a/crates/oxc_transformer/src/regexp/mod.rs b/crates/oxc_transformer/src/regexp/mod.rs index 3d8875d44c8de..4519452d34d3c 100644 --- a/crates/oxc_transformer/src/regexp/mod.rs +++ b/crates/oxc_transformer/src/regexp/mod.rs @@ -163,7 +163,7 @@ impl<'a> RegExp<'a, '_> { } let callee = { - let symbol_id = ctx.scoping().find_binding(ctx.current_scope_id(), "RegExp"); + let symbol_id = ctx.scoping().find_binding_by_name(ctx.current_scope_id(), "RegExp"); ctx.create_ident_expr(SPAN, Atom::from("RegExp"), symbol_id, ReferenceFlags::read()) }; diff --git a/crates/oxc_transformer/src/typescript/enum.rs b/crates/oxc_transformer/src/typescript/enum.rs index 4ca0c7b094891..c2b2ae75ded07 100644 --- a/crates/oxc_transformer/src/typescript/enum.rs +++ b/crates/oxc_transformer/src/typescript/enum.rs @@ -328,7 +328,8 @@ impl<'a> TypeScriptEnum<'a> { // Infinity let expr = if value.is_infinite() { - let infinity_symbol_id = ctx.scoping().find_binding(ctx.current_scope_id(), "Infinity"); + let infinity_symbol_id = + ctx.scoping().find_binding_by_name(ctx.current_scope_id(), "Infinity"); ctx.create_ident_expr( SPAN, Atom::from("Infinity"), diff --git a/crates/oxc_transformer/src/typescript/module.rs b/crates/oxc_transformer/src/typescript/module.rs index 50cbb38e35d58..43e78ddd9a6c0 100644 --- a/crates/oxc_transformer/src/typescript/module.rs +++ b/crates/oxc_transformer/src/typescript/module.rs @@ -109,7 +109,7 @@ impl<'a> TypeScriptModule<'a, '_> { { // No value reference, we will remove this declaration in `TypeScriptAnnotations` let scope_id = ctx.current_scope_id(); - ctx.scoping_mut().remove_binding(scope_id, &decl.id.name); + ctx.scoping_mut().remove_binding(scope_id, decl.id.name); return None; } @@ -136,7 +136,7 @@ impl<'a> TypeScriptModule<'a, '_> { } let require_symbol_id = - ctx.scoping().find_binding(ctx.current_scope_id(), "require"); + ctx.scoping().find_binding_by_name(ctx.current_scope_id(), "require"); let callee = ctx.create_ident_expr( SPAN, Atom::from("require"), diff --git a/crates/oxc_transformer_plugins/src/inject_global_variables.rs b/crates/oxc_transformer_plugins/src/inject_global_variables.rs index 8458a3dc37913..08aab39032223 100644 --- a/crates/oxc_transformer_plugins/src/inject_global_variables.rs +++ b/crates/oxc_transformer_plugins/src/inject_global_variables.rs @@ -190,9 +190,9 @@ impl<'a> InjectGlobalVariables<'a> { if self.replaced_dot_defines.iter().any(|d| d.0 == i.specifier.local()) { false } else { - scoping - .root_unresolved_references() - .contains_key(i.specifier.local().as_str()) + scoping.root_unresolved_references_contains_by_name( + i.specifier.local().as_str(), + ) } } } diff --git a/crates/oxc_transformer_plugins/src/module_runner_transform.rs b/crates/oxc_transformer_plugins/src/module_runner_transform.rs index d7080e670549a..2066ad9224ce0 100644 --- a/crates/oxc_transformer_plugins/src/module_runner_transform.rs +++ b/crates/oxc_transformer_plugins/src/module_runner_transform.rs @@ -624,7 +624,7 @@ impl<'a> ModuleRunnerTransform<'a> { let BindingIdentifier { name, symbol_id, .. } = ident; let scopes = ctx.scoping_mut(); - scopes.remove_binding(scopes.root_scope_id(), &name); + scopes.remove_binding(scopes.root_scope_id(), name); let symbol_id = symbol_id.get().unwrap(); // Do not need to insert if there no identifiers that point to this symbol diff --git a/crates/oxc_traverse/src/context/scoping.rs b/crates/oxc_traverse/src/context/scoping.rs index f5ce6c9400a90..f87ec7cd90174 100644 --- a/crates/oxc_traverse/src/context/scoping.rs +++ b/crates/oxc_traverse/src/context/scoping.rs @@ -4,7 +4,7 @@ use oxc_allocator::{Allocator, Vec as ArenaVec}; use oxc_ast::ast::*; use oxc_ast_visit::Visit; use oxc_semantic::{NodeId, Reference, Scoping}; -use oxc_span::SPAN; +use oxc_span::{Ident, SPAN}; use oxc_syntax::{ reference::{ReferenceFlags, ReferenceId}, scope::{ScopeFlags, ScopeId}, @@ -237,7 +237,7 @@ impl<'a> TraverseScoping<'a> { flags: SymbolFlags, ) -> SymbolId { let symbol_id = self.scoping.create_symbol(SPAN, name, flags, scope_id, NodeId::DUMMY); - self.scoping.add_binding(scope_id, name, symbol_id); + self.scoping.add_binding(scope_id, Ident::from(name), symbol_id); symbol_id } @@ -300,7 +300,7 @@ impl<'a> TraverseScoping<'a> { pub fn create_unbound_reference(&mut self, name: &str, flags: ReferenceFlags) -> ReferenceId { let reference = Reference::new(NodeId::DUMMY, self.current_scope_id, flags); let reference_id = self.scoping.create_reference(reference); - self.scoping.add_root_unresolved_reference(name, reference_id); + self.scoping.add_root_unresolved_reference(Ident::from(name), reference_id); reference_id } @@ -327,7 +327,7 @@ impl<'a> TraverseScoping<'a> { name: &str, flags: ReferenceFlags, ) -> ReferenceId { - let symbol_id = self.scoping.find_binding(self.current_scope_id, name); + let symbol_id = self.scoping.find_binding_by_name(self.current_scope_id, name); self.create_reference(name, symbol_id, flags) } @@ -339,7 +339,7 @@ impl<'a> TraverseScoping<'a> { if let Some(symbol_id) = symbol_id { self.scoping.delete_resolved_reference(symbol_id, reference_id); } else { - self.scoping.delete_root_unresolved_reference(name, reference_id); + self.scoping.delete_root_unresolved_reference(Ident::from(name), reference_id); } } diff --git a/crates/oxc_traverse/src/context/uid.rs b/crates/oxc_traverse/src/context/uid.rs index b4ee17b137253..27f39d6609d0c 100644 --- a/crates/oxc_traverse/src/context/uid.rs +++ b/crates/oxc_traverse/src/context/uid.rs @@ -143,8 +143,8 @@ impl<'a> UidGenerator<'a> { for name in scoping.symbol_names() { generator.add(name); } - for &name in scoping.root_unresolved_references().keys() { - generator.add(name); + for name in scoping.root_unresolved_references().keys() { + generator.add(name.as_str()); } generator diff --git a/tasks/coverage/snapshots/semantic_misc.snap b/tasks/coverage/snapshots/semantic_misc.snap index 540d62071e3bb..aed09cafe47be 100644 --- a/tasks/coverage/snapshots/semantic_misc.snap +++ b/tasks/coverage/snapshots/semantic_misc.snap @@ -91,12 +91,12 @@ rebuilt : SymbolId(164): Span { start: 0, end: 0 } Symbol span mismatch for "Inner4": after transform: SymbolId(195): Span { start: 0, end: 0 } rebuilt : SymbolId(165): Span { start: 12166, end: 12172 } -Unresolved reference IDs mismatch for "Object": -after transform: [ReferenceId(67), ReferenceId(71), ReferenceId(75), ReferenceId(79), ReferenceId(105), ReferenceId(107), ReferenceId(119), ReferenceId(121), ReferenceId(133), ReferenceId(135), ReferenceId(147), ReferenceId(149), ReferenceId(161), ReferenceId(165), ReferenceId(181), ReferenceId(183), ReferenceId(195), ReferenceId(199), ReferenceId(215), ReferenceId(217), ReferenceId(229), ReferenceId(233), ReferenceId(249), ReferenceId(251), ReferenceId(263), ReferenceId(267), ReferenceId(283), ReferenceId(285), ReferenceId(394), ReferenceId(398), ReferenceId(414), ReferenceId(418), ReferenceId(422), ReferenceId(426)] -rebuilt : [ReferenceId(68), ReferenceId(72), ReferenceId(76), ReferenceId(80), ReferenceId(118), ReferenceId(122), ReferenceId(144), ReferenceId(148), ReferenceId(170), ReferenceId(174), ReferenceId(263), ReferenceId(270), ReferenceId(336), ReferenceId(340), ReferenceId(356), ReferenceId(360), ReferenceId(364), ReferenceId(368)] Unresolved reference IDs mismatch for "Function": after transform: [ReferenceId(83), ReferenceId(89), ReferenceId(109), ReferenceId(123), ReferenceId(137), ReferenceId(151), ReferenceId(169), ReferenceId(185), ReferenceId(203), ReferenceId(219), ReferenceId(237), ReferenceId(253), ReferenceId(271), ReferenceId(287), ReferenceId(402), ReferenceId(430), ReferenceId(436), ReferenceId(444), ReferenceId(448), ReferenceId(452), ReferenceId(458), ReferenceId(462), ReferenceId(466), ReferenceId(472), ReferenceId(476), ReferenceId(480)] rebuilt : [ReferenceId(84), ReferenceId(90), ReferenceId(126), ReferenceId(152), ReferenceId(178), ReferenceId(277), ReferenceId(344), ReferenceId(372), ReferenceId(378), ReferenceId(385), ReferenceId(391), ReferenceId(398)] +Unresolved reference IDs mismatch for "Object": +after transform: [ReferenceId(67), ReferenceId(71), ReferenceId(75), ReferenceId(79), ReferenceId(105), ReferenceId(107), ReferenceId(119), ReferenceId(121), ReferenceId(133), ReferenceId(135), ReferenceId(147), ReferenceId(149), ReferenceId(161), ReferenceId(165), ReferenceId(181), ReferenceId(183), ReferenceId(195), ReferenceId(199), ReferenceId(215), ReferenceId(217), ReferenceId(229), ReferenceId(233), ReferenceId(249), ReferenceId(251), ReferenceId(263), ReferenceId(267), ReferenceId(283), ReferenceId(285), ReferenceId(394), ReferenceId(398), ReferenceId(414), ReferenceId(418), ReferenceId(422), ReferenceId(426)] +rebuilt : [ReferenceId(68), ReferenceId(72), ReferenceId(76), ReferenceId(80), ReferenceId(118), ReferenceId(122), ReferenceId(144), ReferenceId(148), ReferenceId(170), ReferenceId(174), ReferenceId(263), ReferenceId(270), ReferenceId(336), ReferenceId(340), ReferenceId(356), ReferenceId(360), ReferenceId(364), ReferenceId(368)] semantic Error: tasks/coverage/misc/pass/oxc-13284.ts Symbol reference IDs mismatch for "_super": diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index 14278d6237b45..b209ddbeec14c 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -10613,15 +10613,15 @@ rebuilt : ["FinalizationRegistry", "Object", "Set", "Symbol", "WeakMap", Unresolved reference IDs mismatch for "WeakRef": after transform: [ReferenceId(0), ReferenceId(2), ReferenceId(11), ReferenceId(15), ReferenceId(33)] rebuilt : [ReferenceId(26)] -Unresolved reference IDs mismatch for "Set": -after transform: [ReferenceId(1), ReferenceId(14)] -rebuilt : [ReferenceId(10)] Unresolved reference IDs mismatch for "Symbol": after transform: [ReferenceId(8), ReferenceId(55), ReferenceId(69), ReferenceId(72)] rebuilt : [ReferenceId(81), ReferenceId(84)] Unresolved reference IDs mismatch for "WeakMap": after transform: [ReferenceId(5), ReferenceId(9), ReferenceId(108), ReferenceId(109), ReferenceId(110)] rebuilt : [ReferenceId(2), ReferenceId(3), ReferenceId(4), ReferenceId(7)] +Unresolved reference IDs mismatch for "Set": +after transform: [ReferenceId(1), ReferenceId(14)] +rebuilt : [ReferenceId(10)] semantic Error: tasks/coverage/typescript/tests/cases/compiler/eventEmitterPatternWithRecordOfFunction.ts Unresolved references mismatch: @@ -23829,21 +23829,21 @@ after transform: ["Iterable"] rebuilt : [] semantic Error: tasks/coverage/typescript/tests/cases/compiler/typeGuardConstructorPrimitiveTypes.ts -Unresolved reference IDs mismatch for "BigInt": -after transform: [ReferenceId(16), ReferenceId(22), ReferenceId(36)] -rebuilt : [ReferenceId(16), ReferenceId(31)] Unresolved reference IDs mismatch for "Number": after transform: [ReferenceId(4), ReferenceId(19), ReferenceId(27)] rebuilt : [ReferenceId(4), ReferenceId(22)] -Unresolved reference IDs mismatch for "String": -after transform: [ReferenceId(1), ReferenceId(18), ReferenceId(24)] -rebuilt : [ReferenceId(1), ReferenceId(19)] Unresolved reference IDs mismatch for "Symbol": after transform: [ReferenceId(13), ReferenceId(21), ReferenceId(33)] rebuilt : [ReferenceId(13), ReferenceId(28)] +Unresolved reference IDs mismatch for "BigInt": +after transform: [ReferenceId(16), ReferenceId(22), ReferenceId(36)] +rebuilt : [ReferenceId(16), ReferenceId(31)] Unresolved reference IDs mismatch for "Boolean": after transform: [ReferenceId(7), ReferenceId(20), ReferenceId(30)] rebuilt : [ReferenceId(7), ReferenceId(25)] +Unresolved reference IDs mismatch for "String": +after transform: [ReferenceId(1), ReferenceId(18), ReferenceId(24)] +rebuilt : [ReferenceId(1), ReferenceId(19)] semantic Error: tasks/coverage/typescript/tests/cases/compiler/typeGuardNarrowByMutableUntypedField.ts Bindings mismatch: @@ -24938,36 +24938,36 @@ after transform: SymbolId(0): [ReferenceId(0), ReferenceId(1)] rebuilt : SymbolId(0): [ReferenceId(1)] semantic Error: tasks/coverage/typescript/tests/cases/compiler/valueOfTypedArray.ts -Unresolved reference IDs mismatch for "Uint16Array": -after transform: [ReferenceId(6), ReferenceId(7)] -rebuilt : [ReferenceId(3)] -Unresolved reference IDs mismatch for "BigInt64Array": -after transform: [ReferenceId(16), ReferenceId(17)] -rebuilt : [ReferenceId(8)] -Unresolved reference IDs mismatch for "Int8Array": -after transform: [ReferenceId(0), ReferenceId(1)] -rebuilt : [ReferenceId(0)] -Unresolved reference IDs mismatch for "Float32Array": -after transform: [ReferenceId(12), ReferenceId(13)] -rebuilt : [ReferenceId(6)] -Unresolved reference IDs mismatch for "Uint8Array": -after transform: [ReferenceId(2), ReferenceId(3)] -rebuilt : [ReferenceId(1)] Unresolved reference IDs mismatch for "Int16Array": after transform: [ReferenceId(4), ReferenceId(5)] rebuilt : [ReferenceId(2)] +Unresolved reference IDs mismatch for "Int8Array": +after transform: [ReferenceId(0), ReferenceId(1)] +rebuilt : [ReferenceId(0)] +Unresolved reference IDs mismatch for "Uint16Array": +after transform: [ReferenceId(6), ReferenceId(7)] +rebuilt : [ReferenceId(3)] Unresolved reference IDs mismatch for "Float64Array": after transform: [ReferenceId(14), ReferenceId(15)] rebuilt : [ReferenceId(7)] -Unresolved reference IDs mismatch for "BigUint64Array": -after transform: [ReferenceId(18), ReferenceId(19)] -rebuilt : [ReferenceId(9)] +Unresolved reference IDs mismatch for "BigInt64Array": +after transform: [ReferenceId(16), ReferenceId(17)] +rebuilt : [ReferenceId(8)] Unresolved reference IDs mismatch for "Int32Array": after transform: [ReferenceId(8), ReferenceId(9)] rebuilt : [ReferenceId(4)] Unresolved reference IDs mismatch for "Uint32Array": after transform: [ReferenceId(10), ReferenceId(11)] rebuilt : [ReferenceId(5)] +Unresolved reference IDs mismatch for "Float32Array": +after transform: [ReferenceId(12), ReferenceId(13)] +rebuilt : [ReferenceId(6)] +Unresolved reference IDs mismatch for "BigUint64Array": +after transform: [ReferenceId(18), ReferenceId(19)] +rebuilt : [ReferenceId(9)] +Unresolved reference IDs mismatch for "Uint8Array": +after transform: [ReferenceId(2), ReferenceId(3)] +rebuilt : [ReferenceId(1)] semantic Error: tasks/coverage/typescript/tests/cases/compiler/vardecl.ts Namespaces exporting non-const are not supported by Babel. Change to const or see: https://babeljs.io/docs/en/babel-plugin-transform-typescript