diff --git a/crates/oxc_linter/src/rules/valid_typeof.rs b/crates/oxc_linter/src/rules/valid_typeof.rs index 620aa0dfb..eacf7cda6 100644 --- a/crates/oxc_linter/src/rules/valid_typeof.rs +++ b/crates/oxc_linter/src/rules/valid_typeof.rs @@ -93,7 +93,7 @@ impl Rule for ValidTypeof { } if sibling.is_undefined() - && ctx.symbols().get_resolved_reference(sibling_id.into()).is_none() + && ctx.semantic().is_unresolved_reference(sibling_id.into()) { ctx.diagnostic_with_fix( if self.require_string_literals { diff --git a/crates/oxc_semantic/src/lib.rs b/crates/oxc_semantic/src/lib.rs index a90e53c33..2517102a7 100644 --- a/crates/oxc_semantic/src/lib.rs +++ b/crates/oxc_semantic/src/lib.rs @@ -11,8 +11,9 @@ mod symbol; use std::rc::Rc; pub use builder::SemanticBuilder; +use node::AstNodeId; pub use node::{AstNode, AstNodes, SemanticNode}; -use oxc_ast::{module_record::ModuleRecord, SourceType, Trivias}; +use oxc_ast::{module_record::ModuleRecord, AstKind, SourceType, Trivias}; pub use scope::{Scope, ScopeFlags, ScopeTree}; pub use symbol::{Reference, ResolvedReference, Symbol, SymbolFlags, SymbolTable}; @@ -67,4 +68,12 @@ impl<'a> Semantic<'a> { pub fn symbols(&self) -> &SymbolTable { &self.symbols } + + #[must_use] + pub fn is_unresolved_reference(&self, node_id: AstNodeId) -> bool { + let reference_node = &self.nodes()[node_id]; + let AstKind::IdentifierReference(id) = reference_node.kind() else { return false; }; + let scope = &self.scopes()[reference_node.scope_id()]; + scope.unresolved_references.contains_key(&id.name) + } } diff --git a/crates/oxc_semantic/src/scope/builder.rs b/crates/oxc_semantic/src/scope/builder.rs index d00892ad9..5afc39d65 100644 --- a/crates/oxc_semantic/src/scope/builder.rs +++ b/crates/oxc_semantic/src/scope/builder.rs @@ -73,11 +73,7 @@ impl ScopeBuilder { let scope = &self.scopes[scope]; if let Some(symbol_id) = scope.get().get_variable_symbol_id(&variable) { // We have resolved this reference. - let symbol = &mut symbol_table[symbol_id]; - symbol.add_references(&reference); - for r in reference { - symbol_table.resolve_reference(r.ast_node_id, r.resolve_to(symbol_id)); - } + symbol_table.resolve_reference(reference, symbol_id); continue 'outer; } } diff --git a/crates/oxc_semantic/src/symbol/mod.rs b/crates/oxc_semantic/src/symbol/mod.rs index dc53c07f9..de28b3daa 100644 --- a/crates/oxc_semantic/src/symbol/mod.rs +++ b/crates/oxc_semantic/src/symbol/mod.rs @@ -8,6 +8,7 @@ mod table; use bitflags::bitflags; use oxc_ast::{Atom, Span}; +use self::reference::ResolvedReferenceId; pub use self::{ id::SymbolId, reference::{Reference, ReferenceFlag, ResolvedReference}, @@ -24,7 +25,7 @@ pub struct Symbol { span: Span, flags: SymbolFlags, /// Pointers to the AST Nodes that reference this symbol - references: Vec, + references: Vec, } #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] @@ -119,12 +120,8 @@ impl Symbol { self.flags.contains(SymbolFlags::Export) } - pub fn add_references(&mut self, new_references: &[Reference]) { - self.references.extend(new_references.iter().map(|r| r.ast_node_id)); - } - #[must_use] - pub fn references(&self) -> &[AstNodeId] { + pub fn references(&self) -> &[ResolvedReferenceId] { &self.references } diff --git a/crates/oxc_semantic/src/symbol/reference.rs b/crates/oxc_semantic/src/symbol/reference.rs index b3c681e8d..51e840c0e 100644 --- a/crates/oxc_semantic/src/symbol/reference.rs +++ b/crates/oxc_semantic/src/symbol/reference.rs @@ -1,5 +1,7 @@ #![allow(non_upper_case_globals)] +use std::num::NonZeroUsize; + use bitflags::bitflags; use oxc_ast::Span; @@ -83,3 +85,24 @@ impl ResolvedReference { self.reference.span } } + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct ResolvedReferenceId(NonZeroUsize); + +impl Default for ResolvedReferenceId { + fn default() -> Self { + Self::new(1) + } +} + +impl ResolvedReferenceId { + #[must_use] + pub fn new(n: usize) -> Self { + unsafe { Self(NonZeroUsize::new_unchecked(n)) } + } + + #[must_use] + pub(crate) fn index0(self) -> usize { + self.0.get() - 1 + } +} diff --git a/crates/oxc_semantic/src/symbol/table.rs b/crates/oxc_semantic/src/symbol/table.rs index b0461869d..2624d5249 100644 --- a/crates/oxc_semantic/src/symbol/table.rs +++ b/crates/oxc_semantic/src/symbol/table.rs @@ -1,16 +1,23 @@ use std::ops::{Deref, Index, IndexMut}; use oxc_ast::{Atom, Span}; -use rustc_hash::FxHashMap; +use super::reference::ResolvedReferenceId; use super::{Symbol, SymbolFlags, SymbolId}; use crate::node::AstNodeId; -use crate::ResolvedReference; +use crate::{Reference, ResolvedReference}; +/// `SymbolTable` is a storage of all the symbols (related to `BindingIdentifiers`) +/// and references (related to `IdentifierReferences`) of the program. It supports two +/// kinds of queries: indexing by `SymbolId` retrieves the corresponding `Symbol` and +/// indexing by `ResolvedReferenceId` retrieves the correspodning `ResolvedReference` +/// #[derive(Debug, Default)] pub struct SymbolTable { + /// Stores all the `Symbols` indexed by `SymbolId` symbols: Vec, - resolved_references: FxHashMap, + /// Stores all the resolved references indexed by `ResolvedReferenceId` + resolved_references: Vec, } impl Index for SymbolTable { @@ -27,6 +34,20 @@ impl IndexMut for SymbolTable { } } +impl Index for SymbolTable { + type Output = ResolvedReference; + + fn index(&self, index: ResolvedReferenceId) -> &Self::Output { + &self.resolved_references[index.index0()] + } +} + +impl IndexMut for SymbolTable { + fn index_mut(&mut self, index: ResolvedReferenceId) -> &mut Self::Output { + &mut self.resolved_references[index.index0()] + } +} + impl Deref for SymbolTable { type Target = Vec; @@ -42,12 +63,12 @@ impl SymbolTable { } #[must_use] - pub fn get(&self, id: SymbolId) -> Option<&Symbol> { + pub fn get_symbol(&self, id: SymbolId) -> Option<&Symbol> { self.symbols.get(id.index0()) } #[must_use] - pub fn create( + pub(crate) fn create( &mut self, declaration: AstNodeId, name: Atom, @@ -61,11 +82,30 @@ impl SymbolTable { } #[must_use] - pub fn get_resolved_reference(&self, id: AstNodeId) -> Option<&ResolvedReference> { - self.resolved_references.get(&id) + pub fn resolved_references(&self) -> &Vec { + &self.resolved_references } - pub fn resolve_reference(&mut self, id: AstNodeId, reference: ResolvedReference) { - self.resolved_references.insert(id, reference); + #[must_use] + pub fn get_resolved_reference(&self, id: ResolvedReferenceId) -> Option<&ResolvedReference> { + self.resolved_references.get(id.index0()) + } + + /// Resolve all `references` to `symbol_id` + pub(crate) fn resolve_reference(&mut self, references: Vec, symbol_id: SymbolId) { + let additional_len = references.len(); + let symbol = &mut self.symbols[symbol_id]; + + self.resolved_references.reserve(additional_len); + symbol.references.reserve(additional_len); + + for reference in references { + let resolved_reference_id = + ResolvedReferenceId::new(self.resolved_references.len() + 1); + let resolved_reference = reference.resolve_to(symbol_id); + self.resolved_references.push(resolved_reference); + // explicitly push to vector here in correspondence to the previous reserve call + symbol.references.push(resolved_reference_id); + } } }