mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(semantic): Add index mapping from span to reference id (#270)
* feat(semantic): Add index mapping from span to reference id * Switch to BTreeMap for index
This commit is contained in:
parent
84d252df01
commit
35b19e7edf
7 changed files with 169 additions and 44 deletions
|
|
@ -143,9 +143,8 @@ impl<'a> LintContext<'a> {
|
|||
|
||||
/* Symbols */
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
pub fn is_reference_to_global_variable(&self, _ident: &IdentifierReference) -> bool {
|
||||
true
|
||||
pub fn is_reference_to_global_variable(&self, ident: &IdentifierReference) -> bool {
|
||||
self.semantic().is_reference_to_global_variables(ident)
|
||||
}
|
||||
|
||||
#[allow(clippy::unused_self)]
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ use crate::{
|
|||
module_record::ModuleRecordBuilder,
|
||||
node::{AstNodeId, AstNodes, NodeFlags, SemanticNode},
|
||||
scope::{ScopeBuilder, ScopeId},
|
||||
symbol::{Reference, ReferenceFlag, SymbolFlags, SymbolId, SymbolTable},
|
||||
symbol::{Reference, ReferenceFlag, SymbolFlags, SymbolId, SymbolTableBuilder},
|
||||
Semantic,
|
||||
};
|
||||
|
||||
|
|
@ -38,7 +38,7 @@ pub struct SemanticBuilder<'a> {
|
|||
// builders
|
||||
pub nodes: AstNodes<'a>,
|
||||
pub scope: ScopeBuilder,
|
||||
pub symbols: SymbolTable,
|
||||
pub symbols: SymbolTableBuilder,
|
||||
|
||||
with_module_record_builder: bool,
|
||||
module_record_builder: ModuleRecordBuilder,
|
||||
|
|
@ -67,7 +67,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
current_symbol_flags: SymbolFlags::empty(),
|
||||
nodes,
|
||||
scope,
|
||||
symbols: SymbolTable::default(),
|
||||
symbols: SymbolTableBuilder::default(),
|
||||
with_module_record_builder: false,
|
||||
module_record_builder: ModuleRecordBuilder::default(),
|
||||
}
|
||||
|
|
@ -84,6 +84,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
// First AST pass
|
||||
self.visit_program(program);
|
||||
|
||||
let symbols = self.symbols.build();
|
||||
// Second partial AST pass on top level import / export statements
|
||||
let module_record = if self.with_module_record_builder {
|
||||
self.module_record_builder.build(program)
|
||||
|
|
@ -97,7 +98,7 @@ impl<'a> SemanticBuilder<'a> {
|
|||
trivias: self.trivias,
|
||||
nodes: self.nodes,
|
||||
scopes: self.scope.scopes,
|
||||
symbols: self.symbols,
|
||||
symbols,
|
||||
module_record,
|
||||
};
|
||||
SemanticBuilderReturn { semantic, errors: self.errors }
|
||||
|
|
|
|||
|
|
@ -13,8 +13,12 @@ use std::rc::Rc;
|
|||
pub use builder::SemanticBuilder;
|
||||
use node::AstNodeId;
|
||||
pub use node::{AstNode, AstNodes, SemanticNode};
|
||||
use oxc_ast::{module_record::ModuleRecord, AstKind, SourceType, Trivias};
|
||||
use oxc_ast::{
|
||||
ast::IdentifierReference, module_record::ModuleRecord, AstKind, SourceType, Trivias,
|
||||
};
|
||||
use scope::ScopeId;
|
||||
pub use scope::{Scope, ScopeFlags, ScopeTree};
|
||||
use symbol::SymbolId;
|
||||
pub use symbol::{Reference, ResolvedReference, Symbol, SymbolFlags, SymbolTable};
|
||||
|
||||
pub struct Semantic<'a> {
|
||||
|
|
@ -76,4 +80,61 @@ impl<'a> Semantic<'a> {
|
|||
let scope = &self.scopes()[reference_node.scope_id()];
|
||||
scope.unresolved_references.contains_key(&id.name)
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn symbol_scope(&self, symbol_id: SymbolId) -> ScopeId {
|
||||
let symbol = &self.symbols[symbol_id];
|
||||
let declaration = symbol.declaration();
|
||||
self.nodes[declaration].scope_id()
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn is_reference_to_global_variables(&self, id: &IdentifierReference) -> bool {
|
||||
// unresolved references are treated as reference to global.
|
||||
self.symbols.get_resolved_reference_for_id(id).map_or(true, |reference_id| {
|
||||
let referred_symbol = reference_id.resolved_symbol_id;
|
||||
let symbol_scope = self.symbol_scope(referred_symbol);
|
||||
// Symbol declared in top level
|
||||
symbol_scope == self.scopes().root_scope_id()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::{AstKind, SourceType};
|
||||
|
||||
use crate::SemanticBuilder;
|
||||
|
||||
#[test]
|
||||
fn test_is_global() {
|
||||
let source = "
|
||||
var a = 0;
|
||||
function foo() {
|
||||
a += 1;
|
||||
}
|
||||
|
||||
var b = a + 2;
|
||||
|
||||
console.log(b);
|
||||
";
|
||||
let allocator = Allocator::default();
|
||||
let source_type = SourceType::default();
|
||||
let parse =
|
||||
oxc_parser::Parser::new(&allocator, source, oxc_ast::SourceType::default()).parse();
|
||||
assert!(parse.errors.is_empty());
|
||||
let program = allocator.alloc(parse.program);
|
||||
|
||||
{
|
||||
let semantic = SemanticBuilder::new(source, source_type, &parse.trivias).build(program);
|
||||
assert!(semantic.errors.is_empty());
|
||||
let semantic = semantic.semantic;
|
||||
for node in semantic.nodes().iter() {
|
||||
if let AstKind::IdentifierReference(id) = node.get().kind() {
|
||||
assert!(semantic.is_reference_to_global_variables(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use oxc_ast::{ast::ClassType, AstKind, Atom, SourceType};
|
|||
use rustc_hash::FxHashMap;
|
||||
|
||||
use super::{Scope, ScopeFlags, ScopeId, ScopeTree};
|
||||
use crate::{symbol::Reference, SymbolTable};
|
||||
use crate::symbol::{Reference, SymbolTableBuilder};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ScopeBuilder {
|
||||
|
|
@ -58,7 +58,7 @@ impl ScopeBuilder {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn resolve_reference(&mut self, symbol_table: &mut SymbolTable) {
|
||||
pub fn resolve_reference(&mut self, symbol_table: &mut SymbolTableBuilder) {
|
||||
// At the initial stage, all references are unresolved.
|
||||
let all_references = {
|
||||
let current_scope = self.current_scope_mut();
|
||||
|
|
|
|||
73
crates/oxc_semantic/src/symbol/builder.rs
Normal file
73
crates/oxc_semantic/src/symbol/builder.rs
Normal file
|
|
@ -0,0 +1,73 @@
|
|||
use std::{
|
||||
collections::BTreeMap,
|
||||
ops::{Index, IndexMut},
|
||||
};
|
||||
|
||||
use oxc_ast::{Atom, Span};
|
||||
|
||||
use super::{reference::ResolvedReferenceId, SymbolId};
|
||||
use crate::{node::AstNodeId, Reference, ResolvedReference, Symbol, SymbolFlags, SymbolTable};
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct SymbolTableBuilder {
|
||||
/// Stores all the `Symbols` indexed by `SymbolId`
|
||||
symbols: Vec<Symbol>,
|
||||
/// Stores all the resolved references indexed by `ResolvedReferenceId`
|
||||
resolved_references: Vec<ResolvedReference>,
|
||||
// BTreeMap is empirically a lot faster than FxHashMap for our insertion,
|
||||
resolved_references_index: BTreeMap<Span, ResolvedReferenceId>,
|
||||
}
|
||||
|
||||
impl Index<SymbolId> for SymbolTableBuilder {
|
||||
type Output = Symbol;
|
||||
|
||||
fn index(&self, index: SymbolId) -> &Self::Output {
|
||||
&self.symbols[index.index0()]
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<SymbolId> for SymbolTableBuilder {
|
||||
fn index_mut(&mut self, index: SymbolId) -> &mut Self::Output {
|
||||
&mut self.symbols[index.index0()]
|
||||
}
|
||||
}
|
||||
|
||||
impl SymbolTableBuilder {
|
||||
#[must_use]
|
||||
pub fn create(
|
||||
&mut self,
|
||||
declaration: AstNodeId,
|
||||
name: Atom,
|
||||
span: Span,
|
||||
flags: SymbolFlags,
|
||||
) -> SymbolId {
|
||||
let symbol_id = SymbolId::new(self.symbols.len() + 1);
|
||||
let symbol = Symbol::new(symbol_id, declaration, name, span, flags);
|
||||
self.symbols.push(symbol);
|
||||
symbol_id
|
||||
}
|
||||
|
||||
/// Resolve all `references` to `symbol_id`
|
||||
pub fn resolve_reference(&mut self, references: Vec<Reference>, 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);
|
||||
self.resolved_references_index.insert(reference.span, resolved_reference_id);
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build(self) -> SymbolTable {
|
||||
SymbolTable::new(self.symbols, self.resolved_references, self.resolved_references_index)
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,7 @@
|
|||
//! Symbol and Symbol Table for tracking of semantics of variables
|
||||
#![allow(non_upper_case_globals)]
|
||||
|
||||
mod builder;
|
||||
mod id;
|
||||
mod reference;
|
||||
mod table;
|
||||
|
|
@ -10,6 +11,7 @@ use oxc_ast::{Atom, Span};
|
|||
|
||||
use self::reference::ResolvedReferenceId;
|
||||
pub use self::{
|
||||
builder::SymbolTableBuilder,
|
||||
id::SymbolId,
|
||||
reference::{Reference, ReferenceFlag, ResolvedReference},
|
||||
table::SymbolTable,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,12 @@
|
|||
use std::collections::BTreeMap;
|
||||
use std::ops::{Deref, Index, IndexMut};
|
||||
|
||||
use oxc_ast::{Atom, Span};
|
||||
use oxc_ast::ast::IdentifierReference;
|
||||
use oxc_ast::Span;
|
||||
|
||||
use super::reference::ResolvedReferenceId;
|
||||
use super::{Symbol, SymbolFlags, SymbolId};
|
||||
use crate::node::AstNodeId;
|
||||
use crate::{Reference, ResolvedReference};
|
||||
use super::{Symbol, SymbolId};
|
||||
use crate::ResolvedReference;
|
||||
|
||||
/// `SymbolTable` is a storage of all the symbols (related to `BindingIdentifiers`)
|
||||
/// and references (related to `IdentifierReferences`) of the program. It supports two
|
||||
|
|
@ -18,6 +19,7 @@ pub struct SymbolTable {
|
|||
symbols: Vec<Symbol>,
|
||||
/// Stores all the resolved references indexed by `ResolvedReferenceId`
|
||||
resolved_references: Vec<ResolvedReference>,
|
||||
resolved_references_index: BTreeMap<Span, ResolvedReferenceId>,
|
||||
}
|
||||
|
||||
impl Index<SymbolId> for SymbolTable {
|
||||
|
|
@ -57,6 +59,15 @@ impl Deref for SymbolTable {
|
|||
}
|
||||
|
||||
impl SymbolTable {
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
symbols: Vec<Symbol>,
|
||||
resolved_references: Vec<ResolvedReference>,
|
||||
resolved_references_index: BTreeMap<Span, ResolvedReferenceId>,
|
||||
) -> Self {
|
||||
Self { symbols, resolved_references, resolved_references_index }
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn symbols(&self) -> &Vec<Symbol> {
|
||||
&self.symbols
|
||||
|
|
@ -67,20 +78,6 @@ impl SymbolTable {
|
|||
self.symbols.get(id.index0())
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub(crate) fn create(
|
||||
&mut self,
|
||||
declaration: AstNodeId,
|
||||
name: Atom,
|
||||
span: Span,
|
||||
flags: SymbolFlags,
|
||||
) -> SymbolId {
|
||||
let symbol_id = SymbolId::new(self.symbols.len() + 1);
|
||||
let symbol = Symbol::new(symbol_id, declaration, name, span, flags);
|
||||
self.symbols.push(symbol);
|
||||
symbol_id
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub fn resolved_references(&self) -> &Vec<ResolvedReference> {
|
||||
&self.resolved_references
|
||||
|
|
@ -91,21 +88,13 @@ impl SymbolTable {
|
|||
self.resolved_references.get(id.index0())
|
||||
}
|
||||
|
||||
/// Resolve all `references` to `symbol_id`
|
||||
pub(crate) fn resolve_reference(&mut self, references: Vec<Reference>, 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);
|
||||
}
|
||||
#[must_use]
|
||||
pub fn get_resolved_reference_for_id(
|
||||
&self,
|
||||
id: &IdentifierReference,
|
||||
) -> Option<&ResolvedReference> {
|
||||
self.resolved_references_index
|
||||
.get(&id.span)
|
||||
.map(|ref_id| &self.resolved_references[ref_id.index0()])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue