Skip to content

Commit 7816e4c

Browse files
committed
feat(ast)!: add IdentifierReference to ExportSpecifier
closes #3795 closes #3796
1 parent 99a40ce commit 7816e4c

File tree

21 files changed

+162
-117
lines changed

21 files changed

+162
-117
lines changed

crates/oxc_ast/src/ast/js.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -1895,7 +1895,7 @@ pub struct ExportDefaultDeclaration<'a> {
18951895
#[cfg_attr(feature = "serialize", serde(flatten))]
18961896
pub span: Span,
18971897
pub declaration: ExportDefaultDeclarationKind<'a>,
1898-
pub exported: ModuleExportName<'a>, // `default`
1898+
pub exported: ModuleExportName<'a>, // the `default` Keyword
18991899
}
19001900

19011901
#[visited_node]
@@ -1955,6 +1955,8 @@ pub enum ExportDefaultDeclarationKind<'a> {
19551955
#[cfg_attr(feature = "serialize", derive(Serialize, Tsify))]
19561956
#[cfg_attr(feature = "serialize", serde(untagged))]
19571957
pub enum ModuleExportName<'a> {
1958-
Identifier(IdentifierName<'a>),
1958+
IdentifierName(IdentifierName<'a>),
1959+
/// For `local` in `ExportSpecifier`: `foo` in `export { foo }`
1960+
IdentifierReference(IdentifierReference<'a>),
19591961
StringLiteral(StringLiteral<'a>),
19601962
}

crates/oxc_ast/src/ast_impl/js.rs

+12-2
Original file line numberDiff line numberDiff line change
@@ -1447,7 +1447,8 @@ impl<'a> ExportDefaultDeclarationKind<'a> {
14471447
impl<'a> fmt::Display for ModuleExportName<'a> {
14481448
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
14491449
let s = match self {
1450-
Self::Identifier(identifier) => identifier.name.to_string(),
1450+
Self::IdentifierName(identifier) => identifier.name.to_string(),
1451+
Self::IdentifierReference(identifier) => identifier.name.to_string(),
14511452
Self::StringLiteral(literal) => format!(r#""{}""#, literal.value),
14521453
};
14531454
write!(f, "{s}")
@@ -1457,8 +1458,17 @@ impl<'a> fmt::Display for ModuleExportName<'a> {
14571458
impl<'a> ModuleExportName<'a> {
14581459
pub fn name(&self) -> Atom<'a> {
14591460
match self {
1460-
Self::Identifier(identifier) => identifier.name.clone(),
1461+
Self::IdentifierName(identifier) => identifier.name.clone(),
1462+
Self::IdentifierReference(identifier) => identifier.name.clone(),
14611463
Self::StringLiteral(literal) => literal.value.clone(),
14621464
}
14631465
}
1466+
1467+
pub fn identifier_name(&self) -> Option<Atom<'a>> {
1468+
match self {
1469+
Self::IdentifierName(identifier) => Some(identifier.name.clone()),
1470+
Self::IdentifierReference(identifier) => Some(identifier.name.clone()),
1471+
Self::StringLiteral(_) => None,
1472+
}
1473+
}
14641474
}

crates/oxc_ast/src/ast_kind.rs

+3
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,7 @@ ast_kinds! {
126126
ModuleDeclaration(&'a ModuleDeclaration<'a>),
127127
ImportDeclaration(&'a ImportDeclaration<'a>),
128128
ImportSpecifier(&'a ImportSpecifier<'a>),
129+
ExportSpecifier(&'a ExportSpecifier<'a>),
129130
ImportDefaultSpecifier(&'a ImportDefaultSpecifier<'a>),
130131
ImportNamespaceSpecifier(&'a ImportNamespaceSpecifier<'a>),
131132
ExportDefaultDeclaration(&'a ExportDefaultDeclaration<'a>),
@@ -469,6 +470,7 @@ impl<'a> GetSpan for AstKind<'a> {
469470
Self::ModuleDeclaration(x) => x.span(),
470471
Self::ImportDeclaration(x) => x.span,
471472
Self::ImportSpecifier(x) => x.span,
473+
Self::ExportSpecifier(x) => x.span,
472474
Self::ImportDefaultSpecifier(x) => x.span,
473475
Self::ImportNamespaceSpecifier(x) => x.span,
474476
Self::ExportDefaultDeclaration(x) => x.span,
@@ -675,6 +677,7 @@ impl<'a> AstKind<'a> {
675677
Self::ModuleDeclaration(_) => "ModuleDeclaration".into(),
676678
Self::ImportDeclaration(_) => "ImportDeclaration".into(),
677679
Self::ImportSpecifier(_) => "ImportSpecifier".into(),
680+
Self::ExportSpecifier(_) => "ExportSpecifier".into(),
678681
Self::ImportDefaultSpecifier(_) => "ImportDefaultSpecifier".into(),
679682
Self::ImportNamespaceSpecifier(_) => "ImportNamespaceSpecifier".into(),
680683
Self::ExportDefaultDeclaration(_) => "ExportDefaultDeclaration".into(),

crates/oxc_ast/src/span.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,8 @@ impl<'a> GetSpan for ImportAttributeKey<'a> {
208208
impl<'a> GetSpan for ModuleExportName<'a> {
209209
fn span(&self) -> Span {
210210
match self {
211-
Self::Identifier(identifier) => identifier.span,
211+
Self::IdentifierName(identifier) => identifier.span,
212+
Self::IdentifierReference(identifier) => identifier.span,
212213
Self::StringLiteral(literal) => literal.span,
213214
}
214215
}

crates/oxc_ast/src/visit/visit.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -617,6 +617,14 @@ pub trait Visit<'a>: Sized {
617617
walk_export_named_declaration(self, decl);
618618
}
619619

620+
fn visit_export_specifier(&mut self, specifier: &ExportSpecifier<'a>) {
621+
walk_export_specifier(self, specifier);
622+
}
623+
624+
fn visit_module_export_name(&mut self, name: &ModuleExportName<'a>) {
625+
walk_module_export_name(self, name);
626+
}
627+
620628
fn visit_enum_member(&mut self, member: &TSEnumMember<'a>) {
621629
walk_enum_member(self, member);
622630
}
@@ -2433,7 +2441,7 @@ pub mod walk {
24332441
) {
24342442
let kind = AstKind::ImportSpecifier(visitor.alloc(specifier));
24352443
visitor.enter_node(kind);
2436-
// TODO: imported
2444+
visitor.visit_module_export_name(&specifier.imported);
24372445
visitor.visit_binding_identifier(&specifier.local);
24382446
visitor.leave_node(kind);
24392447
}
@@ -2496,12 +2504,36 @@ pub mod walk {
24962504
if let Some(decl) = &decl.declaration {
24972505
visitor.visit_declaration(decl);
24982506
}
2507+
for export_specifier in &decl.specifiers {
2508+
visitor.visit_export_specifier(export_specifier);
2509+
}
24992510
if let Some(ref source) = decl.source {
25002511
visitor.visit_string_literal(source);
25012512
}
25022513
visitor.leave_node(kind);
25032514
}
25042515

2516+
pub fn walk_export_specifier<'a, V: Visit<'a>>(
2517+
visitor: &mut V,
2518+
specifier: &ExportSpecifier<'a>,
2519+
) {
2520+
let kind = AstKind::ExportSpecifier(visitor.alloc(specifier));
2521+
visitor.enter_node(kind);
2522+
visitor.visit_module_export_name(&specifier.local);
2523+
visitor.visit_module_export_name(&specifier.exported);
2524+
visitor.leave_node(kind);
2525+
}
2526+
2527+
pub fn walk_module_export_name<'a, V: Visit<'a>>(visitor: &mut V, name: &ModuleExportName<'a>) {
2528+
match name {
2529+
ModuleExportName::IdentifierName(ident) => visitor.visit_identifier_name(ident),
2530+
ModuleExportName::IdentifierReference(ident) => {
2531+
visitor.visit_identifier_reference(ident);
2532+
}
2533+
ModuleExportName::StringLiteral(ident) => visitor.visit_string_literal(ident),
2534+
}
2535+
}
2536+
25052537
pub fn walk_enum_member<'a, V: Visit<'a>>(visitor: &mut V, member: &TSEnumMember<'a>) {
25062538
let kind = AstKind::TSEnumMember(visitor.alloc(member));
25072539
visitor.enter_node(kind);

crates/oxc_codegen/src/gen.rs

+7-4
Original file line numberDiff line numberDiff line change
@@ -784,7 +784,11 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ImportDeclaration<'a> {
784784
}
785785

786786
let imported_name = match &spec.imported {
787-
ModuleExportName::Identifier(identifier) => {
787+
ModuleExportName::IdentifierName(identifier) => {
788+
identifier.gen(p, ctx);
789+
identifier.name.as_bytes()
790+
}
791+
ModuleExportName::IdentifierReference(identifier) => {
788792
identifier.gen(p, ctx);
789793
identifier.name.as_bytes()
790794
}
@@ -961,9 +965,8 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ExportSpecifier<'a> {
961965
impl<'a, const MINIFY: bool> Gen<MINIFY> for ModuleExportName<'a> {
962966
fn gen(&self, p: &mut Codegen<{ MINIFY }>, ctx: Context) {
963967
match self {
964-
Self::Identifier(identifier) => {
965-
p.print_str(identifier.name.as_bytes());
966-
}
968+
Self::IdentifierName(identifier) => p.print_str(identifier.name.as_bytes()),
969+
Self::IdentifierReference(identifier) => p.print_str(identifier.name.as_bytes()),
967970
Self::StringLiteral(literal) => literal.gen(p, ctx),
968971
};
969972
}

crates/oxc_isolated_declarations/src/module.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ impl<'a> IsolatedDeclarations<'a> {
8888
};
8989

9090
declaration.map(|(var_decl, declaration)| {
91-
let exported = ModuleExportName::Identifier(IdentifierName::new(
91+
let exported = ModuleExportName::IdentifierName(IdentifierName::new(
9292
SPAN,
9393
self.ast.new_atom("default"),
9494
));

crates/oxc_isolated_declarations/src/scope.rs

+3-3
Original file line numberDiff line numberDiff line change
@@ -123,9 +123,9 @@ impl<'a> Visit<'a> for ScopeTree<'a> {
123123

124124
fn visit_export_named_declaration(&mut self, decl: &ExportNamedDeclaration<'a>) {
125125
for specifier in &decl.specifiers {
126-
if let ModuleExportName::Identifier(ident) = &specifier.local {
127-
self.add_type_reference(ident.name.clone());
128-
self.add_value_reference(ident.name.clone());
126+
if let Some(name) = specifier.local.identifier_name() {
127+
self.add_type_reference(name.clone());
128+
self.add_value_reference(name);
129129
}
130130
}
131131
}

crates/oxc_linter/src/rules/tree_shaking/no_side_effects_in_initialization/listener_map.rs

+3-5
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use oxc_ast::{
88
FormalParameter, Function, IdentifierReference, JSXAttribute, JSXAttributeItem,
99
JSXAttributeValue, JSXChild, JSXElement, JSXElementName, JSXExpression,
1010
JSXExpressionContainer, JSXFragment, JSXIdentifier, JSXOpeningElement, LogicalExpression,
11-
MemberExpression, ModuleExportName, NewExpression, ObjectExpression, ObjectPropertyKind,
11+
MemberExpression, NewExpression, ObjectExpression, ObjectPropertyKind,
1212
ParenthesizedExpression, PrivateFieldExpression, Program, PropertyKey, SequenceExpression,
1313
SimpleAssignmentTarget, Statement, StaticMemberExpression, SwitchCase, ThisExpression,
1414
UnaryExpression, VariableDeclarator,
@@ -198,10 +198,8 @@ impl<'a> ListenerMap for ExportSpecifier<'a> {
198198
let ctx = options.ctx;
199199
let symbol_table = ctx.symbols();
200200
if has_comment_about_side_effect_check(self.exported.span(), ctx) {
201-
let ModuleExportName::Identifier(ident_name) = &self.exported else {
202-
return;
203-
};
204-
let Some(symbol_id) = options.ctx.symbols().get_symbol_id_from_name(&ident_name.name)
201+
let Some(name) = self.exported.identifier_name() else { return };
202+
let Some(symbol_id) = options.ctx.symbols().get_symbol_id_from_name(name.as_str())
205203
else {
206204
return;
207205
};

crates/oxc_linter/src/rules/unicorn/no_thenable.rs

+4-13
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,13 @@ use oxc_ast::{
22
ast::{
33
match_expression, Argument, ArrayExpressionElement, AssignmentExpression, AssignmentTarget,
44
BindingPatternKind, CallExpression, Declaration, Expression, ModuleDeclaration,
5-
ModuleExportName, ObjectPropertyKind, PropertyKey, VariableDeclarator,
5+
ObjectPropertyKind, PropertyKey, VariableDeclarator,
66
},
77
AstKind,
88
};
99
use oxc_diagnostics::OxcDiagnostic;
1010
use oxc_macros::declare_oxc_lint;
11-
use oxc_span::Span;
11+
use oxc_span::{GetSpan, Span};
1212

1313
use crate::{context::LintContext, rule::Rule, AstNode};
1414

@@ -107,17 +107,8 @@ impl Rule for NoThenable {
107107
}
108108
// check specifier
109109
for spec in &decl.specifiers {
110-
match spec.exported {
111-
ModuleExportName::Identifier(ref ident) => {
112-
if ident.name == "then" {
113-
ctx.diagnostic(export(ident.span));
114-
}
115-
}
116-
ModuleExportName::StringLiteral(ref lit) => {
117-
if lit.value == "then" {
118-
ctx.diagnostic(export(lit.span));
119-
}
120-
}
110+
if spec.exported.name() == "then" {
111+
ctx.diagnostic(export(spec.exported.span()));
121112
}
122113
}
123114
}

crates/oxc_module_lexer/src/lib.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -220,7 +220,8 @@ impl<'a> Visit<'a> for ModuleLexer<'a> {
220220
// export { named }
221221
self.exports.extend(decl.specifiers.iter().map(|s| {
222222
let (exported_start, exported_end) = match &s.exported {
223-
ModuleExportName::Identifier(ident) => (ident.span.start, ident.span.end),
223+
ModuleExportName::IdentifierName(ident) => (ident.span.start, ident.span.end),
224+
ModuleExportName::IdentifierReference(ident) => (ident.span.start, ident.span.end),
224225
// +1 -1 to remove the string quotes
225226
ModuleExportName::StringLiteral(s) => (s.span.start + 1, s.span.end - 1),
226227
};

crates/oxc_parser/src/js/module.rs

+15-8
Original file line numberDiff line numberDiff line change
@@ -218,7 +218,7 @@ impl<'a> ParserImpl<'a> {
218218
span: Span,
219219
) -> Result<Box<'a, ExportNamedDeclaration<'a>>> {
220220
let export_kind = self.parse_import_or_export_kind();
221-
let specifiers =
221+
let mut specifiers =
222222
self.context(Context::empty(), self.ctx, ExportNamedSpecifiers::parse)?.elements;
223223
let (source, with_clause) = if self.eat(Kind::From) && self.cur_kind().is_literal() {
224224
let source = self.parse_literal_string()?;
@@ -229,7 +229,7 @@ impl<'a> ParserImpl<'a> {
229229

230230
// ExportDeclaration : export NamedExports ;
231231
if source.is_none() {
232-
for specifier in &specifiers {
232+
for specifier in specifiers.iter_mut() {
233233
match &specifier.local {
234234
// It is a Syntax Error if ReferencedBindings of NamedExports contains any StringLiterals.
235235
ModuleExportName::StringLiteral(literal) => {
@@ -242,18 +242,25 @@ impl<'a> ParserImpl<'a> {
242242
// For each IdentifierName n in ReferencedBindings of NamedExports:
243243
// It is a Syntax Error if StringValue of n is a ReservedWord or the StringValue of n
244244
// is one of "implements", "interface", "let", "package", "private", "protected", "public", or "static".
245-
ModuleExportName::Identifier(id) => {
246-
let match_result = Kind::match_keyword(&id.name);
245+
ModuleExportName::IdentifierName(ident) => {
246+
let match_result = Kind::match_keyword(&ident.name);
247247
if match_result.is_reserved_keyword()
248248
|| match_result.is_future_reserved_keyword()
249249
{
250250
self.error(diagnostics::export_reserved_word(
251251
&specifier.local.to_string(),
252252
&specifier.exported.to_string(),
253-
id.span,
253+
ident.span,
254254
));
255255
}
256+
257+
// `local` becomes a reference for `export { local }`.
258+
specifier.local = ModuleExportName::IdentifierReference(
259+
self.ast.identifier_reference(ident.span, ident.name.as_str()),
260+
);
256261
}
262+
// No prior code path should lead to parsing `ModuleExportName` as `IdentifierReference`.
263+
ModuleExportName::IdentifierReference(_) => unreachable!(),
257264
}
258265
}
259266
}
@@ -343,7 +350,7 @@ impl<'a> ParserImpl<'a> {
343350
decl
344351
}
345352
};
346-
let exported = ModuleExportName::Identifier(exported);
353+
let exported = ModuleExportName::IdentifierName(exported);
347354
let span = self.end_span(span);
348355
Ok(self.ast.export_default_declaration(span, declaration, exported))
349356
}
@@ -400,7 +407,7 @@ impl<'a> ParserImpl<'a> {
400407
} else {
401408
let local = self.parse_binding_identifier()?;
402409
let imported = IdentifierName { span: local.span, name: local.name.clone() };
403-
(ModuleExportName::Identifier(imported), local)
410+
(ModuleExportName::IdentifierName(imported), local)
404411
};
405412
Ok(self.ast.alloc(ImportSpecifier {
406413
span: self.end_span(specifier_span),
@@ -424,7 +431,7 @@ impl<'a> ParserImpl<'a> {
424431
};
425432
Ok(ModuleExportName::StringLiteral(literal))
426433
}
427-
_ => Ok(ModuleExportName::Identifier(self.parse_identifier_name()?)),
434+
_ => Ok(ModuleExportName::IdentifierName(self.parse_identifier_name()?)),
428435
}
429436
}
430437

crates/oxc_prettier/src/format/mod.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -1209,7 +1209,8 @@ impl<'a> Format<'a> for ExportSpecifier<'a> {
12091209
impl<'a> Format<'a> for ModuleExportName<'a> {
12101210
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
12111211
match self {
1212-
Self::Identifier(ident) => ident.format(p),
1212+
Self::IdentifierName(ident) => ident.format(p),
1213+
Self::IdentifierReference(ident) => ident.format(p),
12131214
Self::StringLiteral(literal) => literal.format(p),
12141215
}
12151216
}

0 commit comments

Comments
 (0)