Skip to content

Commit

Permalink
feat(ast)!: add IdentifierReference to ExportSpecifier (#3820)
Browse files Browse the repository at this point in the history
closes #3795
closes #3796
  • Loading branch information
Boshen committed Jun 22, 2024
1 parent 99a40ce commit 4456034
Show file tree
Hide file tree
Showing 21 changed files with 162 additions and 117 deletions.
6 changes: 4 additions & 2 deletions crates/oxc_ast/src/ast/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down Expand Up @@ -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>),
}
14 changes: 12 additions & 2 deletions crates/oxc_ast/src/ast_impl/js.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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}")
Expand All @@ -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<Atom<'a>> {
match self {
Self::IdentifierName(identifier) => Some(identifier.name.clone()),
Self::IdentifierReference(identifier) => Some(identifier.name.clone()),
Self::StringLiteral(_) => None,
}
}
}
3 changes: 3 additions & 0 deletions crates/oxc_ast/src/ast_kind.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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>),
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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(),
Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_ast/src/span.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
}
Expand Down
34 changes: 33 additions & 1 deletion crates/oxc_ast/src/visit/visit.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
}
Expand Down Expand Up @@ -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);
Expand Down
11 changes: 7 additions & 4 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -784,7 +784,11 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> 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()
}
Expand Down Expand Up @@ -961,9 +965,8 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for ExportSpecifier<'a> {
impl<'a, const MINIFY: bool> Gen<MINIFY> 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),
};
}
Expand Down
2 changes: 1 addition & 1 deletion crates/oxc_isolated_declarations/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
));
Expand Down
6 changes: 3 additions & 3 deletions crates/oxc_isolated_declarations/src/scope.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down Expand Up @@ -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;
};
Expand Down
17 changes: 4 additions & 13 deletions crates/oxc_linter/src/rules/unicorn/no_thenable.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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};

Expand Down Expand Up @@ -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()));
}
}
}
Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_module_lexer/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
};
Expand Down
23 changes: 15 additions & 8 deletions crates/oxc_parser/src/js/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -218,7 +218,7 @@ impl<'a> ParserImpl<'a> {
span: Span,
) -> Result<Box<'a, ExportNamedDeclaration<'a>>> {
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()?;
Expand All @@ -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) => {
Expand All @@ -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!(),
}
}
}
Expand Down Expand Up @@ -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))
}
Expand Down Expand Up @@ -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),
Expand All @@ -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()?)),
}
}

Expand Down
3 changes: 2 additions & 1 deletion crates/oxc_prettier/src/format/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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),
}
}
Expand Down
Loading

0 comments on commit 4456034

Please sign in to comment.