feat(ast)!: add IdentifierReference to ExportSpecifier (#3820)

closes #3795
closes #3796
This commit is contained in:
Boshen 2024-06-22 11:43:41 +00:00
parent 99a40ce6ac
commit 445603444f
21 changed files with 163 additions and 118 deletions

View file

@ -1895,7 +1895,7 @@ pub struct ExportDefaultDeclaration<'a> {
#[cfg_attr(feature = "serialize", serde(flatten))] #[cfg_attr(feature = "serialize", serde(flatten))]
pub span: Span, pub span: Span,
pub declaration: ExportDefaultDeclarationKind<'a>, pub declaration: ExportDefaultDeclarationKind<'a>,
pub exported: ModuleExportName<'a>, // `default` pub exported: ModuleExportName<'a>, // the `default` Keyword
} }
#[visited_node] #[visited_node]
@ -1955,6 +1955,8 @@ pub enum ExportDefaultDeclarationKind<'a> {
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))] #[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
#[cfg_attr(feature = "serialize", serde(untagged))] #[cfg_attr(feature = "serialize", serde(untagged))]
pub enum ModuleExportName<'a> { pub enum ModuleExportName<'a> {
Identifier(IdentifierName<'a>), IdentifierName(IdentifierName<'a>),
/// For `local` in `ExportSpecifier`: `foo` in `export { foo }`
IdentifierReference(IdentifierReference<'a>),
StringLiteral(StringLiteral<'a>), StringLiteral(StringLiteral<'a>),
} }

View file

@ -1447,7 +1447,8 @@ impl<'a> ExportDefaultDeclarationKind<'a> {
impl<'a> fmt::Display for ModuleExportName<'a> { impl<'a> fmt::Display for ModuleExportName<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let s = match self { 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), Self::StringLiteral(literal) => format!(r#""{}""#, literal.value),
}; };
write!(f, "{s}") write!(f, "{s}")
@ -1457,8 +1458,17 @@ impl<'a> fmt::Display for ModuleExportName<'a> {
impl<'a> ModuleExportName<'a> { impl<'a> ModuleExportName<'a> {
pub fn name(&self) -> Atom<'a> { pub fn name(&self) -> Atom<'a> {
match self { 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(), Self::StringLiteral(literal) => literal.value.clone(),
} }
} }
pub fn identifier_name(&self) -> Option<Atom<'a>> {
match self {
Self::IdentifierName(identifier) => Some(identifier.name.clone()),
Self::IdentifierReference(identifier) => Some(identifier.name.clone()),
Self::StringLiteral(_) => None,
}
}
} }

View file

@ -126,6 +126,7 @@ ast_kinds! {
ModuleDeclaration(&'a ModuleDeclaration<'a>), ModuleDeclaration(&'a ModuleDeclaration<'a>),
ImportDeclaration(&'a ImportDeclaration<'a>), ImportDeclaration(&'a ImportDeclaration<'a>),
ImportSpecifier(&'a ImportSpecifier<'a>), ImportSpecifier(&'a ImportSpecifier<'a>),
ExportSpecifier(&'a ExportSpecifier<'a>),
ImportDefaultSpecifier(&'a ImportDefaultSpecifier<'a>), ImportDefaultSpecifier(&'a ImportDefaultSpecifier<'a>),
ImportNamespaceSpecifier(&'a ImportNamespaceSpecifier<'a>), ImportNamespaceSpecifier(&'a ImportNamespaceSpecifier<'a>),
ExportDefaultDeclaration(&'a ExportDefaultDeclaration<'a>), ExportDefaultDeclaration(&'a ExportDefaultDeclaration<'a>),
@ -469,6 +470,7 @@ impl<'a> GetSpan for AstKind<'a> {
Self::ModuleDeclaration(x) => x.span(), Self::ModuleDeclaration(x) => x.span(),
Self::ImportDeclaration(x) => x.span, Self::ImportDeclaration(x) => x.span,
Self::ImportSpecifier(x) => x.span, Self::ImportSpecifier(x) => x.span,
Self::ExportSpecifier(x) => x.span,
Self::ImportDefaultSpecifier(x) => x.span, Self::ImportDefaultSpecifier(x) => x.span,
Self::ImportNamespaceSpecifier(x) => x.span, Self::ImportNamespaceSpecifier(x) => x.span,
Self::ExportDefaultDeclaration(x) => x.span, Self::ExportDefaultDeclaration(x) => x.span,
@ -675,6 +677,7 @@ impl<'a> AstKind<'a> {
Self::ModuleDeclaration(_) => "ModuleDeclaration".into(), Self::ModuleDeclaration(_) => "ModuleDeclaration".into(),
Self::ImportDeclaration(_) => "ImportDeclaration".into(), Self::ImportDeclaration(_) => "ImportDeclaration".into(),
Self::ImportSpecifier(_) => "ImportSpecifier".into(), Self::ImportSpecifier(_) => "ImportSpecifier".into(),
Self::ExportSpecifier(_) => "ExportSpecifier".into(),
Self::ImportDefaultSpecifier(_) => "ImportDefaultSpecifier".into(), Self::ImportDefaultSpecifier(_) => "ImportDefaultSpecifier".into(),
Self::ImportNamespaceSpecifier(_) => "ImportNamespaceSpecifier".into(), Self::ImportNamespaceSpecifier(_) => "ImportNamespaceSpecifier".into(),
Self::ExportDefaultDeclaration(_) => "ExportDefaultDeclaration".into(), Self::ExportDefaultDeclaration(_) => "ExportDefaultDeclaration".into(),

View file

@ -208,7 +208,8 @@ impl<'a> GetSpan for ImportAttributeKey<'a> {
impl<'a> GetSpan for ModuleExportName<'a> { impl<'a> GetSpan for ModuleExportName<'a> {
fn span(&self) -> Span { fn span(&self) -> Span {
match self { match self {
Self::Identifier(identifier) => identifier.span, Self::IdentifierName(identifier) => identifier.span,
Self::IdentifierReference(identifier) => identifier.span,
Self::StringLiteral(literal) => literal.span, Self::StringLiteral(literal) => literal.span,
} }
} }

View file

@ -617,6 +617,14 @@ pub trait Visit<'a>: Sized {
walk_export_named_declaration(self, decl); 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>) { fn visit_enum_member(&mut self, member: &TSEnumMember<'a>) {
walk_enum_member(self, member); walk_enum_member(self, member);
} }
@ -2433,7 +2441,7 @@ pub mod walk {
) { ) {
let kind = AstKind::ImportSpecifier(visitor.alloc(specifier)); let kind = AstKind::ImportSpecifier(visitor.alloc(specifier));
visitor.enter_node(kind); visitor.enter_node(kind);
// TODO: imported visitor.visit_module_export_name(&specifier.imported);
visitor.visit_binding_identifier(&specifier.local); visitor.visit_binding_identifier(&specifier.local);
visitor.leave_node(kind); visitor.leave_node(kind);
} }
@ -2496,12 +2504,36 @@ pub mod walk {
if let Some(decl) = &decl.declaration { if let Some(decl) = &decl.declaration {
visitor.visit_declaration(decl); visitor.visit_declaration(decl);
} }
for export_specifier in &decl.specifiers {
visitor.visit_export_specifier(export_specifier);
}
if let Some(ref source) = decl.source { if let Some(ref source) = decl.source {
visitor.visit_string_literal(source); visitor.visit_string_literal(source);
} }
visitor.leave_node(kind); 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>) { pub fn walk_enum_member<'a, V: Visit<'a>>(visitor: &mut V, member: &TSEnumMember<'a>) {
let kind = AstKind::TSEnumMember(visitor.alloc(member)); let kind = AstKind::TSEnumMember(visitor.alloc(member));
visitor.enter_node(kind); visitor.enter_node(kind);

View file

@ -784,7 +784,11 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ImportDeclaration<'a> {
} }
let imported_name = match &spec.imported { 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.gen(p, ctx);
identifier.name.as_bytes() identifier.name.as_bytes()
} }
@ -961,9 +965,8 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ExportSpecifier<'a> {
impl<'a, const MINIFY: bool> Gen<MINIFY> for ModuleExportName<'a> { impl<'a, const MINIFY: bool> Gen<MINIFY> for ModuleExportName<'a> {
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) { fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
match self { match self {
Self::Identifier(identifier) => { Self::IdentifierName(identifier) => p.print_str(identifier.name.as_bytes()),
p.print_str(identifier.name.as_bytes()); Self::IdentifierReference(identifier) => p.print_str(identifier.name.as_bytes()),
}
Self::StringLiteral(literal) => literal.gen(p, ctx), Self::StringLiteral(literal) => literal.gen(p, ctx),
}; };
} }

View file

@ -88,7 +88,7 @@ impl<'a> IsolatedDeclarations<'a> {
}; };
declaration.map(|(var_decl, declaration)| { declaration.map(|(var_decl, declaration)| {
let exported = ModuleExportName::Identifier(IdentifierName::new( let exported = ModuleExportName::IdentifierName(IdentifierName::new(
SPAN, SPAN,
self.ast.new_atom("default"), self.ast.new_atom("default"),
)); ));

View file

@ -123,9 +123,9 @@ impl<'a> Visit<'a> for ScopeTree<'a> {
fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) { fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) {
for specifier in &decl.specifiers { for specifier in &decl.specifiers {
if let ModuleExportName::Identifier(ident) = &specifier.local { if let Some(name) = specifier.local.identifier_name() {
self.add_type_reference(ident.name.clone()); self.add_type_reference(name.clone());
self.add_value_reference(ident.name.clone()); self.add_value_reference(name);
} }
} }
} }

View file

@ -8,7 +8,7 @@ use oxc_ast::{
FormalParameter, Function, IdentifierReference, JSXAttribute, JSXAttributeItem, FormalParameter, Function, IdentifierReference, JSXAttribute, JSXAttributeItem,
JSXAttributeValue, JSXChild, JSXElement, JSXElementName, JSXExpression, JSXAttributeValue, JSXChild, JSXElement, JSXElementName, JSXExpression,
JSXExpressionContainer, JSXFragment, JSXIdentifier, JSXOpeningElement, LogicalExpression, JSXExpressionContainer, JSXFragment, JSXIdentifier, JSXOpeningElement, LogicalExpression,
MemberExpression, ModuleExportName, NewExpression, ObjectExpression, ObjectPropertyKind, MemberExpression, NewExpression, ObjectExpression, ObjectPropertyKind,
ParenthesizedExpression, PrivateFieldExpression, Program, PropertyKey, SequenceExpression, ParenthesizedExpression, PrivateFieldExpression, Program, PropertyKey, SequenceExpression,
SimpleAssignmentTarget, Statement, StaticMemberExpression, SwitchCase, ThisExpression, SimpleAssignmentTarget, Statement, StaticMemberExpression, SwitchCase, ThisExpression,
UnaryExpression, VariableDeclarator, UnaryExpression, VariableDeclarator,
@ -198,10 +198,8 @@ impl<'a> ListenerMap for ExportSpecifier<'a> {
let ctx = options.ctx; let ctx = options.ctx;
let symbol_table = ctx.symbols(); let symbol_table = ctx.symbols();
if has_comment_about_side_effect_check(self.exported.span(), ctx) { if has_comment_about_side_effect_check(self.exported.span(), ctx) {
let ModuleExportName::Identifier(ident_name) = &self.exported else { let Some(name) = self.exported.identifier_name() else { return };
return; let Some(symbol_id) = options.ctx.symbols().get_symbol_id_from_name(name.as_str())
};
let Some(symbol_id) = options.ctx.symbols().get_symbol_id_from_name(&ident_name.name)
else { else {
return; return;
}; };

View file

@ -2,13 +2,13 @@ use oxc_ast::{
ast::{ ast::{
match_expression, Argument, ArrayExpressionElement, AssignmentExpression, AssignmentTarget, match_expression, Argument, ArrayExpressionElement, AssignmentExpression, AssignmentTarget,
BindingPatternKind, CallExpression, Declaration, Expression, ModuleDeclaration, BindingPatternKind, CallExpression, Declaration, Expression, ModuleDeclaration,
ModuleExportName, ObjectPropertyKind, PropertyKey, VariableDeclarator, ObjectPropertyKind, PropertyKey, VariableDeclarator,
}, },
AstKind, AstKind,
}; };
use oxc_diagnostics::OxcDiagnostic; use oxc_diagnostics::OxcDiagnostic;
use oxc_macros::declare_oxc_lint; use oxc_macros::declare_oxc_lint;
use oxc_span::Span; use oxc_span::{GetSpan, Span};
use crate::{context::LintContext, rule::Rule, AstNode}; use crate::{context::LintContext, rule::Rule, AstNode};
@ -107,17 +107,8 @@ impl Rule for NoThenable {
} }
// check specifier // check specifier
for spec in &decl.specifiers { for spec in &decl.specifiers {
match spec.exported { if spec.exported.name() == "then" {
ModuleExportName::Identifier(ref ident) => { ctx.diagnostic(export(spec.exported.span()));
if ident.name == "then" {
ctx.diagnostic(export(ident.span));
}
}
ModuleExportName::StringLiteral(ref lit) => {
if lit.value == "then" {
ctx.diagnostic(export(lit.span));
}
}
} }
} }
} }

View file

@ -220,7 +220,8 @@ impl<'a> Visit<'a> for ModuleLexer<'a> {
// export { named } // export { named }
self.exports.extend(decl.specifiers.iter().map(|s| { self.exports.extend(decl.specifiers.iter().map(|s| {
let (exported_start, exported_end) = match &s.exported { 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 // +1 -1 to remove the string quotes
ModuleExportName::StringLiteral(s) => (s.span.start + 1, s.span.end - 1), ModuleExportName::StringLiteral(s) => (s.span.start + 1, s.span.end - 1),
}; };

View file

@ -218,7 +218,7 @@ impl<'a> ParserImpl<'a> {
span: Span, span: Span,
) -> Result<Box<'a, ExportNamedDeclaration<'a>>> { ) -> Result<Box<'a, ExportNamedDeclaration<'a>>> {
let export_kind = self.parse_import_or_export_kind(); let export_kind = self.parse_import_or_export_kind();
let specifiers = let mut specifiers =
self.context(Context::empty(), self.ctx, ExportNamedSpecifiers::parse)?.elements; 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, with_clause) = if self.eat(Kind::From) && self.cur_kind().is_literal() {
let source = self.parse_literal_string()?; let source = self.parse_literal_string()?;
@ -229,7 +229,7 @@ impl<'a> ParserImpl<'a> {
// ExportDeclaration : export NamedExports ; // ExportDeclaration : export NamedExports ;
if source.is_none() { if source.is_none() {
for specifier in &specifiers { for specifier in specifiers.iter_mut() {
match &specifier.local { match &specifier.local {
// It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals. // It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals.
ModuleExportName::StringLiteral(literal) => { ModuleExportName::StringLiteral(literal) => {
@ -242,18 +242,25 @@ impl<'a> ParserImpl<'a> {
// For each IdentifierName n in ReferencedBindings of NamedExports: // 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 // 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". // is one of "implements", "interface", "let", "package", "private", "protected", "public", or "static".
ModuleExportName::Identifier(id) => { ModuleExportName::IdentifierName(ident) => {
let match_result = Kind::match_keyword(&id.name); let match_result = Kind::match_keyword(&ident.name);
if match_result.is_reserved_keyword() if match_result.is_reserved_keyword()
|| match_result.is_future_reserved_keyword() || match_result.is_future_reserved_keyword()
{ {
self.error(diagnostics::export_reserved_word( self.error(diagnostics::export_reserved_word(
&specifier.local.to_string(), &specifier.local.to_string(),
&specifier.exported.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 decl
} }
}; };
let exported = ModuleExportName::Identifier(exported); let exported = ModuleExportName::IdentifierName(exported);
let span = self.end_span(span); let span = self.end_span(span);
Ok(self.ast.export_default_declaration(span, declaration, exported)) Ok(self.ast.export_default_declaration(span, declaration, exported))
} }
@ -400,7 +407,7 @@ impl<'a> ParserImpl<'a> {
} else { } else {
let local = self.parse_binding_identifier()?; let local = self.parse_binding_identifier()?;
let imported = IdentifierName { span: local.span, name: local.name.clone() }; let imported = IdentifierName { span: local.span, name: local.name.clone() };
(ModuleExportName::Identifier(imported), local) (ModuleExportName::IdentifierName(imported), local)
}; };
Ok(self.ast.alloc(ImportSpecifier { Ok(self.ast.alloc(ImportSpecifier {
span: self.end_span(specifier_span), span: self.end_span(specifier_span),
@ -424,7 +431,7 @@ impl<'a> ParserImpl<'a> {
}; };
Ok(ModuleExportName::StringLiteral(literal)) Ok(ModuleExportName::StringLiteral(literal))
} }
_ => Ok(ModuleExportName::Identifier(self.parse_identifier_name()?)), _ => Ok(ModuleExportName::IdentifierName(self.parse_identifier_name()?)),
} }
} }

View file

@ -1209,7 +1209,8 @@ impl<'a> Format<'a> for ExportSpecifier<'a> {
impl<'a> Format<'a> for ModuleExportName<'a> { impl<'a> Format<'a> for ModuleExportName<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
match self { 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), Self::StringLiteral(literal) => literal.format(p),
} }
} }

View file

@ -10,10 +10,7 @@ use oxc_cfg::{
}; };
use oxc_diagnostics::OxcDiagnostic; use oxc_diagnostics::OxcDiagnostic;
use oxc_span::{CompactStr, SourceType, Span}; use oxc_span::{CompactStr, SourceType, Span};
use oxc_syntax::{ use oxc_syntax::{module_record::ModuleRecord, operator::AssignmentOperator};
module_record::{ExportImportName, ModuleRecord},
operator::AssignmentOperator,
};
use crate::{ use crate::{
binder::Binder, binder::Binder,
@ -388,23 +385,30 @@ impl<'a> SemanticBuilder<'a> {
self.symbols.add_redeclare_variable(symbol_id, span); self.symbols.add_redeclare_variable(symbol_id, span);
} }
fn add_export_flag_for_export_identifier(&mut self) { fn add_export_flag_to_export_identifiers(&mut self, program: &Program<'a>) {
self.module_record.indirect_export_entries.iter().for_each(|entry| { for stmt in &program.body {
if let ExportImportName::Name(name) = &entry.import_name { if let Statement::ExportDefaultDeclaration(decl) = stmt {
if let Some(symbol_id) = self.symbols.get_symbol_id_from_name(name.name()) { if let ExportDefaultDeclarationKind::Identifier(ident) = &decl.declaration {
self.symbols.union_flag(symbol_id, SymbolFlags::Export); self.add_export_flag_to_identifier(ident.name.as_str());
}
}
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());
}
}
}
}
} }
} }
});
self.module_record.local_export_entries.iter().for_each(|entry| { fn add_export_flag_to_identifier(&mut self, name: &str) {
if let Some(name) = entry.local_name.name() { if let Some(symbol_id) = self.scope.get_binding(self.current_scope_id, name) {
if let Some(symbol_id) = self.scope.get_root_binding(name.as_str()) {
self.symbols.union_flag(symbol_id, SymbolFlags::Export); self.symbols.union_flag(symbol_id, SymbolFlags::Export);
} }
} }
});
}
} }
impl<'a> Visit<'a> for SemanticBuilder<'a> { impl<'a> Visit<'a> for SemanticBuilder<'a> {
@ -1680,9 +1684,21 @@ impl<'a> SemanticBuilder<'a> {
/* cfg */ /* cfg */
match kind { match kind {
AstKind::ExportDefaultDeclaration(_) | AstKind::ExportNamedDeclaration(_) => { AstKind::ExportDefaultDeclaration(_) => {
self.current_symbol_flags |= SymbolFlags::Export; 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) => { AstKind::ImportSpecifier(specifier) => {
specifier.bind(self); specifier.bind(self);
} }
@ -1768,6 +1784,9 @@ impl<'a> SemanticBuilder<'a> {
AstKind::TSTypeParameter(type_parameter) => { AstKind::TSTypeParameter(type_parameter) => {
type_parameter.bind(self); type_parameter.bind(self);
} }
AstKind::ExportSpecifier(s) if s.export_kind.is_type() => {
self.current_reference_flag = ReferenceFlag::Type;
}
AstKind::TSTypeName(_) => { AstKind::TSTypeName(_) => {
self.current_reference_flag = ReferenceFlag::Type; self.current_reference_flag = ReferenceFlag::Type;
} }
@ -1815,16 +1834,28 @@ impl<'a> SemanticBuilder<'a> {
#[allow(clippy::single_match)] #[allow(clippy::single_match)]
fn leave_kind(&mut self, kind: AstKind<'a>) { fn leave_kind(&mut self, kind: AstKind<'a>) {
match kind { match kind {
AstKind::Program(_) => { AstKind::Program(program) => {
self.add_export_flag_for_export_identifier(); self.add_export_flag_to_export_identifiers(program);
} }
AstKind::Class(_) => { AstKind::Class(_) => {
self.current_node_flags -= NodeFlags::Class; self.current_node_flags -= NodeFlags::Class;
self.class_table_builder.pop_class(); self.class_table_builder.pop_class();
} }
AstKind::ExportDefaultDeclaration(_) | AstKind::ExportNamedDeclaration(_) => { AstKind::ExportDefaultDeclaration(_) => {
self.current_symbol_flags -= SymbolFlags::Export; 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::LabeledStatement(_) => self.label_builder.leave(),
AstKind::StaticBlock(_) => { AstKind::StaticBlock(_) => {
self.label_builder.leave_function_or_static_block(); self.label_builder.leave_function_or_static_block();

View file

@ -1,5 +1,4 @@
use oxc_semantic::SymbolFlags; use oxc_semantic::SymbolFlags;
use oxc_syntax::module_record::ExportExportName;
use crate::util::SemanticTester; use crate::util::SemanticTester;
@ -26,9 +25,7 @@ fn test_exports() {
); );
test.has_some_symbol("foo").is_exported().test(); test.has_some_symbol("foo").is_exported().test();
test.has_some_symbol("defaultExport").is_exported().test();
// FIXME: failing
// test.has_some_symbol("defaultExport").is_exported().test();
} }
#[test] #[test]
@ -118,21 +115,6 @@ fn test_exported_default_class() {
test.has_class("Foo"); test.has_class("Foo");
test.has_some_symbol("a").is_not_exported().test(); test.has_some_symbol("a").is_not_exported().test();
test.has_some_symbol("T").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 // FIXME

View file

@ -1,7 +1,7 @@
mod class_tester; mod class_tester;
mod expect; mod expect;
mod symbol_tester; mod symbol_tester;
use std::{path::PathBuf, sync::Arc}; use std::sync::Arc;
pub use class_tester::ClassTester; pub use class_tester::ClassTester;
pub use expect::Expect; pub use expect::Expect;
@ -88,7 +88,6 @@ impl<'a> SemanticTester<'a> {
let semantic_ret = SemanticBuilder::new(self.source_text, self.source_type) let semantic_ret = SemanticBuilder::new(self.source_text, self.source_type)
.with_check_syntax_error(true) .with_check_syntax_error(true)
.with_trivias(parse.trivias) .with_trivias(parse.trivias)
.build_module_record(PathBuf::new(), program)
.with_cfg(self.cfg) .with_cfg(self.cfg)
.build(program); .build(program);

View file

@ -159,21 +159,12 @@ impl<'a> SymbolTester<'a> {
self.test_result = match self.test_result { self.test_result = match self.test_result {
Ok(symbol_id) => { Ok(symbol_id) => {
let binding = self.target_symbol_name.clone(); let binding = self.target_symbol_name.clone();
let is_in_module_record = if self.semantic.symbols().get_flag(symbol_id).is_export() {
self.semantic.module_record().exported_bindings.contains_key(binding.as_str()) Ok(symbol_id)
&& self.semantic.scopes().get_root_binding(&binding) == Some(symbol_id); } else {
let has_export_flag = self.semantic.symbols().get_flag(symbol_id).is_export(); Err(OxcDiagnostic::error(format!(
match (is_in_module_record, has_export_flag) { "Expected {binding} to be exported with SymbolFlags::Export"
(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),
} }
} }
e => e, e => e,
@ -191,15 +182,6 @@ impl<'a> SymbolTester<'a> {
Err(OxcDiagnostic::error(format!( Err(OxcDiagnostic::error(format!(
"Expected {binding} to not be exported. Symbol has export flag." "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 { } else {
Ok(symbol_id) Ok(symbol_id)
} }

View file

@ -90,7 +90,10 @@ impl<'a> ModuleImports<'a> {
let local = name.local.unwrap_or_else(|| name.imported.clone()); let local = name.local.unwrap_or_else(|| name.imported.clone());
ImportDeclarationSpecifier::ImportSpecifier(self.ast.alloc(ImportSpecifier { ImportDeclarationSpecifier::ImportSpecifier(self.ast.alloc(ImportSpecifier {
span: SPAN, span: SPAN,
imported: ModuleExportName::Identifier(IdentifierName::new(SPAN, name.imported)), imported: ModuleExportName::IdentifierName(IdentifierName::new(
SPAN,
name.imported,
)),
local: BindingIdentifier { local: BindingIdentifier {
span: SPAN, span: SPAN,
name: local, name: local,

View file

@ -516,11 +516,6 @@ impl<'a> TypeScriptAnnotations<'a> {
pub fn has_value_reference(&self, name: &str, ctx: &TraverseCtx<'a>) -> bool { pub fn has_value_reference(&self, name: &str, ctx: &TraverseCtx<'a>) -> bool {
if let Some(symbol_id) = ctx.scopes().get_root_binding(name) { 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 if ctx
.symbols() .symbols()
.get_resolved_references(symbol_id) .get_resolved_references(symbol_id)

View file

@ -3109,7 +3109,12 @@ pub(crate) unsafe fn walk_module_export_name<'a, Tr: Traverse<'a>>(
) { ) {
traverser.enter_module_export_name(&mut *node, ctx); traverser.enter_module_export_name(&mut *node, ctx);
match &mut *node { 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) => { ModuleExportName::StringLiteral(node) => {
walk_string_literal(traverser, node as *mut _, ctx) walk_string_literal(traverser, node as *mut _, ctx)
} }

View file

@ -1,6 +1,6 @@
commit: 12619ffe commit: 12619ffe
Passed: 472/927 Passed: 473/927
# All Passed: # All Passed:
* babel-preset-react * babel-preset-react
@ -445,11 +445,10 @@ Passed: 472/927
* opts/optimizeConstEnums/input.ts * opts/optimizeConstEnums/input.ts
* opts/rewriteImportExtensions/input.ts * opts/rewriteImportExtensions/input.ts
# babel-plugin-transform-typescript (128/151) # babel-plugin-transform-typescript (129/151)
* enum/mix-references/input.ts * enum/mix-references/input.ts
* enum/ts5.0-const-foldable/input.ts * enum/ts5.0-const-foldable/input.ts
* exports/declared-types/input.ts * exports/declared-types/input.ts
* exports/imported-types/input.ts
* exports/interface/input.ts * exports/interface/input.ts
* imports/elide-no-import-specifiers/input.ts * imports/elide-no-import-specifiers/input.ts
* imports/elision-locations/input.ts * imports/elision-locations/input.ts