Skip to content

Commit c1942fa

Browse files
committed
fix(parser): fix enum member parsing
1 parent e6a8af6 commit c1942fa

File tree

19 files changed

+296
-22
lines changed

19 files changed

+296
-22
lines changed

crates/oxc_ast/src/ast/ts.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,9 @@ inherit_variants! {
115115
pub enum TSEnumMemberName<'a> {
116116
StaticIdentifier(Box<'a, IdentifierName<'a>>) = 64,
117117
StaticStringLiteral(Box<'a, StringLiteral<'a>>) = 65,
118+
StaticTemplateLiteral(Box<'a, TemplateLiteral<'a>>) = 66,
118119
// Invalid Grammar `enum E { 1 }`
119-
StaticNumericLiteral(Box<'a, NumericLiteral<'a>>) = 66,
120+
StaticNumericLiteral(Box<'a, NumericLiteral<'a>>) = 67,
120121
// Invalid Grammar `enum E { [computed] }`
121122
// `Expression` variants added here by `inherit_variants!` macro
122123
@inherit Expression

crates/oxc_ast/src/generated/ast_builder.rs

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8116,6 +8116,37 @@ impl<'a> AstBuilder<'a> {
81168116
TSEnumMemberName::StaticStringLiteral(inner.into_in(self.allocator))
81178117
}
81188118

8119+
/// Build a [`TSEnumMemberName::StaticTemplateLiteral`]
8120+
///
8121+
/// This node contains a [`TemplateLiteral`] that will be stored in the memory arena.
8122+
///
8123+
/// ## Parameters
8124+
/// - span: The [`Span`] covering this node
8125+
/// - quasis
8126+
/// - expressions
8127+
#[inline]
8128+
pub fn ts_enum_member_name_template_literal(
8129+
self,
8130+
span: Span,
8131+
quasis: Vec<'a, TemplateElement<'a>>,
8132+
expressions: Vec<'a, Expression<'a>>,
8133+
) -> TSEnumMemberName<'a> {
8134+
TSEnumMemberName::StaticTemplateLiteral(self.alloc(self.template_literal(
8135+
span,
8136+
quasis,
8137+
expressions,
8138+
)))
8139+
}
8140+
8141+
/// Convert a [`TemplateLiteral`] into a [`TSEnumMemberName::StaticTemplateLiteral`]
8142+
#[inline]
8143+
pub fn ts_enum_member_name_from_template_literal<T>(self, inner: T) -> TSEnumMemberName<'a>
8144+
where
8145+
T: IntoIn<'a, Box<'a, TemplateLiteral<'a>>>,
8146+
{
8147+
TSEnumMemberName::StaticTemplateLiteral(inner.into_in(self.allocator))
8148+
}
8149+
81198150
/// Build a [`TSEnumMemberName::StaticNumericLiteral`]
81208151
///
81218152
/// This node contains a [`NumericLiteral`] that will be stored in the memory arena.

crates/oxc_ast/src/generated/span.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1278,6 +1278,7 @@ impl<'a> GetSpan for TSEnumMemberName<'a> {
12781278
match self {
12791279
Self::StaticIdentifier(it) => it.span(),
12801280
Self::StaticStringLiteral(it) => it.span(),
1281+
Self::StaticTemplateLiteral(it) => it.span(),
12811282
Self::StaticNumericLiteral(it) => it.span(),
12821283
Self::BooleanLiteral(it) => it.span(),
12831284
Self::NullLiteral(it) => it.span(),

crates/oxc_ast/src/generated/visit.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3878,6 +3878,7 @@ pub mod walk {
38783878
match it {
38793879
TSEnumMemberName::StaticIdentifier(it) => visitor.visit_identifier_name(it),
38803880
TSEnumMemberName::StaticStringLiteral(it) => visitor.visit_string_literal(it),
3881+
TSEnumMemberName::StaticTemplateLiteral(it) => visitor.visit_template_literal(it),
38813882
TSEnumMemberName::StaticNumericLiteral(it) => visitor.visit_numeric_literal(it),
38823883
match_expression!(TSEnumMemberName) => visitor.visit_expression(it.to_expression()),
38833884
}

crates/oxc_ast/src/generated/visit_mut.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4102,6 +4102,7 @@ pub mod walk_mut {
41024102
match it {
41034103
TSEnumMemberName::StaticIdentifier(it) => visitor.visit_identifier_name(it),
41044104
TSEnumMemberName::StaticStringLiteral(it) => visitor.visit_string_literal(it),
4105+
TSEnumMemberName::StaticTemplateLiteral(it) => visitor.visit_template_literal(it),
41054106
TSEnumMemberName::StaticNumericLiteral(it) => visitor.visit_numeric_literal(it),
41064107
match_expression!(TSEnumMemberName) => visitor.visit_expression(it.to_expression_mut()),
41074108
}

crates/oxc_codegen/src/gen.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3531,6 +3531,7 @@ impl<'a, const MINIFY: bool> Gen<MINIFY> for TSEnumMember<'a> {
35313531
match &self.id {
35323532
TSEnumMemberName::StaticIdentifier(decl) => decl.gen(p, ctx),
35333533
TSEnumMemberName::StaticStringLiteral(decl) => decl.gen(p, ctx),
3534+
TSEnumMemberName::StaticTemplateLiteral(decl) => decl.gen(p, ctx),
35343535
TSEnumMemberName::StaticNumericLiteral(decl) => decl.gen(p, ctx),
35353536
decl @ match_expression!(TSEnumMemberName) => {
35363537
p.print_str("[");

crates/oxc_isolated_declarations/src/enum.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@ impl<'a> IsolatedDeclarations<'a> {
4545
let member_name = match &member.id {
4646
TSEnumMemberName::StaticIdentifier(id) => &id.name,
4747
TSEnumMemberName::StaticStringLiteral(str) => &str.value,
48+
TSEnumMemberName::StaticTemplateLiteral(template) => {
49+
&template.quasi().expect("Template enum members cannot have substitutions.")
50+
}
4851
#[allow(clippy::unnested_or_patterns)] // Clippy is wrong
4952
TSEnumMemberName::StaticNumericLiteral(_)
5053
| match_expression!(TSEnumMemberName) => {

crates/oxc_parser/src/diagnostics.rs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,3 +449,16 @@ pub fn accessibility_modifier_on_private_property(modifier: &Modifier) -> OxcDia
449449
ts_error("18010", "An accessibility modifier cannot be used with a private identifier.")
450450
.with_label(modifier.span)
451451
}
452+
453+
// ================================== TS ENUMS =================================
454+
455+
/// Computed property names are not allowed in enums.ts(1164)
456+
#[cold]
457+
pub fn computed_property_names_not_allowed_in_enums(span: Span) -> OxcDiagnostic {
458+
ts_error("1164", "Computed property names are not allowed in enums.").with_label(span)
459+
}
460+
/// An enum member cannot have a numeric name.ts(2452)
461+
#[cold]
462+
pub fn enum_member_cannot_have_numeric_name(span: Span) -> OxcDiagnostic {
463+
ts_error("2452", "An enum member cannot have a numeric name.").with_label(span)
464+
}

crates/oxc_parser/src/js/expression.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -407,7 +407,7 @@ impl<'a> ParserImpl<'a> {
407407
/// `TemplateLiteral`[Yield, Await, Tagged] :
408408
/// `NoSubstitutionTemplate`
409409
/// `SubstitutionTemplate`[?Yield, ?Await, ?Tagged]
410-
fn parse_template_literal(&mut self, tagged: bool) -> Result<TemplateLiteral<'a>> {
410+
pub(crate) fn parse_template_literal(&mut self, tagged: bool) -> Result<TemplateLiteral<'a>> {
411411
let span = self.start_span();
412412
let mut expressions = self.ast.vec();
413413
let mut quasis = self.ast.vec();

crates/oxc_parser/src/ts/statement.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
use oxc_allocator::Box;
22
use oxc_ast::ast::*;
33
use oxc_diagnostics::Result;
4-
use oxc_span::Span;
4+
use oxc_span::{GetSpan, Span};
55

66
use crate::{
77
diagnostics,
@@ -59,21 +59,37 @@ impl<'a> ParserImpl<'a> {
5959
None
6060
};
6161

62-
Ok(TSEnumMember { span: self.end_span(span), id, initializer })
62+
let span = self.end_span(span);
63+
if initializer.is_some() && matches!(id, TSEnumMemberName::StaticTemplateLiteral(_)) {
64+
self.error(diagnostics::invalid_assignment(span))
65+
}
66+
67+
Ok(TSEnumMember { span, id, initializer })
6368
}
6469

6570
fn parse_ts_enum_member_name(&mut self) -> Result<TSEnumMemberName<'a>> {
6671
match self.cur_kind() {
6772
Kind::LBrack => {
6873
let node = self.parse_computed_property_name()?;
74+
self.check_invalid_ts_enum_computed_property(&node);
6975
Ok(self.ast.ts_enum_member_name_expression(node))
7076
}
7177
Kind::Str => {
7278
let node = self.parse_literal_string()?;
7379
Ok(self.ast.ts_enum_member_name_from_string_literal(node))
7480
}
81+
Kind::NoSubstitutionTemplate | Kind::TemplateHead => {
82+
let node = self.parse_template_literal(false)?;
83+
if !node.expressions.is_empty() {
84+
self.error(diagnostics::computed_property_names_not_allowed_in_enums(
85+
node.span(),
86+
))
87+
}
88+
Ok(self.ast.ts_enum_member_name_from_template_literal(node))
89+
}
7590
kind if kind.is_number() => {
7691
let node = self.parse_literal_number()?;
92+
self.error(diagnostics::enum_member_cannot_have_numeric_name(node.span()));
7793
Ok(self.ast.ts_enum_member_name_from_numeric_literal(node))
7894
}
7995
_ => {
@@ -82,6 +98,21 @@ impl<'a> ParserImpl<'a> {
8298
}
8399
}
84100
}
101+
fn check_invalid_ts_enum_computed_property(&mut self, property: &Expression<'a>) {
102+
match property {
103+
Expression::StringLiteral(_) => {
104+
return;
105+
}
106+
Expression::TemplateLiteral(template) if template.expressions.is_empty() => {
107+
return;
108+
}
109+
Expression::NumericLiteral(_) => {
110+
self.error(diagnostics::enum_member_cannot_have_numeric_name(property.span()))
111+
}
112+
_ => self
113+
.error(diagnostics::computed_property_names_not_allowed_in_enums(property.span())),
114+
}
115+
}
85116

86117
/** ------------------- Annotation ----------------- */
87118

0 commit comments

Comments
 (0)