Skip to content
Merged
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
1 change: 1 addition & 0 deletions crates/oxc_ast/src/ast_kind_impl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ impl<'a> AstKind<'a> {
Self::TSConditionalType(_) => "TSConditionalType".into(),
Self::TSMappedType(_) => "TSMappedType".into(),
Self::TSConstructSignatureDeclaration(_) => "TSConstructSignatureDeclaration".into(),
Self::TSModuleReference(_) => "TSModuleReference".into(),
}
}
}
3 changes: 3 additions & 0 deletions crates/oxc_ast/src/generated/ast_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ pub enum AstType {
TSSatisfiesExpression,
TSTypeAssertion,
TSImportEqualsDeclaration,
TSModuleReference,
TSExternalModuleReference,
TSNonNullExpression,
Decorator,
Expand Down Expand Up @@ -321,6 +322,7 @@ pub enum AstKind<'a> {
TSSatisfiesExpression(&'a TSSatisfiesExpression<'a>),
TSTypeAssertion(&'a TSTypeAssertion<'a>),
TSImportEqualsDeclaration(&'a TSImportEqualsDeclaration<'a>),
TSModuleReference(&'a TSModuleReference<'a>),
TSExternalModuleReference(&'a TSExternalModuleReference<'a>),
TSNonNullExpression(&'a TSNonNullExpression<'a>),
Decorator(&'a Decorator<'a>),
Expand Down Expand Up @@ -491,6 +493,7 @@ impl<'a> GetSpan for AstKind<'a> {
Self::TSSatisfiesExpression(it) => it.span(),
Self::TSTypeAssertion(it) => it.span(),
Self::TSImportEqualsDeclaration(it) => it.span(),
Self::TSModuleReference(it) => it.span(),
Self::TSExternalModuleReference(it) => it.span(),
Self::TSNonNullExpression(it) => it.span(),
Self::Decorator(it) => it.span(),
Expand Down
3 changes: 3 additions & 0 deletions crates/oxc_ast/src/generated/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3939,6 +3939,8 @@ pub mod walk {

#[inline]
pub fn walk_ts_module_reference<'a, V: Visit<'a>>(visitor: &mut V, it: &TSModuleReference<'a>) {
let kind = AstKind::TSModuleReference(visitor.alloc(it));
visitor.enter_node(kind);
match it {
TSModuleReference::ExternalModuleReference(it) => {
visitor.visit_ts_external_module_reference(it)
Expand All @@ -3947,6 +3949,7 @@ pub mod walk {
visitor.visit_ts_type_name(it.to_ts_type_name())
}
}
visitor.leave_node(kind);
}

#[inline]
Expand Down
3 changes: 3 additions & 0 deletions crates/oxc_ast/src/generated/visit_mut.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4166,6 +4166,8 @@ pub mod walk_mut {
visitor: &mut V,
it: &mut TSModuleReference<'a>,
) {
let kind = AstType::TSModuleReference;
visitor.enter_node(kind);
match it {
TSModuleReference::ExternalModuleReference(it) => {
visitor.visit_ts_external_module_reference(it)
Expand All @@ -4174,6 +4176,7 @@ pub mod walk_mut {
visitor.visit_ts_type_name(it.to_ts_type_name_mut())
}
}
visitor.leave_node(kind);
}

#[inline]
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/eslint/no_global_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,8 @@ impl Rule for NoGlobalAssign {

fn run_once(&self, ctx: &LintContext) {
let symbol_table = ctx.symbols();
for reference_id_list in ctx.scopes().root_unresolved_references().values() {
for &reference_id in reference_id_list {
for reference_id_list in ctx.scopes().root_unresolved_references_ids() {
for reference_id in reference_id_list {
let reference = symbol_table.get_reference(reference_id);
if reference.is_write() {
let name = reference.name();
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/eslint/no_import_assign.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ const REFLECT_MUTATION_METHODS: phf::Set<&'static str> =
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() {
if symbol_table.get_flag(symbol_id).is_import() {
let kind = ctx.nodes().kind(symbol_table.get_declaration(symbol_id));
let is_namespace_specifier = matches!(kind, AstKind::ImportNamespaceSpecifier(_));
for reference in symbol_table.get_resolved_references(symbol_id) {
Expand Down
4 changes: 2 additions & 2 deletions crates/oxc_linter/src/rules/eslint/no_undef.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ impl Rule for NoUndef {
fn run_once(&self, ctx: &LintContext) {
let symbol_table = ctx.symbols();

for reference_id_list in ctx.scopes().root_unresolved_references().values() {
for &reference_id in reference_id_list {
for reference_id_list in ctx.scopes().root_unresolved_references_ids() {
for reference_id in reference_id_list {
let reference = symbol_table.get_reference(reference_id);
let name = reference.name();

Expand Down
22 changes: 13 additions & 9 deletions crates/oxc_linter/src/rules/jest/no_confusing_set_timeout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,15 +90,19 @@ impl Rule for NoConfusingSetTimeout {
let mut jest_reference_id_list: Vec<(ReferenceId, Span)> = vec![];
let mut seen_jest_set_timeout = false;

for reference_ids in scopes.root_unresolved_references().values() {
for reference_ids in scopes.root_unresolved_references_ids() {
collect_jest_reference_id(reference_ids, &mut jest_reference_id_list, ctx);
}

for reference_ids in &symbol_table.resolved_references {
collect_jest_reference_id(reference_ids, &mut jest_reference_id_list, ctx);
collect_jest_reference_id(
reference_ids.iter().copied(),
&mut jest_reference_id_list,
ctx,
);
}

for reference_id_list in scopes.root_unresolved_references().values() {
for reference_id_list in scopes.root_unresolved_references_ids() {
handle_jest_set_time_out(
ctx,
reference_id_list,
Expand All @@ -111,7 +115,7 @@ impl Rule for NoConfusingSetTimeout {
for reference_id_list in &symbol_table.resolved_references {
handle_jest_set_time_out(
ctx,
reference_id_list,
reference_id_list.iter().copied(),
&jest_reference_id_list,
&mut seen_jest_set_timeout,
&id_to_jest_node_map,
Expand All @@ -121,15 +125,15 @@ impl Rule for NoConfusingSetTimeout {
}

fn collect_jest_reference_id(
reference_id_list: &Vec<ReferenceId>,
reference_id_list: impl Iterator<Item = ReferenceId>,
jest_reference_list: &mut Vec<(ReferenceId, Span)>,
ctx: &LintContext,
) {
let symbol_table = ctx.symbols();
let nodes = ctx.nodes();

for reference_id in reference_id_list {
let reference = symbol_table.get_reference(*reference_id);
let reference = symbol_table.get_reference(reference_id);
if !is_jest_call(reference.name()) {
continue;
}
Expand All @@ -139,13 +143,13 @@ fn collect_jest_reference_id(
let AstKind::MemberExpression(member_expr) = parent_node.kind() else {
continue;
};
jest_reference_list.push((*reference_id, member_expr.span()));
jest_reference_list.push((reference_id, member_expr.span()));
}
}

fn handle_jest_set_time_out<'a>(
ctx: &LintContext<'a>,
reference_id_list: &Vec<ReferenceId>,
reference_id_list: impl Iterator<Item = ReferenceId>,
jest_reference_id_list: &Vec<(ReferenceId, Span)>,
seen_jest_set_timeout: &mut bool,
id_to_jest_node_map: &HashMap<AstNodeId, &PossibleJestNode<'a, '_>>,
Expand All @@ -154,7 +158,7 @@ fn handle_jest_set_time_out<'a>(
let scopes = ctx.scopes();
let symbol_table = ctx.symbols();

for &reference_id in reference_id_list {
for reference_id in reference_id_list {
let reference = symbol_table.get_reference(reference_id);

let Some(parent_node) = nodes.parent_node(reference.node_id()) else {
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/jest/no_jasmine_globals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ impl Rule for NoJasmineGlobals {
.filter(|(key, _)| NON_JASMINE_PROPERTY_NAMES.contains(&key.as_str()));

for (name, reference_ids) in jasmine_references {
for &reference_id in reference_ids {
for &(reference_id, _) in reference_ids {
let reference = symbol_table.get_reference(reference_id);
if let Some((error, help)) = get_non_jasmine_property_messages(name) {
ctx.diagnostic(no_jasmine_globals_diagnostic(error, help, reference.span()));
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/jest/no_mocks_import.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Rule for NoMocksImport {
return;
};

for reference_id in require_reference_ids {
for (reference_id, _) in require_reference_ids {
let reference = ctx.symbols().get_reference(*reference_id);
let Some(parent) = ctx.nodes().parent_node(reference.node_id()) else {
return;
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_linter/src/rules/nextjs/no_duplicate_head.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ impl Rule for NoDuplicateHead {
}

let flag = symbols.get_flag(symbol_id);
if !flag.is_import_binding() {
if !flag.is_import() {
return;
}

Expand Down
10 changes: 5 additions & 5 deletions crates/oxc_linter/src/utils/jest.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use oxc_ast::{
},
AstKind,
};
use oxc_semantic::{AstNode, ReferenceId};
use oxc_semantic::{AstNode, ReferenceFlag, ReferenceId};
use phf::phf_set;

use crate::LintContext;
Expand Down Expand Up @@ -159,7 +159,7 @@ pub fn collect_possible_jest_call_node<'a, 'b>(
collect_ids_referenced_to_global(ctx)
.iter()
// set the original of global test function to None
.map(|id| (*id, None)),
.map(|(id, _)| (*id, None)),
);
}

Expand Down Expand Up @@ -198,7 +198,7 @@ fn collect_ids_referenced_to_import<'a>(
.resolved_references
.iter_enumerated()
.filter_map(|(symbol_id, reference_ids)| {
if ctx.symbols().get_flag(symbol_id).is_import_binding() {
if ctx.symbols().get_flag(symbol_id).is_import() {
let id = ctx.symbols().get_declaration(symbol_id);
let Some(AstKind::ImportDeclaration(import_decl)) = ctx.nodes().parent_kind(id)
else {
Expand Down Expand Up @@ -236,13 +236,13 @@ fn find_original_name<'a>(import_decl: &'a ImportDeclaration<'a>, name: &str) ->
})
}

fn collect_ids_referenced_to_global(ctx: &LintContext) -> Vec<ReferenceId> {
fn collect_ids_referenced_to_global(ctx: &LintContext) -> Vec<(ReferenceId, ReferenceFlag)> {
ctx.scopes()
.root_unresolved_references()
.iter()
.filter(|(name, _)| JEST_METHOD_NAMES.contains(name.as_str()))
.flat_map(|(_, reference_ids)| reference_ids.clone())
.collect::<Vec<ReferenceId>>()
.collect::<Vec<(ReferenceId, ReferenceFlag)>>()
}

/// join name of the expression. e.g.
Expand Down
25 changes: 19 additions & 6 deletions crates/oxc_semantic/src/binder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,37 +251,50 @@ impl<'a> Binder for CatchParameter<'a> {
}
}

fn declare_symbol_for_import_specifier(ident: &BindingIdentifier, builder: &mut SemanticBuilder) {
fn declare_symbol_for_import_specifier(
ident: &BindingIdentifier,
is_type: bool,
builder: &mut SemanticBuilder,
) {
let includes = if is_type
|| builder.nodes.parent_kind(builder.current_node_id).is_some_and(
|decl| matches!(decl, AstKind::ImportDeclaration(decl) if decl.import_kind.is_type()),
) {
SymbolFlags::TypeImport
} else {
SymbolFlags::Import
};

let symbol_id = builder.declare_symbol(
ident.span,
&ident.name,
SymbolFlags::ImportBinding,
includes,
SymbolFlags::ImportBindingExcludes,
);
ident.symbol_id.set(Some(symbol_id));
}

impl<'a> Binder for ImportSpecifier<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
declare_symbol_for_import_specifier(&self.local, builder);
declare_symbol_for_import_specifier(&self.local, self.import_kind.is_type(), builder);
}
}

impl<'a> Binder for ImportDefaultSpecifier<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
declare_symbol_for_import_specifier(&self.local, builder);
declare_symbol_for_import_specifier(&self.local, false, builder);
}
}

impl<'a> Binder for ImportNamespaceSpecifier<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
declare_symbol_for_import_specifier(&self.local, builder);
declare_symbol_for_import_specifier(&self.local, false, builder);
}
}

impl<'a> Binder for TSImportEqualsDeclaration<'a> {
fn bind(&self, builder: &mut SemanticBuilder) {
declare_symbol_for_import_specifier(&self.id, builder);
declare_symbol_for_import_specifier(&self.id, false, builder);
}
}

Expand Down
53 changes: 43 additions & 10 deletions crates/oxc_semantic/src/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,12 +353,13 @@ impl<'a> SemanticBuilder<'a> {
/// # Panics
pub fn declare_reference(&mut self, reference: Reference) -> ReferenceId {
let reference_name = reference.name().clone();
let reference_flag = *reference.flag();
let reference_id = self.symbols.create_reference(reference);

self.unresolved_references[self.current_scope_depth]
.entry(reference_name)
.or_default()
.push(reference_id);
.push((reference_id, reference_flag));
reference_id
}

Expand All @@ -385,19 +386,39 @@ impl<'a> SemanticBuilder<'a> {
let parent_refs = iter.nth(self.current_scope_depth - 1).unwrap();
let current_refs = iter.next().unwrap();

let bindings = self.scope.get_bindings(self.current_scope_id);
for (name, reference_ids) in current_refs.drain() {
for (name, mut references) in current_refs.drain() {
// Try to resolve a reference.
// If unresolved, transfer it to parent scope's unresolved references.
let bindings = self.scope.get_bindings(self.current_scope_id);
if let Some(symbol_id) = bindings.get(&name).copied() {
for reference_id in &reference_ids {
self.symbols.references[*reference_id].set_symbol_id(symbol_id);
let symbol_flag = self.symbols.get_flag(symbol_id);

let resolved_references: &mut Vec<_> =
self.symbols.resolved_references[symbol_id].as_mut();
// Reserve space for all references to avoid reallocations.
resolved_references.reserve(references.len());

references.retain(|(id, flag)| {
if flag.is_type() && symbol_flag.is_type()
|| flag.is_value() && symbol_flag.is_value()
{
self.symbols.references[*id].set_symbol_id(symbol_id);
resolved_references.push(*id);
false
} else {
true
}
});

if references.is_empty() {
continue;
}
self.symbols.resolved_references[symbol_id].extend(reference_ids);
} else if let Some(parent_reference_ids) = parent_refs.get_mut(&name) {
parent_reference_ids.extend(reference_ids);
}

if let Some(parent_reference_ids) = parent_refs.get_mut(&name) {
parent_reference_ids.extend(references);
} else {
parent_refs.insert(name, reference_ids);
parent_refs.insert(name, references);
}
}
}
Expand Down Expand Up @@ -1692,7 +1713,19 @@ impl<'a> SemanticBuilder<'a> {
self.current_reference_flag = ReferenceFlag::Type;
}
AstKind::TSTypeName(_) => {
self.current_reference_flag = ReferenceFlag::Type;
match self.nodes.parent_kind(self.current_node_id) {
Some(AstKind::TSModuleReference(_)) => {
// import A = a;
self.current_reference_flag = ReferenceFlag::Read;
}
Some(AstKind::TSQualifiedName(_)) => {
// import A = a.b
// ^^^ Keep the current reference flag
}
_ => {
self.current_reference_flag = ReferenceFlag::Type;
}
}
}
AstKind::IdentifierReference(ident) => {
self.reference_identifier(ident);
Expand Down
Loading