Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 82 additions & 1 deletion crates/oxc_semantic/src/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -97,9 +97,17 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> {
});
}

// Save `@__NO_SIDE_EFFECTS__` for function initializers.
if let BindingPatternKind::BindingIdentifier(id) = &self.id.kind {
if let Some(symbol_id) = id.symbol_id.get() {
if self.init.as_ref().is_some_and(Expression::is_anonymous_function_definition) {
if matches!(self.init, Some(Expression::ClassExpression(_))) {
builder.class_name_symbols.insert(symbol_id);
} else {
builder.function_name_symbols.insert(symbol_id);
}
}

// Save `@__NO_SIDE_EFFECTS__` for function initializers.
if let Some(init) = &self.init {
if match init {
Expression::FunctionExpression(func) => func.pure,
Expand All @@ -114,6 +122,76 @@ impl<'a> Binder<'a> for VariableDeclarator<'a> {
}
}

impl<'a> Binder<'a> for AssignmentPattern<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
if let BindingPatternKind::BindingIdentifier(id) = &self.left.kind {
if let Some(symbol_id) = id.symbol_id.get() {
if self.right.is_anonymous_function_definition() {
{
if matches!(self.right, Expression::ClassExpression(_)) {
builder.class_name_symbols.insert(symbol_id);
} else {
builder.function_name_symbols.insert(symbol_id);
}
}
}
}
}
}
}

impl<'a> Binder<'a> for AssignmentExpression<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
if let AssignmentTarget::AssignmentTargetIdentifier(id) = &self.left {
if let Some(reference_id) = id.reference_id.get() {
if self.right.is_anonymous_function_definition() {
{
if matches!(self.right, Expression::ClassExpression(_)) {
builder.class_name_references.insert(reference_id);
} else {
builder.function_name_references.insert(reference_id);
}
}
}
}
}
}
}

impl<'a> Binder<'a> for AssignmentTargetWithDefault<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
if let AssignmentTarget::AssignmentTargetIdentifier(id) = &self.binding {
if let Some(reference_id) = id.reference_id.get() {
if self.init.is_anonymous_function_definition() {
{
if matches!(self.init, Expression::ClassExpression(_)) {
builder.class_name_references.insert(reference_id);
} else {
builder.function_name_references.insert(reference_id);
}
}
}
}
}
}
}

impl<'a> Binder<'a> for AssignmentTargetPropertyIdentifier<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
if let Some(reference_id) = self.binding.reference_id.get() {
if self.init.as_ref().is_some_and(Expression::is_anonymous_function_definition) {
{
if matches!(self.init, Some(Expression::ClassExpression(_))) {
builder.class_name_references.insert(reference_id);
} else {
builder.function_name_references.insert(reference_id);
}
}
}
}
}
}

impl<'a> Binder<'a> for Class<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
if !self.declare {
Expand All @@ -129,6 +207,7 @@ impl<'a> Binder<'a> for Class<'a> {
},
);
ident.symbol_id.set(Some(symbol_id));
builder.class_name_symbols.insert(symbol_id);
}
}
}
Expand Down Expand Up @@ -198,6 +277,7 @@ impl<'a> Binder<'a> for Function<'a> {

let symbol_id = builder.declare_symbol(ident.span, &ident.name, includes, excludes);
ident.symbol_id.set(Some(symbol_id));
builder.function_name_symbols.insert(symbol_id);
} else if self.r#type == FunctionType::FunctionExpression {
// https://tc39.es/ecma262/#sec-runtime-semantics-instantiateordinaryfunctionexpression
// 5. Perform ! funcEnv.CreateImmutableBinding(name, false).
Expand All @@ -208,6 +288,7 @@ impl<'a> Binder<'a> for Function<'a> {
SymbolFlags::empty(),
);
ident.symbol_id.set(Some(symbol_id));
builder.function_name_symbols.insert(symbol_id);
}
}

Expand Down
37 changes: 36 additions & 1 deletion crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use std::{
};

use oxc_data_structures::stack::Stack;
use rustc_hash::FxHashMap;
use rustc_hash::{FxHashMap, FxHashSet};

use oxc_ast::{AstKind, ast::*};
use oxc_ast_visit::Visit;
Expand Down Expand Up @@ -84,6 +84,14 @@ pub struct SemanticBuilder<'a> {
// builders
pub(crate) nodes: AstNodes<'a>,
pub(crate) scoping: Scoping,
/// Symbols that are used as the name property of a function.
pub(crate) function_name_symbols: FxHashSet<SymbolId>,
/// Symbols that are used as the name property of a class.
pub(crate) class_name_symbols: FxHashSet<SymbolId>,
/// References that are used as the name property of a function.
pub(crate) function_name_references: FxHashSet<ReferenceId>,
/// References that are used as the name property of a class.
pub(crate) class_name_references: FxHashSet<ReferenceId>,

pub(crate) unresolved_references: UnresolvedReferencesStack<'a>,

Expand Down Expand Up @@ -135,6 +143,10 @@ impl<'a> SemanticBuilder<'a> {
nodes: AstNodes::default(),
hoisting_variables: FxHashMap::default(),
scoping,
function_name_symbols: FxHashSet::default(),
function_name_references: FxHashSet::default(),
class_name_symbols: FxHashSet::default(),
class_name_references: FxHashSet::default(),
unresolved_references: UnresolvedReferencesStack::new(),
unused_labels: UnusedLabels::default(),
build_jsdoc: false,
Expand Down Expand Up @@ -279,6 +291,12 @@ impl<'a> SemanticBuilder<'a> {
self.scoping.set_root_unresolved_references(
self.unresolved_references.into_root().into_iter().map(|(k, v)| (k.as_str(), v)),
);
self.scoping.set_name_symbols(
self.function_name_symbols,
self.function_name_references,
self.class_name_symbols,
self.class_name_references,
);

let jsdoc = if self.build_jsdoc { self.jsdoc.build() } else { JSDocFinder::default() };

Expand Down Expand Up @@ -886,6 +904,8 @@ impl<'a> Visit<'a> for SemanticBuilder<'a> {
});
/* cfg */

expr.bind(self);

self.leave_node(kind);
}

Expand Down Expand Up @@ -1950,6 +1970,9 @@ impl<'a> SemanticBuilder<'a> {
decl.bind(self);
self.make_all_namespaces_valuelike();
}
AstKind::AssignmentPattern(assign_pattern) => {
assign_pattern.bind(self);
}
AstKind::Function(func) => {
self.function_stack.push(self.current_node_id);
if func.is_declaration() {
Expand Down Expand Up @@ -2085,6 +2108,18 @@ impl<'a> SemanticBuilder<'a> {

fn leave_kind(&mut self, kind: AstKind<'a>) {
match kind {
AstKind::AssignmentTargetWithDefault(assign_target) => {
assign_target.bind(self);
}
AstKind::ObjectAssignmentTarget(assignment_target) => {
for target in &assignment_target.properties {
if let AssignmentTargetProperty::AssignmentTargetPropertyIdentifier(id_target) =
target
{
id_target.bind(self);
}
}
}
AstKind::Class(_) => {
self.current_node_flags -= NodeFlags::Class;
self.class_table_builder.pop_class();
Expand Down
40 changes: 40 additions & 0 deletions crates/oxc_semantic/src/scoping.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ pub struct Scoping {

pub(crate) references: IndexVec<ReferenceId, Reference>,

/// Symbols that are used as the name property of a function.
function_names: FxHashSet<SymbolId>,
/// Symbols that are used as the name property of a class.
class_names: FxHashSet<SymbolId>,

/// Function or Variable Symbol IDs that are marked with `@__NO_SIDE_EFFECTS__`.
pub(crate) no_side_effects: FxHashSet<SymbolId>,

Expand Down Expand Up @@ -77,6 +82,8 @@ impl Default for Scoping {
symbol_declarations: IndexVec::new(),
symbol_redeclarations: IndexVec::new(),
references: IndexVec::new(),
function_names: FxHashSet::default(),
class_names: FxHashSet::default(),
no_side_effects: FxHashSet::default(),
scope_parent_ids: IndexVec::new(),
scope_build_child_ids: false,
Expand Down Expand Up @@ -784,4 +791,37 @@ impl Scoping {
}
});
}

pub fn function_name_symbols(&self) -> &FxHashSet<SymbolId> {
&self.function_names
}

pub fn class_name_symbols(&self) -> &FxHashSet<SymbolId> {
&self.class_names
}

pub(crate) fn set_name_symbols(
&mut self,
function_symbols: FxHashSet<SymbolId>,
function_references: FxHashSet<ReferenceId>,
class_symbols: FxHashSet<SymbolId>,
class_references: FxHashSet<ReferenceId>,
) {
self.function_names = function_symbols
.into_iter()
.chain(
function_references
.into_iter()
.filter_map(|ref_id| self.references[ref_id].symbol_id()),
)
.collect();
self.class_names = class_symbols
.into_iter()
.chain(
class_references
.into_iter()
.filter_map(|ref_id| self.references[ref_id].symbol_id()),
)
.collect();
}
}
1 change: 1 addition & 0 deletions crates/oxc_semantic/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
pub mod cfg;
pub mod classes;
pub mod modules;
pub mod names;
pub mod scopes;
pub mod symbols;
pub mod util;
Loading
Loading