From 445603444f98402fb66a4a956b1023611bc66133 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Sat, 22 Jun 2024 11:43:41 +0000 Subject: [PATCH] feat(ast)!: add `IdentifierReference` to `ExportSpecifier` (#3820) closes #3795 closes #3796 --- crates/oxc_ast/src/ast/js.rs | 6 +- crates/oxc_ast/src/ast_impl/js.rs | 14 +++- crates/oxc_ast/src/ast_kind.rs | 3 + crates/oxc_ast/src/span.rs | 3 +- crates/oxc_ast/src/visit/visit.rs | 34 ++++++++- crates/oxc_codegen/src/gen.rs | 11 +-- .../oxc_isolated_declarations/src/module.rs | 2 +- crates/oxc_isolated_declarations/src/scope.rs | 6 +- .../listener_map.rs | 8 +-- .../src/rules/unicorn/no_thenable.rs | 17 ++--- crates/oxc_module_lexer/src/lib.rs | 3 +- crates/oxc_parser/src/js/module.rs | 23 +++--- crates/oxc_prettier/src/format/mod.rs | 3 +- crates/oxc_semantic/src/builder.rs | 71 +++++++++++++------ .../oxc_semantic/tests/integration/modules.rs | 20 +----- .../tests/integration/util/mod.rs | 3 +- .../tests/integration/util/symbol_tester.rs | 30 ++------ .../src/helpers/module_imports.rs | 5 +- .../src/typescript/annotations.rs | 5 -- crates/oxc_traverse/src/walk.rs | 7 +- tasks/transform_conformance/babel.snap.md | 5 +- 21 files changed, 162 insertions(+), 117 deletions(-) diff --git a/crates/oxc_ast/src/ast/js.rs b/crates/oxc_ast/src/ast/js.rs index e7ad53a3460f..4e1b4c6facdb 100644 --- a/crates/oxc_ast/src/ast/js.rs +++ b/crates/oxc_ast/src/ast/js.rs @@ -1895,7 +1895,7 @@ pub struct ExportDefaultDeclaration<'a> { #[cfg_attr(feature = "serialize", serde(flatten))] pub span: Span, pub declaration: ExportDefaultDeclarationKind<'a>, - pub exported: ModuleExportName<'a>, // `default` + pub exported: ModuleExportName<'a>, // the `default` Keyword } #[visited_node] @@ -1955,6 +1955,8 @@ pub enum ExportDefaultDeclarationKind<'a> { #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", serde(untagged))] pub enum ModuleExportName<'a> { - Identifier(IdentifierName<'a>), + IdentifierName(IdentifierName<'a>), + /// For `local` in `ExportSpecifier`: `foo` in `export { foo }` + IdentifierReference(IdentifierReference<'a>), StringLiteral(StringLiteral<'a>), } diff --git a/crates/oxc_ast/src/ast_impl/js.rs b/crates/oxc_ast/src/ast_impl/js.rs index 8ca8c38a66d6..000e6ebb7897 100644 --- a/crates/oxc_ast/src/ast_impl/js.rs +++ b/crates/oxc_ast/src/ast_impl/js.rs @@ -1447,7 +1447,8 @@ impl<'a> ExportDefaultDeclarationKind<'a> { impl<'a> fmt::Display for ModuleExportName<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let s = match self { - Self::Identifier(identifier) => identifier.name.to_string(), + Self::IdentifierName(identifier) => identifier.name.to_string(), + Self::IdentifierReference(identifier) => identifier.name.to_string(), Self::StringLiteral(literal) => format!(r#""{}""#, literal.value), }; write!(f, "{s}") @@ -1457,8 +1458,17 @@ impl<'a> fmt::Display for ModuleExportName<'a> { impl<'a> ModuleExportName<'a> { pub fn name(&self) -> Atom<'a> { match self { - Self::Identifier(identifier) => identifier.name.clone(), + Self::IdentifierName(identifier) => identifier.name.clone(), + Self::IdentifierReference(identifier) => identifier.name.clone(), Self::StringLiteral(literal) => literal.value.clone(), } } + + pub fn identifier_name(&self) -> Option> { + match self { + Self::IdentifierName(identifier) => Some(identifier.name.clone()), + Self::IdentifierReference(identifier) => Some(identifier.name.clone()), + Self::StringLiteral(_) => None, + } + } } diff --git a/crates/oxc_ast/src/ast_kind.rs b/crates/oxc_ast/src/ast_kind.rs index 0c02e6c0d9c5..b9cb6349bd45 100644 --- a/crates/oxc_ast/src/ast_kind.rs +++ b/crates/oxc_ast/src/ast_kind.rs @@ -126,6 +126,7 @@ ast_kinds! { ModuleDeclaration(&'a ModuleDeclaration<'a>), ImportDeclaration(&'a ImportDeclaration<'a>), ImportSpecifier(&'a ImportSpecifier<'a>), + ExportSpecifier(&'a ExportSpecifier<'a>), ImportDefaultSpecifier(&'a ImportDefaultSpecifier<'a>), ImportNamespaceSpecifier(&'a ImportNamespaceSpecifier<'a>), ExportDefaultDeclaration(&'a ExportDefaultDeclaration<'a>), @@ -469,6 +470,7 @@ impl<'a> GetSpan for AstKind<'a> { Self::ModuleDeclaration(x) => x.span(), Self::ImportDeclaration(x) => x.span, Self::ImportSpecifier(x) => x.span, + Self::ExportSpecifier(x) => x.span, Self::ImportDefaultSpecifier(x) => x.span, Self::ImportNamespaceSpecifier(x) => x.span, Self::ExportDefaultDeclaration(x) => x.span, @@ -675,6 +677,7 @@ impl<'a> AstKind<'a> { Self::ModuleDeclaration(_) => "ModuleDeclaration".into(), Self::ImportDeclaration(_) => "ImportDeclaration".into(), Self::ImportSpecifier(_) => "ImportSpecifier".into(), + Self::ExportSpecifier(_) => "ExportSpecifier".into(), Self::ImportDefaultSpecifier(_) => "ImportDefaultSpecifier".into(), Self::ImportNamespaceSpecifier(_) => "ImportNamespaceSpecifier".into(), Self::ExportDefaultDeclaration(_) => "ExportDefaultDeclaration".into(), diff --git a/crates/oxc_ast/src/span.rs b/crates/oxc_ast/src/span.rs index 715e7ef42e6a..ac9cef1c5b2e 100644 --- a/crates/oxc_ast/src/span.rs +++ b/crates/oxc_ast/src/span.rs @@ -208,7 +208,8 @@ impl<'a> GetSpan for ImportAttributeKey<'a> { impl<'a> GetSpan for ModuleExportName<'a> { fn span(&self) -> Span { match self { - Self::Identifier(identifier) => identifier.span, + Self::IdentifierName(identifier) => identifier.span, + Self::IdentifierReference(identifier) => identifier.span, Self::StringLiteral(literal) => literal.span, } } diff --git a/crates/oxc_ast/src/visit/visit.rs b/crates/oxc_ast/src/visit/visit.rs index aaf4cd713b03..72d9741957a3 100644 --- a/crates/oxc_ast/src/visit/visit.rs +++ b/crates/oxc_ast/src/visit/visit.rs @@ -617,6 +617,14 @@ pub trait Visit<'a>: Sized { walk_export_named_declaration(self, decl); } + fn visit_export_specifier(&mut self, specifier: &ExportSpecifier<'a>) { + walk_export_specifier(self, specifier); + } + + fn visit_module_export_name(&mut self, name: &ModuleExportName<'a>) { + walk_module_export_name(self, name); + } + fn visit_enum_member(&mut self, member: &TSEnumMember<'a>) { walk_enum_member(self, member); } @@ -2433,7 +2441,7 @@ pub mod walk { ) { let kind = AstKind::ImportSpecifier(visitor.alloc(specifier)); visitor.enter_node(kind); - // TODO: imported + visitor.visit_module_export_name(&specifier.imported); visitor.visit_binding_identifier(&specifier.local); visitor.leave_node(kind); } @@ -2496,12 +2504,36 @@ pub mod walk { if let Some(decl) = &decl.declaration { visitor.visit_declaration(decl); } + for export_specifier in &decl.specifiers { + visitor.visit_export_specifier(export_specifier); + } if let Some(ref source) = decl.source { visitor.visit_string_literal(source); } visitor.leave_node(kind); } + pub fn walk_export_specifier<'a, V: Visit<'a>>( + visitor: &mut V, + specifier: &ExportSpecifier<'a>, + ) { + let kind = AstKind::ExportSpecifier(visitor.alloc(specifier)); + visitor.enter_node(kind); + visitor.visit_module_export_name(&specifier.local); + visitor.visit_module_export_name(&specifier.exported); + visitor.leave_node(kind); + } + + pub fn walk_module_export_name<'a, V: Visit<'a>>(visitor: &mut V, name: &ModuleExportName<'a>) { + match name { + ModuleExportName::IdentifierName(ident) => visitor.visit_identifier_name(ident), + ModuleExportName::IdentifierReference(ident) => { + visitor.visit_identifier_reference(ident); + } + ModuleExportName::StringLiteral(ident) => visitor.visit_string_literal(ident), + } + } + pub fn walk_enum_member<'a, V: Visit<'a>>(visitor: &mut V, member: &TSEnumMember<'a>) { let kind = AstKind::TSEnumMember(visitor.alloc(member)); visitor.enter_node(kind); diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 5409990fbfa3..142dfd3c5ddb 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -784,7 +784,11 @@ impl<'a, const MINIFY: bool> Gen for ImportDeclaration<'a> { } let imported_name = match &spec.imported { - ModuleExportName::Identifier(identifier) => { + ModuleExportName::IdentifierName(identifier) => { + identifier.gen(p, ctx); + identifier.name.as_bytes() + } + ModuleExportName::IdentifierReference(identifier) => { identifier.gen(p, ctx); identifier.name.as_bytes() } @@ -961,9 +965,8 @@ impl<'a, const MINIFY: bool> Gen for ExportSpecifier<'a> { impl<'a, const MINIFY: bool> Gen for ModuleExportName<'a> { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { match self { - Self::Identifier(identifier) => { - p.print_str(identifier.name.as_bytes()); - } + Self::IdentifierName(identifier) => p.print_str(identifier.name.as_bytes()), + Self::IdentifierReference(identifier) => p.print_str(identifier.name.as_bytes()), Self::StringLiteral(literal) => literal.gen(p, ctx), }; } diff --git a/crates/oxc_isolated_declarations/src/module.rs b/crates/oxc_isolated_declarations/src/module.rs index cf0bb1fd8e51..b09bc1a21824 100644 --- a/crates/oxc_isolated_declarations/src/module.rs +++ b/crates/oxc_isolated_declarations/src/module.rs @@ -88,7 +88,7 @@ impl<'a> IsolatedDeclarations<'a> { }; declaration.map(|(var_decl, declaration)| { - let exported = ModuleExportName::Identifier(IdentifierName::new( + let exported = ModuleExportName::IdentifierName(IdentifierName::new( SPAN, self.ast.new_atom("default"), )); diff --git a/crates/oxc_isolated_declarations/src/scope.rs b/crates/oxc_isolated_declarations/src/scope.rs index e732a6f951c0..a68951fd9bf1 100644 --- a/crates/oxc_isolated_declarations/src/scope.rs +++ b/crates/oxc_isolated_declarations/src/scope.rs @@ -123,9 +123,9 @@ impl<'a> Visit<'a> for ScopeTree<'a> { fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) { for specifier in &decl.specifiers { - if let ModuleExportName::Identifier(ident) = &specifier.local { - self.add_type_reference(ident.name.clone()); - self.add_value_reference(ident.name.clone()); + if let Some(name) = specifier.local.identifier_name() { + self.add_type_reference(name.clone()); + self.add_value_reference(name); } } } diff --git a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs index 040e143491f7..da3080c4d92e 100644 --- a/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs +++ b/crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs @@ -8,7 +8,7 @@ use oxc_ast::{ FormalParameter, Function, IdentifierReference, JSXAttribute, JSXAttributeItem, JSXAttributeValue, JSXChild, JSXElement, JSXElementName, JSXExpression, JSXExpressionContainer, JSXFragment, JSXIdentifier, JSXOpeningElement, LogicalExpression, - MemberExpression, ModuleExportName, NewExpression, ObjectExpression, ObjectPropertyKind, + MemberExpression, NewExpression, ObjectExpression, ObjectPropertyKind, ParenthesizedExpression, PrivateFieldExpression, Program, PropertyKey, SequenceExpression, SimpleAssignmentTarget, Statement, StaticMemberExpression, SwitchCase, ThisExpression, UnaryExpression, VariableDeclarator, @@ -198,10 +198,8 @@ impl<'a> ListenerMap for ExportSpecifier<'a> { let ctx = options.ctx; let symbol_table = ctx.symbols(); if has_comment_about_side_effect_check(self.exported.span(), ctx) { - let ModuleExportName::Identifier(ident_name) = &self.exported else { - return; - }; - let Some(symbol_id) = options.ctx.symbols().get_symbol_id_from_name(&ident_name.name) + let Some(name) = self.exported.identifier_name() else { return }; + let Some(symbol_id) = options.ctx.symbols().get_symbol_id_from_name(name.as_str()) else { return; }; diff --git a/crates/oxc_linter/src/rules/unicorn/no_thenable.rs b/crates/oxc_linter/src/rules/unicorn/no_thenable.rs index 090a9100ca53..56eb3f7d4682 100644 --- a/crates/oxc_linter/src/rules/unicorn/no_thenable.rs +++ b/crates/oxc_linter/src/rules/unicorn/no_thenable.rs @@ -2,13 +2,13 @@ use oxc_ast::{ ast::{ match_expression, Argument, ArrayExpressionElement, AssignmentExpression, AssignmentTarget, BindingPatternKind, CallExpression, Declaration, Expression, ModuleDeclaration, - ModuleExportName, ObjectPropertyKind, PropertyKey, VariableDeclarator, + ObjectPropertyKind, PropertyKey, VariableDeclarator, }, AstKind, }; use oxc_diagnostics::OxcDiagnostic; use oxc_macros::declare_oxc_lint; -use oxc_span::Span; +use oxc_span::{GetSpan, Span}; use crate::{context::LintContext, rule::Rule, AstNode}; @@ -107,17 +107,8 @@ impl Rule for NoThenable { } // check specifier for spec in &decl.specifiers { - match spec.exported { - ModuleExportName::Identifier(ref ident) => { - if ident.name == "then" { - ctx.diagnostic(export(ident.span)); - } - } - ModuleExportName::StringLiteral(ref lit) => { - if lit.value == "then" { - ctx.diagnostic(export(lit.span)); - } - } + if spec.exported.name() == "then" { + ctx.diagnostic(export(spec.exported.span())); } } } diff --git a/crates/oxc_module_lexer/src/lib.rs b/crates/oxc_module_lexer/src/lib.rs index 83d367d2accd..3484fba3c28c 100644 --- a/crates/oxc_module_lexer/src/lib.rs +++ b/crates/oxc_module_lexer/src/lib.rs @@ -220,7 +220,8 @@ impl<'a> Visit<'a> for ModuleLexer<'a> { // export { named } self.exports.extend(decl.specifiers.iter().map(|s| { let (exported_start, exported_end) = match &s.exported { - ModuleExportName::Identifier(ident) => (ident.span.start, ident.span.end), + ModuleExportName::IdentifierName(ident) => (ident.span.start, ident.span.end), + ModuleExportName::IdentifierReference(ident) => (ident.span.start, ident.span.end), // +1 -1 to remove the string quotes ModuleExportName::StringLiteral(s) => (s.span.start + 1, s.span.end - 1), }; diff --git a/crates/oxc_parser/src/js/module.rs b/crates/oxc_parser/src/js/module.rs index c5fd9440964b..d2d614e002d0 100644 --- a/crates/oxc_parser/src/js/module.rs +++ b/crates/oxc_parser/src/js/module.rs @@ -218,7 +218,7 @@ impl<'a> ParserImpl<'a> { span: Span, ) -> Result>> { let export_kind = self.parse_import_or_export_kind(); - let specifiers = + let mut specifiers = self.context(Context::empty(), self.ctx, ExportNamedSpecifiers::parse)?.elements; let (source, with_clause) = if self.eat(Kind::From) && self.cur_kind().is_literal() { let source = self.parse_literal_string()?; @@ -229,7 +229,7 @@ impl<'a> ParserImpl<'a> { // ExportDeclaration : export NamedExports ; if source.is_none() { - for specifier in &specifiers { + for specifier in specifiers.iter_mut() { match &specifier.local { // It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals. ModuleExportName::StringLiteral(literal) => { @@ -242,18 +242,25 @@ impl<'a> ParserImpl<'a> { // For each IdentifierName n in ReferencedBindings of NamedExports: // It is a Syntax Error if StringValue of n is a ReservedWord or the StringValue of n // is one of "implements", "interface", "let", "package", "private", "protected", "public", or "static". - ModuleExportName::Identifier(id) => { - let match_result = Kind::match_keyword(&id.name); + ModuleExportName::IdentifierName(ident) => { + let match_result = Kind::match_keyword(&ident.name); if match_result.is_reserved_keyword() || match_result.is_future_reserved_keyword() { self.error(diagnostics::export_reserved_word( &specifier.local.to_string(), &specifier.exported.to_string(), - id.span, + ident.span, )); } + + // `local` becomes a reference for `export { local }`. + specifier.local = ModuleExportName::IdentifierReference( + self.ast.identifier_reference(ident.span, ident.name.as_str()), + ); } + // No prior code path should lead to parsing `ModuleExportName` as `IdentifierReference`. + ModuleExportName::IdentifierReference(_) => unreachable!(), } } } @@ -343,7 +350,7 @@ impl<'a> ParserImpl<'a> { decl } }; - let exported = ModuleExportName::Identifier(exported); + let exported = ModuleExportName::IdentifierName(exported); let span = self.end_span(span); Ok(self.ast.export_default_declaration(span, declaration, exported)) } @@ -400,7 +407,7 @@ impl<'a> ParserImpl<'a> { } else { let local = self.parse_binding_identifier()?; let imported = IdentifierName { span: local.span, name: local.name.clone() }; - (ModuleExportName::Identifier(imported), local) + (ModuleExportName::IdentifierName(imported), local) }; Ok(self.ast.alloc(ImportSpecifier { span: self.end_span(specifier_span), @@ -424,7 +431,7 @@ impl<'a> ParserImpl<'a> { }; Ok(ModuleExportName::StringLiteral(literal)) } - _ => Ok(ModuleExportName::Identifier(self.parse_identifier_name()?)), + _ => Ok(ModuleExportName::IdentifierName(self.parse_identifier_name()?)), } } diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index 206aa1558f9a..164e70a8e812 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -1209,7 +1209,8 @@ impl<'a> Format<'a> for ExportSpecifier<'a> { impl<'a> Format<'a> for ModuleExportName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { - Self::Identifier(ident) => ident.format(p), + Self::IdentifierName(ident) => ident.format(p), + Self::IdentifierReference(ident) => ident.format(p), Self::StringLiteral(literal) => literal.format(p), } } diff --git a/crates/oxc_semantic/src/builder.rs b/crates/oxc_semantic/src/builder.rs index 5ea61c63fb23..9aa732819b86 100644 --- a/crates/oxc_semantic/src/builder.rs +++ b/crates/oxc_semantic/src/builder.rs @@ -10,10 +10,7 @@ use oxc_cfg::{ }; use oxc_diagnostics::OxcDiagnostic; use oxc_span::{CompactStr, SourceType, Span}; -use oxc_syntax::{ - module_record::{ExportImportName, ModuleRecord}, - operator::AssignmentOperator, -}; +use oxc_syntax::{module_record::ModuleRecord, operator::AssignmentOperator}; use crate::{ binder::Binder, @@ -388,22 +385,29 @@ impl<'a> SemanticBuilder<'a> { self.symbols.add_redeclare_variable(symbol_id, span); } - fn add_export_flag_for_export_identifier(&mut self) { - self.module_record.indirect_export_entries.iter().for_each(|entry| { - if let ExportImportName::Name(name) = &entry.import_name { - if let Some(symbol_id) = self.symbols.get_symbol_id_from_name(name.name()) { - self.symbols.union_flag(symbol_id, SymbolFlags::Export); + fn add_export_flag_to_export_identifiers(&mut self, program: &Program<'a>) { + for stmt in &program.body { + if let Statement::ExportDefaultDeclaration(decl) = stmt { + if let ExportDefaultDeclarationKind::Identifier(ident) = &decl.declaration { + self.add_export_flag_to_identifier(ident.name.as_str()); } } - }); - - self.module_record.local_export_entries.iter().for_each(|entry| { - if let Some(name) = entry.local_name.name() { - if let Some(symbol_id) = self.scope.get_root_binding(name.as_str()) { - self.symbols.union_flag(symbol_id, SymbolFlags::Export); + if let Statement::ExportNamedDeclaration(decl) = stmt { + for specifier in &decl.specifiers { + if specifier.export_kind.is_value() { + if let Some(name) = specifier.local.identifier_name() { + self.add_export_flag_to_identifier(name.as_str()); + } + } } } - }); + } + } + + fn add_export_flag_to_identifier(&mut self, name: &str) { + if let Some(symbol_id) = self.scope.get_binding(self.current_scope_id, name) { + self.symbols.union_flag(symbol_id, SymbolFlags::Export); + } } } @@ -1680,8 +1684,20 @@ impl<'a> SemanticBuilder<'a> { /* cfg */ match kind { - AstKind::ExportDefaultDeclaration(_) | AstKind::ExportNamedDeclaration(_) => { + AstKind::ExportDefaultDeclaration(_) => { + self.current_symbol_flags |= SymbolFlags::Export; + } + AstKind::ExportNamedDeclaration(decl) => { self.current_symbol_flags |= SymbolFlags::Export; + if decl.export_kind.is_type() { + self.current_reference_flag = ReferenceFlag::Type; + } + } + AstKind::ExportAllDeclaration(s) if s.export_kind.is_type() => { + self.current_reference_flag = ReferenceFlag::Type; + } + AstKind::ExportSpecifier(s) if s.export_kind.is_type() => { + self.current_reference_flag = ReferenceFlag::Type; } AstKind::ImportSpecifier(specifier) => { specifier.bind(self); @@ -1768,6 +1784,9 @@ impl<'a> SemanticBuilder<'a> { AstKind::TSTypeParameter(type_parameter) => { type_parameter.bind(self); } + AstKind::ExportSpecifier(s) if s.export_kind.is_type() => { + self.current_reference_flag = ReferenceFlag::Type; + } AstKind::TSTypeName(_) => { self.current_reference_flag = ReferenceFlag::Type; } @@ -1815,15 +1834,27 @@ impl<'a> SemanticBuilder<'a> { #[allow(clippy::single_match)] fn leave_kind(&mut self, kind: AstKind<'a>) { match kind { - AstKind::Program(_) => { - self.add_export_flag_for_export_identifier(); + AstKind::Program(program) => { + self.add_export_flag_to_export_identifiers(program); } AstKind::Class(_) => { self.current_node_flags -= NodeFlags::Class; self.class_table_builder.pop_class(); } - AstKind::ExportDefaultDeclaration(_) | AstKind::ExportNamedDeclaration(_) => { + AstKind::ExportDefaultDeclaration(_) => { + self.current_symbol_flags -= SymbolFlags::Export; + } + AstKind::ExportNamedDeclaration(decl) => { self.current_symbol_flags -= SymbolFlags::Export; + if decl.export_kind.is_type() { + self.current_reference_flag -= ReferenceFlag::Type; + } + } + AstKind::ExportAllDeclaration(s) if s.export_kind.is_type() => { + self.current_reference_flag -= ReferenceFlag::Type; + } + AstKind::ExportSpecifier(s) if s.export_kind.is_type() => { + self.current_reference_flag -= ReferenceFlag::Type; } AstKind::LabeledStatement(_) => self.label_builder.leave(), AstKind::StaticBlock(_) => { diff --git a/crates/oxc_semantic/tests/integration/modules.rs b/crates/oxc_semantic/tests/integration/modules.rs index d2150872cd6d..bd40ae95b66e 100644 --- a/crates/oxc_semantic/tests/integration/modules.rs +++ b/crates/oxc_semantic/tests/integration/modules.rs @@ -1,5 +1,4 @@ use oxc_semantic::SymbolFlags; -use oxc_syntax::module_record::ExportExportName; use crate::util::SemanticTester; @@ -26,9 +25,7 @@ fn test_exports() { ); test.has_some_symbol("foo").is_exported().test(); - - // FIXME: failing - // test.has_some_symbol("defaultExport").is_exported().test(); + test.has_some_symbol("defaultExport").is_exported().test(); } #[test] @@ -118,21 +115,6 @@ fn test_exported_default_class() { test.has_class("Foo"); test.has_some_symbol("a").is_not_exported().test(); test.has_some_symbol("T").is_not_exported().test(); - - { - let foo_test = test.has_some_symbol("Foo"); - let (semantic, _) = foo_test.inner(); - let m = semantic.module_record(); - let local_default_entry = m - .local_export_entries - .iter() - .find(|export| matches!(export.export_name, ExportExportName::Default(_))) - .unwrap(); - assert!(local_default_entry.local_name.name().is_some_and(|name| name == &"Foo")); - assert!(!m.exported_bindings.contains_key("Foo")); - assert!(m.export_default.is_some()); - foo_test.contains_flags(SymbolFlags::Export).test(); - } } // FIXME diff --git a/crates/oxc_semantic/tests/integration/util/mod.rs b/crates/oxc_semantic/tests/integration/util/mod.rs index 03256d5ecf53..df4ea82b3345 100644 --- a/crates/oxc_semantic/tests/integration/util/mod.rs +++ b/crates/oxc_semantic/tests/integration/util/mod.rs @@ -1,7 +1,7 @@ mod class_tester; mod expect; mod symbol_tester; -use std::{path::PathBuf, sync::Arc}; +use std::sync::Arc; pub use class_tester::ClassTester; pub use expect::Expect; @@ -88,7 +88,6 @@ impl<'a> SemanticTester<'a> { let semantic_ret = SemanticBuilder::new(self.source_text, self.source_type) .with_check_syntax_error(true) .with_trivias(parse.trivias) - .build_module_record(PathBuf::new(), program) .with_cfg(self.cfg) .build(program); diff --git a/crates/oxc_semantic/tests/integration/util/symbol_tester.rs b/crates/oxc_semantic/tests/integration/util/symbol_tester.rs index 2fade7fd403a..ccc1929e9f78 100644 --- a/crates/oxc_semantic/tests/integration/util/symbol_tester.rs +++ b/crates/oxc_semantic/tests/integration/util/symbol_tester.rs @@ -159,21 +159,12 @@ impl<'a> SymbolTester<'a> { self.test_result = match self.test_result { Ok(symbol_id) => { let binding = self.target_symbol_name.clone(); - let is_in_module_record = - self.semantic.module_record().exported_bindings.contains_key(binding.as_str()) - && self.semantic.scopes().get_root_binding(&binding) == Some(symbol_id); - let has_export_flag = self.semantic.symbols().get_flag(symbol_id).is_export(); - match (is_in_module_record, has_export_flag) { - (false, false) => Err(OxcDiagnostic::error(format!( - "Expected {binding} to be exported. Symbol is not in module record and does not have SymbolFlags::Export" - ))), - (false, true) => Err(OxcDiagnostic::error(format!( - "Expected {binding} to be exported. Symbol is not in module record, but has SymbolFlags::Export" - ))), - (true, false) => Err(OxcDiagnostic::error(format!( - "Expected {binding} to be exported. Symbol is in module record, but does not have SymbolFlags::Export" - ))), - (true, true) => Ok(symbol_id), + if self.semantic.symbols().get_flag(symbol_id).is_export() { + Ok(symbol_id) + } else { + Err(OxcDiagnostic::error(format!( + "Expected {binding} to be exported with SymbolFlags::Export" + ))) } } e => e, @@ -191,15 +182,6 @@ impl<'a> SymbolTester<'a> { Err(OxcDiagnostic::error(format!( "Expected {binding} to not be exported. Symbol has export flag." ))) - } else if self - .semantic - .module_record() - .exported_bindings - .contains_key(binding.as_str()) - { - Err(OxcDiagnostic::error(format!( - "Expected {binding} to not be exported. Binding is in the module record" - ))) } else { Ok(symbol_id) } diff --git a/crates/oxc_transformer/src/helpers/module_imports.rs b/crates/oxc_transformer/src/helpers/module_imports.rs index d63d2806e0e2..a312ebfb3176 100644 --- a/crates/oxc_transformer/src/helpers/module_imports.rs +++ b/crates/oxc_transformer/src/helpers/module_imports.rs @@ -90,7 +90,10 @@ impl<'a> ModuleImports<'a> { let local = name.local.unwrap_or_else(|| name.imported.clone()); ImportDeclarationSpecifier::ImportSpecifier(self.ast.alloc(ImportSpecifier { span: SPAN, - imported: ModuleExportName::Identifier(IdentifierName::new(SPAN, name.imported)), + imported: ModuleExportName::IdentifierName(IdentifierName::new( + SPAN, + name.imported, + )), local: BindingIdentifier { span: SPAN, name: local, diff --git a/crates/oxc_transformer/src/typescript/annotations.rs b/crates/oxc_transformer/src/typescript/annotations.rs index 448c357e3fbb..5bd33549d789 100644 --- a/crates/oxc_transformer/src/typescript/annotations.rs +++ b/crates/oxc_transformer/src/typescript/annotations.rs @@ -516,11 +516,6 @@ impl<'a> TypeScriptAnnotations<'a> { pub fn has_value_reference(&self, name: &str, ctx: &TraverseCtx<'a>) -> bool { if let Some(symbol_id) = ctx.scopes().get_root_binding(name) { - if ctx.symbols().get_flag(symbol_id).is_export() - && !self.type_identifier_names.contains(name) - { - return true; - } if ctx .symbols() .get_resolved_references(symbol_id) diff --git a/crates/oxc_traverse/src/walk.rs b/crates/oxc_traverse/src/walk.rs index 12ce0e145593..6bb98779be16 100644 --- a/crates/oxc_traverse/src/walk.rs +++ b/crates/oxc_traverse/src/walk.rs @@ -3109,7 +3109,12 @@ pub(crate) unsafe fn walk_module_export_name<'a, Tr: Traverse<'a>>( ) { traverser.enter_module_export_name(&mut *node, ctx); match &mut *node { - ModuleExportName::Identifier(node) => walk_identifier_name(traverser, node as *mut _, ctx), + ModuleExportName::IdentifierName(node) => { + walk_identifier_name(traverser, node as *mut _, ctx) + } + ModuleExportName::IdentifierReference(node) => { + walk_identifier_reference(traverser, node as *mut _, ctx) + } ModuleExportName::StringLiteral(node) => { walk_string_literal(traverser, node as *mut _, ctx) } diff --git a/tasks/transform_conformance/babel.snap.md b/tasks/transform_conformance/babel.snap.md index 7ed3a259fb57..94041f1bc2c8 100644 --- a/tasks/transform_conformance/babel.snap.md +++ b/tasks/transform_conformance/babel.snap.md @@ -1,6 +1,6 @@ commit: 12619ffe -Passed: 472/927 +Passed: 473/927 # All Passed: * babel-preset-react @@ -445,11 +445,10 @@ Passed: 472/927 * opts/optimizeConstEnums/input.ts * opts/rewriteImportExtensions/input.ts -# babel-plugin-transform-typescript (128/151) +# babel-plugin-transform-typescript (129/151) * enum/mix-references/input.ts * enum/ts5.0-const-foldable/input.ts * exports/declared-types/input.ts -* exports/imported-types/input.ts * exports/interface/input.ts * imports/elide-no-import-specifiers/input.ts * imports/elision-locations/input.ts