mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
refactor(semantic): symbol declarations and references (#594)
This commit is contained in:
parent
a637003c42
commit
87e65ac0f7
9 changed files with 86 additions and 37 deletions
|
|
@ -42,8 +42,7 @@ impl Rule for NoClassAssign {
|
|||
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
|
||||
let symbol_table = ctx.semantic().symbols();
|
||||
if symbol_table.get_flag(symbol_id).is_class() {
|
||||
for reference_id in symbol_table.get_resolved_references(symbol_id) {
|
||||
let reference = symbol_table.get_reference(*reference_id);
|
||||
for reference in symbol_table.get_resolved_references(symbol_id) {
|
||||
if reference.is_write() {
|
||||
ctx.diagnostic(NoClassAssignDiagnostic(
|
||||
symbol_table.get_name(symbol_id).clone(),
|
||||
|
|
|
|||
|
|
@ -41,8 +41,7 @@ impl Rule for NoConstAssign {
|
|||
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
|
||||
let symbol_table = ctx.semantic().symbols();
|
||||
if symbol_table.get_flag(symbol_id).is_const_variable() {
|
||||
for reference_id in symbol_table.get_resolved_references(symbol_id) {
|
||||
let reference = symbol_table.get_reference(*reference_id);
|
||||
for reference in symbol_table.get_resolved_references(symbol_id) {
|
||||
if reference.is_write() {
|
||||
ctx.diagnostic(NoConstAssignDiagnostic(
|
||||
symbol_table.get_name(symbol_id).clone(),
|
||||
|
|
|
|||
|
|
@ -43,8 +43,7 @@ impl Rule for NoExAssign {
|
|||
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
|
||||
let symbol_table = ctx.semantic().symbols();
|
||||
if symbol_table.get_flag(symbol_id).is_catch_variable() {
|
||||
for reference_id in symbol_table.get_resolved_references(symbol_id) {
|
||||
let reference = symbol_table.get_reference(*reference_id);
|
||||
for reference in symbol_table.get_resolved_references(symbol_id) {
|
||||
if reference.is_write() {
|
||||
ctx.diagnostic(NoExAssignDiagnostic(
|
||||
reference.span(),
|
||||
|
|
|
|||
|
|
@ -39,8 +39,7 @@ impl Rule for NoFuncAssign {
|
|||
let symbol_table = ctx.semantic().symbols();
|
||||
let decl = symbol_table.get_declaration(symbol_id);
|
||||
if let AstKind::Function(_) = ctx.nodes().kind(decl) {
|
||||
for reference_id in symbol_table.get_resolved_references(symbol_id) {
|
||||
let reference = symbol_table.get_reference(*reference_id);
|
||||
for reference in symbol_table.get_resolved_references(symbol_id) {
|
||||
if reference.is_write() {
|
||||
ctx.diagnostic(NoFuncAssignDiagnostic(
|
||||
symbol_table.get_name(symbol_id).clone(),
|
||||
|
|
|
|||
|
|
@ -46,8 +46,7 @@ impl Rule for NoImportAssign {
|
|||
fn run_on_symbol(&self, symbol_id: SymbolId, ctx: &LintContext<'_>) {
|
||||
let symbol_table = ctx.semantic().symbols();
|
||||
if symbol_table.get_flag(symbol_id).is_import_binding() {
|
||||
for reference_id in symbol_table.get_resolved_references(symbol_id) {
|
||||
let reference = symbol_table.get_reference(*reference_id);
|
||||
for reference in symbol_table.get_resolved_references(symbol_id) {
|
||||
if reference.is_write() {
|
||||
ctx.diagnostic(NoImportAssignDiagnostic(reference.span()));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,8 +42,7 @@ fn safely_shadows_undefined(symbol_id: SymbolId, ctx: &LintContext<'_>) -> bool
|
|||
let symbol_table = ctx.semantic().symbols();
|
||||
if symbol_table.get_name(symbol_id).as_str() == "undefined" {
|
||||
let mut no_assign = true;
|
||||
for reference_id in symbol_table.get_resolved_references(symbol_id) {
|
||||
let reference = symbol_table.get_reference(*reference_id);
|
||||
for reference in symbol_table.get_resolved_references(symbol_id) {
|
||||
if reference.is_write() {
|
||||
no_assign = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -226,7 +226,8 @@ impl<'a> ManglerBuilder<'a> {
|
|||
}
|
||||
let index = *slot;
|
||||
frequencies[index].slot = *slot;
|
||||
frequencies[index].frequency += symbol_table.get_resolved_references(symbol_id).len();
|
||||
frequencies[index].frequency +=
|
||||
symbol_table.get_resolved_reference_ids(symbol_id).len();
|
||||
frequencies[index].symbol_ids.push(symbol_id);
|
||||
}
|
||||
frequencies.sort_by_key(|x| (std::cmp::Reverse(x.frequency)));
|
||||
|
|
|
|||
|
|
@ -99,10 +99,23 @@ impl<'a> Semantic<'a> {
|
|||
self.scopes().root_unresolved_references().contains_key(&id.name)
|
||||
}
|
||||
|
||||
/// Find which scope a symbol is declared in
|
||||
pub fn symbol_scope(&self, symbol_id: SymbolId) -> ScopeId {
|
||||
self.symbols.get_scope_id(symbol_id)
|
||||
}
|
||||
|
||||
/// Get all resolved references for a symbol
|
||||
pub fn symbol_references(
|
||||
&'a self,
|
||||
symbol_id: SymbolId,
|
||||
) -> impl Iterator<Item = &'a Reference> + '_ {
|
||||
self.symbols.get_resolved_references(symbol_id)
|
||||
}
|
||||
|
||||
pub fn symbol_declaration(&self, symbol_id: SymbolId) -> AstNodeId {
|
||||
self.symbols.get_declaration(symbol_id)
|
||||
}
|
||||
|
||||
pub fn is_reference_to_global_variable(&self, ident: &IdentifierReference) -> bool {
|
||||
self.scopes().root_unresolved_references().contains_key(&ident.name)
|
||||
}
|
||||
|
|
@ -111,36 +124,68 @@ impl<'a> Semantic<'a> {
|
|||
#[cfg(test)]
|
||||
mod tests {
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::AstKind;
|
||||
use oxc_span::SourceType;
|
||||
use oxc_ast::{ast::VariableDeclarationKind, AstKind};
|
||||
use oxc_span::{Atom, SourceType};
|
||||
|
||||
use crate::SemanticBuilder;
|
||||
use super::*;
|
||||
|
||||
/// Create a [`Semantic`] from source code, assuming there are no syntax/semantic errors.
|
||||
fn get_semantic<'s, 'a: 's>(
|
||||
allocator: &'a Allocator,
|
||||
source: &'s str,
|
||||
source_type: SourceType,
|
||||
) -> Semantic<'s> {
|
||||
let parse = oxc_parser::Parser::new(allocator, source, source_type).parse();
|
||||
assert!(parse.errors.is_empty());
|
||||
let program = allocator.alloc(parse.program);
|
||||
let semantic = SemanticBuilder::new(source, source_type).build(program);
|
||||
assert!(semantic.errors.is_empty());
|
||||
semantic.semantic
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_symbols() {
|
||||
let source = "
|
||||
let a;
|
||||
function foo(a) {
|
||||
return a + 1;
|
||||
}
|
||||
let b = a + foo(1);";
|
||||
let allocator = Allocator::default();
|
||||
let semantic = get_semantic(&allocator, source, SourceType::default());
|
||||
|
||||
let top_level_a = semantic
|
||||
.scopes()
|
||||
.get_binding(semantic.scopes().root_scope_id(), &Atom::from("a"))
|
||||
.unwrap();
|
||||
|
||||
let decl = semantic.symbol_declaration(top_level_a);
|
||||
match semantic.nodes().get_node(decl).kind() {
|
||||
AstKind::VariableDeclarator(decl) => {
|
||||
assert_eq!(decl.kind, VariableDeclarationKind::Let);
|
||||
}
|
||||
kind => panic!("Expected VariableDeclarator for 'let', got {kind:?}"),
|
||||
}
|
||||
|
||||
let references = semantic.symbol_references(top_level_a);
|
||||
assert_eq!(references.count(), 1);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_is_global() {
|
||||
let source = "
|
||||
var a = 0;
|
||||
function foo() {
|
||||
a += 1;
|
||||
}
|
||||
var a = 0;
|
||||
function foo() {
|
||||
a += 1;
|
||||
}
|
||||
|
||||
var b = a + 2;
|
||||
";
|
||||
var b = a + 2;
|
||||
";
|
||||
let allocator = Allocator::default();
|
||||
let source_type = SourceType::default();
|
||||
let parse =
|
||||
oxc_parser::Parser::new(&allocator, source, oxc_span::SourceType::default()).parse();
|
||||
assert!(parse.errors.is_empty());
|
||||
let program = allocator.alloc(parse.program);
|
||||
|
||||
{
|
||||
let semantic = SemanticBuilder::new(source, source_type).build(program);
|
||||
assert!(semantic.errors.is_empty());
|
||||
let semantic = semantic.semantic;
|
||||
for node in semantic.nodes().iter() {
|
||||
if let AstKind::IdentifierReference(id) = node.kind() {
|
||||
assert!(!semantic.is_reference_to_global_variable(id));
|
||||
}
|
||||
let semantic = get_semantic(&allocator, source, SourceType::default());
|
||||
for node in semantic.nodes().iter() {
|
||||
if let AstKind::IdentifierReference(id) = node.kind() {
|
||||
assert!(!semantic.is_reference_to_global_variable(id));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,7 +92,16 @@ impl SymbolTable {
|
|||
self.references[reference_id].symbol_id().is_none()
|
||||
}
|
||||
|
||||
pub fn get_resolved_references(&self, symbol_id: SymbolId) -> &Vec<ReferenceId> {
|
||||
pub fn get_resolved_reference_ids(&self, symbol_id: SymbolId) -> &Vec<ReferenceId> {
|
||||
&self.resolved_references[symbol_id]
|
||||
}
|
||||
|
||||
pub fn get_resolved_references(
|
||||
&self,
|
||||
symbol_id: SymbolId,
|
||||
) -> impl Iterator<Item = &Reference> + '_ {
|
||||
self.resolved_references[symbol_id]
|
||||
.iter()
|
||||
.map(|reference_id| &self.references[*reference_id])
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue