diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index c786718561422..7ce690871680d 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -278,9 +278,6 @@ impl<'a> SemanticBuilder<'a> { } debug_assert_eq!(self.unresolved_references.scope_depth(), 1); - if self.check_syntax_error && !self.source_type.is_typescript() { - checker::check_unresolved_exports(&self); - } self.scoping.set_root_unresolved_references( self.unresolved_references.into_root().into_iter().map(|(k, v)| (k.as_str(), v)), ); diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index 078b9c0830973..6900ccc7d37fc 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -16,7 +16,31 @@ use oxc_syntax::{ symbol::SymbolFlags, }; -use crate::{builder::SemanticBuilder, diagnostics::redeclaration}; +use crate::{IsGlobalReference, builder::SemanticBuilder, diagnostics::redeclaration}; + +#[cold] +fn undefined_export(x0: &str, span1: Span) -> OxcDiagnostic { + OxcDiagnostic::error(format!("Export '{x0}' is not defined")).with_label(span1) +} + +/// It is a Syntax Error if any element of the ExportedBindings of ModuleItemList +/// does not also occur in either the VarDeclaredNames of ModuleItemList, or the LexicallyDeclaredNames of ModuleItemList. +pub fn check_unresolved_exports(program: &Program<'_>, ctx: &SemanticBuilder<'_>) { + if ctx.source_type.is_typescript() || ctx.source_type.is_script() { + return; + } + for stmt in &program.body { + if let Statement::ExportNamedDeclaration(decl) = stmt { + for specifier in &decl.specifiers { + if let ModuleExportName::IdentifierReference(ident) = &specifier.local + && ident.is_global_reference(&ctx.scoping) + { + ctx.errors.borrow_mut().push(undefined_export(&ident.name, ident.span)); + } + } + } + } +} pub fn check_duplicate_class_elements(ctx: &SemanticBuilder<'_>) { let classes = &ctx.class_table_builder.classes; diff --git a/crates/oxc_semantic/src/checker/mod.rs b/crates/oxc_semantic/src/checker/mod.rs index e875f877259e3..ad188d8ec4e9f 100644 --- a/crates/oxc_semantic/src/checker/mod.rs +++ b/crates/oxc_semantic/src/checker/mod.rs @@ -1,9 +1,4 @@ -use oxc_ast::{ - AstKind, - ast::{DoWhileStatement, ForStatement, WhileStatement}, -}; -use oxc_diagnostics::OxcDiagnostic; -use oxc_span::Span; +use oxc_ast::{AstKind, ast::*}; use crate::builder::SemanticBuilder; @@ -16,8 +11,9 @@ pub use javascript::is_function_part_of_if_statement; pub fn check<'a>(kind: AstKind<'a>, ctx: &SemanticBuilder<'a>) { match kind { - AstKind::Program(_) => { + AstKind::Program(program) => { js::check_duplicate_class_elements(ctx); + js::check_unresolved_exports(program, ctx); } AstKind::BindingIdentifier(ident) => { js::check_identifier(&ident.name, ident.span, ctx); @@ -124,25 +120,3 @@ pub fn check<'a>(kind: AstKind<'a>, ctx: &SemanticBuilder<'a>) { _ => {} } } - -#[cold] -fn undefined_export(x0: &str, span1: Span) -> OxcDiagnostic { - OxcDiagnostic::error(format!("Export '{x0}' is not defined")).with_label(span1) -} - -/// It is a Syntax Error if any element of the ExportedBindings of ModuleItemList -/// does not also occur in either the VarDeclaredNames of ModuleItemList, or the LexicallyDeclaredNames of ModuleItemList. -pub fn check_unresolved_exports(ctx: &SemanticBuilder<'_>) { - for reference_ids in ctx.unresolved_references.root().values() { - for reference_id in reference_ids { - let reference = ctx.scoping.get_reference(*reference_id); - let node_id = reference.node_id(); - let node = ctx.nodes.get_node(node_id); - if ctx.nodes.flags(node_id).has_export_specifier() - && let AstKind::IdentifierReference(ident) = node.kind() - { - ctx.errors.borrow_mut().push(undefined_export(&ident.name, ident.span)); - } - } - } -} diff --git a/crates/oxc_semantic/src/unresolved_stack.rs b/crates/oxc_semantic/src/unresolved_stack.rs index 934cba4530058..afd7070ba9171 100644 --- a/crates/oxc_semantic/src/unresolved_stack.rs +++ b/crates/oxc_semantic/src/unresolved_stack.rs @@ -72,17 +72,6 @@ impl<'a> UnresolvedReferencesStack<'a> { self.current_scope_depth } - /// Get unresolved references hash map for current scope - #[inline] - pub(crate) fn root(&self) -> &TempUnresolvedReferences<'a> { - // SAFETY: `stack.len() > current_scope_depth` initially. - // Thereafter, `stack` never shrinks, only grows. - // `current_scope_depth` is only increased in `increment_scope_depth`, - // and it grows `stack` to ensure `stack.len()` always exceeds `current_scope_depth`. - // So this read is always guaranteed to be in bounds. - unsafe { self.stack.get_unchecked(0) } - } - /// Get unresolved references hash map for current scope #[inline] pub(crate) fn current_mut(&mut self) -> &mut TempUnresolvedReferences<'a> {