diff --git a/CHANGELOG.md b/CHANGELOG.md index a26f171d050c..846925ffb396 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,21 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b ### Linter +#### Enhancements + +- [noInvalidUseBeforeDeclaration](https://biomejs.dev/linter/rules/no-invalid-use-before-declaration) now reports direct use of an enum member before its declaration. + + In the following code, `A` is reported as use before its declaration. + + ```ts + enum E { + B = A << 1, + A = 1, + } + ``` + + Contributed by @Conaclos + #### Bug fixes - Don't request alt text for elements hidden from assistive technologies ([#3316](https://github.com/biomejs/biome/issues/3316)). Contributed by @robintown @@ -51,6 +66,19 @@ our [guidelines for writing a good changelog entry](https://github.com/biomejs/b - Add [nursery/noDynamicNamespaceImportAccess](https://biomejs.dev/linter/no-dynamic-namespace-import-access/). Contributed by @minht11 +- [noUndeclaredVariables](https://biomejs.dev/linter/rules/no-undeclared-variables/) n longer report a direct reference to an enum member ([#2974](https://github.com/biomejs/biome/issues/2974)). + + In the following code, the `A` reference is no longer reported as an undeclared variable. + + ```ts + enum E { + A = 1, + B = A << 1, + } + ``` + + Contributed by @Conaclos + ### Parser ## v1.8.3 (2024-06-27) diff --git a/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs b/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs index 0fae72a1bbfa..22932c895ccc 100644 --- a/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs +++ b/crates/biome_js_analyze/src/lint/complexity/use_literal_keys.rs @@ -4,15 +4,12 @@ use biome_analyze::{ RuleSource, }; use biome_console::markup; -use biome_js_factory::make::{ - self, ident, js_literal_member_name, js_name, js_static_member_assignment, - js_static_member_expression, token, -}; +use biome_js_factory::make; use biome_js_syntax::{ AnyJsAssignment, AnyJsComputedMember, AnyJsMemberExpression, AnyJsName, AnyJsObjectMemberName, - JsComputedMemberName, T, + AnyTsEnumMemberName, JsComputedMemberName, JsSyntaxKind, T, }; -use biome_rowan::{declare_node_union, AstNode, BatchMutationExt, TextRange}; +use biome_rowan::{declare_node_union, AstNode, BatchMutationExt, SyntaxNodeOptionExt, TextRange}; use biome_unicode_table::is_js_ident; declare_lint_rule! { @@ -129,12 +126,14 @@ impl Rule for UseLiteralKeys { match node { AnyJsMember::AnyJsComputedMember(node) => { let object = node.object().ok()?; - let member = js_name(ident(identifier)); - let dot_token = node.optional_chain_token().unwrap_or_else(|| token(T![.])); + let member = make::js_name(make::ident(identifier)); + let dot_token = node + .optional_chain_token() + .unwrap_or_else(|| make::token(T![.])); match node { AnyJsComputedMember::JsComputedMemberExpression(node) => { - let static_expression = js_static_member_expression( + let static_expression = make::js_static_member_expression( object, dot_token, AnyJsName::JsName(member), @@ -145,7 +144,7 @@ impl Rule for UseLiteralKeys { ); } AnyJsComputedMember::JsComputedMemberAssignment(node) => { - let static_member = js_static_member_assignment( + let static_member = make::js_static_member_assignment( object, dot_token, AnyJsName::JsName(member), @@ -163,11 +162,19 @@ impl Rule for UseLiteralKeys { } else { make::js_string_literal_single_quotes(identifier) }; - let literal_member_name = js_literal_member_name(name_token); - mutation.replace_node( - AnyJsObjectMemberName::from(member.clone()), - literal_member_name.into(), - ); + if member.syntax().parent().kind() == Some(JsSyntaxKind::TS_ENUM_MEMBER) { + let literal_enum_member_name = make::ts_literal_enum_member_name(name_token); + mutation.replace_node( + AnyTsEnumMemberName::from(member.clone()), + literal_enum_member_name.into(), + ); + } else { + let literal_member_name = make::js_literal_member_name(name_token); + mutation.replace_node( + AnyJsObjectMemberName::from(member.clone()), + literal_member_name.into(), + ); + } } } Some(JsRuleAction::new( diff --git a/crates/biome_js_analyze/src/lint/correctness/no_invalid_use_before_declaration.rs b/crates/biome_js_analyze/src/lint/correctness/no_invalid_use_before_declaration.rs index a5e52ce5766f..6cbde2d7ed28 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_invalid_use_before_declaration.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_invalid_use_before_declaration.rs @@ -79,7 +79,12 @@ impl Rule for NoInvalidUseBeforeDeclaration { let model = ctx.model(); let mut result = vec![]; for binding in model.all_bindings() { - let AnyJsIdentifierBinding::JsIdentifierBinding(id) = binding.tree() else { + let id = binding.tree(); + if matches!( + id, + AnyJsIdentifierBinding::TsIdentifierBinding(_) + | AnyJsIdentifierBinding::TsTypeParameterName(_) + ) { // Ignore type declarations (interfaces, type-aliases, ...) continue; }; @@ -162,6 +167,7 @@ impl Rule for NoInvalidUseBeforeDeclaration { binding_range: declaration_range, } = state; let declaration_kind_text = match declaration_kind { + DeclarationKind::EnumMember => "enum member", DeclarationKind::Parameter => "parameter", DeclarationKind::Variable => "variable", }; @@ -188,6 +194,7 @@ pub struct InvalidUseBeforeDeclaration { #[derive(Debug, Copy, Clone)] pub enum DeclarationKind { + EnumMember, Parameter, Variable, } @@ -197,6 +204,7 @@ impl TryFrom<&AnyJsBindingDeclaration> for DeclarationKind { fn try_from(value: &AnyJsBindingDeclaration) -> Result { match value { + AnyJsBindingDeclaration::TsEnumMember(_) => Ok(DeclarationKind::EnumMember), // Variable declaration AnyJsBindingDeclaration::JsArrayBindingPatternElement(_) | AnyJsBindingDeclaration::JsArrayBindingPatternRestElement(_) diff --git a/crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs b/crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs index c20a65bc9d1b..0923633ec2c8 100644 --- a/crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs +++ b/crates/biome_js_analyze/src/lint/correctness/no_unused_variables.rs @@ -175,7 +175,8 @@ fn suggested_fix_if_unused(binding: &AnyJsIdentifierBinding) -> Option None, + | AnyJsBindingDeclaration::TsMappedType(_) + | AnyJsBindingDeclaration::TsEnumMember(_) => None, // Some parameters are ok to not be used AnyJsBindingDeclaration::JsArrowFunctionExpression(_) => { @@ -288,6 +289,10 @@ impl Rule for NoUnusedVariables { } let binding = ctx.query(); + if matches!(binding, AnyJsIdentifierBinding::TsLiteralEnumMemberName(_)) { + // Enum members can be unused. + return None; + } if binding.name_token().ok()?.text_trimmed().starts_with('_') { return None; @@ -423,6 +428,9 @@ impl Rule for NoUnusedVariables { AnyJsIdentifierBinding::TsTypeParameterName(binding) => { binding.ident_token().ok()? } + AnyJsIdentifierBinding::TsLiteralEnumMemberName(_) => { + return None; + } }; let name_trimmed = name.text_trimmed(); let new_name = format!("_{name_trimmed}"); diff --git a/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs b/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs index 6f4067a17728..7158a021c324 100644 --- a/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs +++ b/crates/biome_js_analyze/src/lint/correctness/use_exhaustive_dependencies.rs @@ -475,7 +475,8 @@ fn capture_needs_to_be_in_the_dependency_list( | AnyJsBindingDeclaration::TsModuleDeclaration(_) | AnyJsBindingDeclaration::TsInferType(_) | AnyJsBindingDeclaration::TsMappedType(_) - | AnyJsBindingDeclaration::TsTypeParameter(_) => false, + | AnyJsBindingDeclaration::TsTypeParameter(_) + | AnyJsBindingDeclaration::TsEnumMember(_) => false, // Function declarations are stable if ... AnyJsBindingDeclaration::JsFunctionDeclaration(declaration) => { let declaration_range = declaration.syntax().text_range(); diff --git a/crates/biome_js_analyze/src/lint/style/use_naming_convention.rs b/crates/biome_js_analyze/src/lint/style/use_naming_convention.rs index 987fb8bc02ae..574fbe50132f 100644 --- a/crates/biome_js_analyze/src/lint/style/use_naming_convention.rs +++ b/crates/biome_js_analyze/src/lint/style/use_naming_convention.rs @@ -20,9 +20,9 @@ use biome_js_syntax::{ binding_ext::AnyJsBindingDeclaration, AnyJsClassMember, AnyJsObjectMember, AnyJsVariableDeclaration, AnyTsTypeMember, JsIdentifierBinding, JsLiteralExportName, JsLiteralMemberName, JsMethodModifierList, JsPrivateClassMemberName, JsPropertyModifierList, - JsSyntaxKind, JsSyntaxToken, JsVariableDeclarator, JsVariableKind, Modifier, TsEnumMember, - TsIdentifierBinding, TsMethodSignatureModifierList, TsPropertySignatureModifierList, - TsTypeParameterName, + JsSyntaxKind, JsSyntaxToken, JsVariableDeclarator, JsVariableKind, Modifier, + TsIdentifierBinding, TsLiteralEnumMemberName, TsMethodSignatureModifierList, + TsPropertySignatureModifierList, TsTypeParameterName, }; use biome_rowan::{ declare_node_union, AstNode, BatchMutationExt, SyntaxResult, TextRange, TextSize, @@ -843,6 +843,7 @@ declare_node_union! { JsPrivateClassMemberName | JsLiteralExportName | TsIdentifierBinding | + TsLiteralEnumMemberName | TsTypeParameterName } @@ -856,6 +857,7 @@ impl AnyIdentifierBindingLike { } AnyIdentifierBindingLike::JsLiteralExportName(export_name) => export_name.value(), AnyIdentifierBindingLike::TsIdentifierBinding(binding) => binding.name_token(), + AnyIdentifierBindingLike::TsLiteralEnumMemberName(member_name) => member_name.value(), AnyIdentifierBindingLike::TsTypeParameterName(type_parameter) => { type_parameter.ident_token() } @@ -1177,8 +1179,6 @@ impl Selector { Selector::from_type_member(&member) } else if let Some(member) = member_name.parent::() { Selector::from_object_member(&member) - } else if member_name.parent::().is_some() { - Some(Kind::EnumMember.into()) } else { None } @@ -1202,6 +1202,7 @@ impl Selector { _ => None, } } + AnyIdentifierBindingLike::TsLiteralEnumMemberName(_) => Some(Kind::EnumMember.into()), AnyIdentifierBindingLike::TsTypeParameterName(_) => Some(Kind::TypeParameter.into()), } } @@ -1298,7 +1299,8 @@ impl Selector { // Type parameters should be handled at call site | AnyJsBindingDeclaration::TsInferType(_) | AnyJsBindingDeclaration::TsMappedType(_) - | AnyJsBindingDeclaration::TsTypeParameter(_) => None, + | AnyJsBindingDeclaration::TsTypeParameter(_) + | AnyJsBindingDeclaration::TsEnumMember(_) => None, } } diff --git a/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.ts b/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.ts index 2f26ec23b572..e0222ab70bcc 100644 --- a/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.ts +++ b/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.ts @@ -21,3 +21,8 @@ export type T = { [""]: number } + +export enum E { + ["A"], + ["B"], +} diff --git a/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.ts.snap b/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.ts.snap index a5989965fb3d..f5df48b9c3e6 100644 --- a/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.ts.snap +++ b/crates/biome_js_analyze/tests/specs/complexity/useLiteralKeys/invalid.ts.snap @@ -28,6 +28,11 @@ export type T = { [""]: number } +export enum E { + ["A"], + ["B"], +} + ``` # Diagnostics @@ -218,3 +223,40 @@ invalid.ts:22:3 lint/complexity/useLiteralKeys FIXABLE ━━━━━━━ │ - - ``` + +``` +invalid.ts:26:3 lint/complexity/useLiteralKeys FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The computed expression can be simplified to a string literal. + + 25 │ export enum E { + > 26 │ ["A"], + │ ^^^ + 27 │ ["B"], + 28 │ } + + i Unsafe fix: Use a literal key instead. + + 26 │ → ["A"], + │ - - + +``` + +``` +invalid.ts:27:3 lint/complexity/useLiteralKeys FIXABLE ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + + ! The computed expression can be simplified to a string literal. + + 25 │ export enum E { + 26 │ ["A"], + > 27 │ ["B"], + │ ^^^ + 28 │ } + 29 │ + + i Unsafe fix: Use a literal key instead. + + 27 │ → ["B"], + │ - - + +``` diff --git a/crates/biome_js_analyze/tests/specs/correctness/noInvalidUseBeforeDeclaration/invalid.ts b/crates/biome_js_analyze/tests/specs/correctness/noInvalidUseBeforeDeclaration/invalid.ts index 11a2e0981120..5dadd03e45a4 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/noInvalidUseBeforeDeclaration/invalid.ts +++ b/crates/biome_js_analyze/tests/specs/correctness/noInvalidUseBeforeDeclaration/invalid.ts @@ -1,3 +1,8 @@ class C { constructor(readonly a = b, readonly b = 0) {} } + +enum E { + A = B, + B, +} diff --git a/crates/biome_js_analyze/tests/specs/correctness/noInvalidUseBeforeDeclaration/invalid.ts.snap b/crates/biome_js_analyze/tests/specs/correctness/noInvalidUseBeforeDeclaration/invalid.ts.snap index ab1393a271dd..1b9b70cd55ba 100644 --- a/crates/biome_js_analyze/tests/specs/correctness/noInvalidUseBeforeDeclaration/invalid.ts.snap +++ b/crates/biome_js_analyze/tests/specs/correctness/noInvalidUseBeforeDeclaration/invalid.ts.snap @@ -8,6 +8,11 @@ class C { constructor(readonly a = b, readonly b = 0) {} } +enum E { + A = B, + B, +} + ``` # Diagnostics @@ -33,4 +38,25 @@ invalid.ts:2:30 lint/correctness/noInvalidUseBeforeDeclaration ━━━━━ ``` +``` +invalid.ts:6:9 lint/correctness/noInvalidUseBeforeDeclaration ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ + ! This enum member is used before its declaration. + + 5 │ enum E { + > 6 │ A = B, + │ ^ + 7 │ B, + 8 │ } + + i The enum member is declared here: + + 5 │ enum E { + 6 │ A = B, + > 7 │ B, + │ ^ + 8 │ } + 9 │ + + +``` diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUndeclaredVariables/validEnumMemberRef.ts b/crates/biome_js_analyze/tests/specs/correctness/noUndeclaredVariables/validEnumMemberRef.ts new file mode 100644 index 000000000000..eb2316fd44cc --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noUndeclaredVariables/validEnumMemberRef.ts @@ -0,0 +1,6 @@ +enum E { + A = 1, + B = A << 1, + "C" = 3 << 1, + D = C << 1, +} diff --git a/crates/biome_js_analyze/tests/specs/correctness/noUndeclaredVariables/validEnumMemberRef.ts.snap b/crates/biome_js_analyze/tests/specs/correctness/noUndeclaredVariables/validEnumMemberRef.ts.snap new file mode 100644 index 000000000000..704de8b99574 --- /dev/null +++ b/crates/biome_js_analyze/tests/specs/correctness/noUndeclaredVariables/validEnumMemberRef.ts.snap @@ -0,0 +1,14 @@ +--- +source: crates/biome_js_analyze/tests/spec_tests.rs +expression: validEnumMemberRef.ts +--- +# Input +```ts +enum E { + A = 1, + B = A << 1, + "C" = 3 << 1, + D = C << 1, +} + +``` diff --git a/crates/biome_js_factory/src/generated/node_factory.rs b/crates/biome_js_factory/src/generated/node_factory.rs index f2a104715ed7..30a8fa06ca70 100644 --- a/crates/biome_js_factory/src/generated/node_factory.rs +++ b/crates/biome_js_factory/src/generated/node_factory.rs @@ -4580,14 +4580,14 @@ impl TsEnumDeclarationBuilder { )) } } -pub fn ts_enum_member(name: AnyJsObjectMemberName) -> TsEnumMemberBuilder { +pub fn ts_enum_member(name: AnyTsEnumMemberName) -> TsEnumMemberBuilder { TsEnumMemberBuilder { name, initializer: None, } } pub struct TsEnumMemberBuilder { - name: AnyJsObjectMemberName, + name: AnyTsEnumMemberName, initializer: Option, } impl TsEnumMemberBuilder { @@ -5308,6 +5308,12 @@ impl TsIntersectionTypeBuilder { )) } } +pub fn ts_literal_enum_member_name(value_token: SyntaxToken) -> TsLiteralEnumMemberName { + TsLiteralEnumMemberName::unwrap_cast(SyntaxNode::new_detached( + JsSyntaxKind::TS_LITERAL_ENUM_MEMBER_NAME, + [Some(SyntaxElement::Token(value_token))], + )) +} pub fn ts_mapped_type( l_curly_token: SyntaxToken, l_brack_token: SyntaxToken, diff --git a/crates/biome_js_factory/src/generated/syntax_factory.rs b/crates/biome_js_factory/src/generated/syntax_factory.rs index 0dfed3fa7967..c114d1eda91d 100644 --- a/crates/biome_js_factory/src/generated/syntax_factory.rs +++ b/crates/biome_js_factory/src/generated/syntax_factory.rs @@ -6987,7 +6987,7 @@ impl SyntaxFactory for JsSyntaxFactory { let mut slots: RawNodeSlots<2usize> = RawNodeSlots::default(); let mut current_element = elements.next(); if let Some(element) = ¤t_element { - if AnyJsObjectMemberName::can_cast(element.kind()) { + if AnyTsEnumMemberName::can_cast(element.kind()) { slots.mark_present(); current_element = elements.next(); } @@ -7959,6 +7959,25 @@ impl SyntaxFactory for JsSyntaxFactory { } slots.into_node(TS_INTERSECTION_TYPE, children) } + TS_LITERAL_ENUM_MEMBER_NAME => { + let mut elements = (&children).into_iter(); + let mut slots: RawNodeSlots<1usize> = RawNodeSlots::default(); + let mut current_element = elements.next(); + if let Some(element) = ¤t_element { + if matches!(element.kind(), IDENT | JS_STRING_LITERAL) { + slots.mark_present(); + current_element = elements.next(); + } + } + slots.next_slot(); + if current_element.is_some() { + return RawSyntaxNode::new( + TS_LITERAL_ENUM_MEMBER_NAME.to_bogus(), + children.into_iter().map(Some), + ); + } + slots.into_node(TS_LITERAL_ENUM_MEMBER_NAME, children) + } TS_MAPPED_TYPE => { let mut elements = (&children).into_iter(); let mut slots: RawNodeSlots<12usize> = RawNodeSlots::default(); diff --git a/crates/biome_js_formatter/src/generated.rs b/crates/biome_js_formatter/src/generated.rs index 3018bb43d240..2db2e99696e3 100644 --- a/crates/biome_js_formatter/src/generated.rs +++ b/crates/biome_js_formatter/src/generated.rs @@ -8154,6 +8154,46 @@ impl IntoFormat for biome_js_syntax::TsIntersectionType { ) } } +impl FormatRule + for crate::ts::objects::literal_enum_member_name::FormatTsLiteralEnumMemberName +{ + type Context = JsFormatContext; + #[inline(always)] + fn fmt( + &self, + node: &biome_js_syntax::TsLiteralEnumMemberName, + f: &mut JsFormatter, + ) -> FormatResult<()> { + FormatNodeRule::::fmt(self, node, f) + } +} +impl AsFormat for biome_js_syntax::TsLiteralEnumMemberName { + type Format<'a> = FormatRefWithRule< + 'a, + biome_js_syntax::TsLiteralEnumMemberName, + crate::ts::objects::literal_enum_member_name::FormatTsLiteralEnumMemberName, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule::new( + self, + crate::ts::objects::literal_enum_member_name::FormatTsLiteralEnumMemberName::default(), + ) + } +} +impl IntoFormat for biome_js_syntax::TsLiteralEnumMemberName { + type Format = FormatOwnedWithRule< + biome_js_syntax::TsLiteralEnumMemberName, + crate::ts::objects::literal_enum_member_name::FormatTsLiteralEnumMemberName, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule::new( + self, + crate::ts::objects::literal_enum_member_name::FormatTsLiteralEnumMemberName::default(), + ) + } +} impl FormatRule for crate::ts::types::mapped_type::FormatTsMappedType { @@ -13006,6 +13046,33 @@ impl IntoFormat for biome_js_syntax::AnyJsxTag { FormatOwnedWithRule::new(self, crate::jsx::any::tag::FormatAnyJsxTag::default()) } } +impl AsFormat for biome_js_syntax::AnyTsEnumMemberName { + type Format<'a> = FormatRefWithRule< + 'a, + biome_js_syntax::AnyTsEnumMemberName, + crate::ts::any::enum_member_name::FormatAnyTsEnumMemberName, + >; + fn format(&self) -> Self::Format<'_> { + #![allow(clippy::default_constructed_unit_structs)] + FormatRefWithRule::new( + self, + crate::ts::any::enum_member_name::FormatAnyTsEnumMemberName::default(), + ) + } +} +impl IntoFormat for biome_js_syntax::AnyTsEnumMemberName { + type Format = FormatOwnedWithRule< + biome_js_syntax::AnyTsEnumMemberName, + crate::ts::any::enum_member_name::FormatAnyTsEnumMemberName, + >; + fn into_format(self) -> Self::Format { + #![allow(clippy::default_constructed_unit_structs)] + FormatOwnedWithRule::new( + self, + crate::ts::any::enum_member_name::FormatAnyTsEnumMemberName::default(), + ) + } +} impl AsFormat for biome_js_syntax::AnyTsExternalModuleDeclarationBody { type Format<'a> = FormatRefWithRule< 'a, diff --git a/crates/biome_js_formatter/src/js/classes/method_class_member.rs b/crates/biome_js_formatter/src/js/classes/method_class_member.rs index 919525db8704..2e2f0003aeac 100644 --- a/crates/biome_js_formatter/src/js/classes/method_class_member.rs +++ b/crates/biome_js_formatter/src/js/classes/method_class_member.rs @@ -1,10 +1,11 @@ use crate::prelude::*; use crate::js::declarations::function_declaration::should_group_function_parameters; +use crate::utils::object::AnyJsMemberName; use biome_formatter::write; use biome_js_syntax::{ - AnyJsClassMemberName, AnyJsObjectMemberName, JsConstructorClassMember, JsConstructorParameters, - JsFunctionBody, JsParameters, TsMethodSignatureClassMember, TsMethodSignatureTypeMember, + AnyJsClassMemberName, JsConstructorClassMember, JsConstructorParameters, JsFunctionBody, + JsParameters, TsMethodSignatureClassMember, TsMethodSignatureTypeMember, TsReturnTypeAnnotation, TsTypeParameters, }; use biome_js_syntax::{JsMethodClassMember, JsMethodObjectMember, JsSyntaxToken}; @@ -110,12 +111,12 @@ impl FormatAnyJsMethodMember { } } - fn name(&self) -> SyntaxResult { + fn name(&self) -> SyntaxResult { Ok(match self { FormatAnyJsMethodMember::JsMethodClassMember(member) => member.name()?.into(), FormatAnyJsMethodMember::JsMethodObjectMember(member) => member.name()?.into(), FormatAnyJsMethodMember::JsConstructorClassMember(member) => { - AnyMemberName::from(AnyJsClassMemberName::from(member.name()?)) + AnyJsMemberName::from(AnyJsClassMemberName::from(member.name()?)) } FormatAnyJsMethodMember::TsMethodSignatureClassMember(signature) => { signature.name()?.into() @@ -193,19 +194,6 @@ impl FormatAnyJsMethodMember { } } -declare_node_union! { - AnyMemberName = AnyJsClassMemberName | AnyJsObjectMemberName -} - -impl Format for AnyMemberName { - fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { - match self { - AnyMemberName::AnyJsClassMemberName(name) => name.format().fmt(f), - AnyMemberName::AnyJsObjectMemberName(name) => name.format().fmt(f), - } - } -} - declare_node_union! { MethodParameters = JsParameters | JsConstructorParameters } diff --git a/crates/biome_js_formatter/src/ts/any/enum_member_name.rs b/crates/biome_js_formatter/src/ts/any/enum_member_name.rs new file mode 100644 index 000000000000..3b159a77eda3 --- /dev/null +++ b/crates/biome_js_formatter/src/ts/any/enum_member_name.rs @@ -0,0 +1,15 @@ +//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. + +use crate::prelude::*; +use biome_js_syntax::AnyTsEnumMemberName; +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatAnyTsEnumMemberName; +impl FormatRule for FormatAnyTsEnumMemberName { + type Context = JsFormatContext; + fn fmt(&self, node: &AnyTsEnumMemberName, f: &mut JsFormatter) -> FormatResult<()> { + match node { + AnyTsEnumMemberName::JsComputedMemberName(node) => node.format().fmt(f), + AnyTsEnumMemberName::TsLiteralEnumMemberName(node) => node.format().fmt(f), + } + } +} diff --git a/crates/biome_js_formatter/src/ts/any/mod.rs b/crates/biome_js_formatter/src/ts/any/mod.rs index cb18b1695a9c..1e301b6df2f8 100644 --- a/crates/biome_js_formatter/src/ts/any/mod.rs +++ b/crates/biome_js_formatter/src/ts/any/mod.rs @@ -1,5 +1,6 @@ //! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. +pub(crate) mod enum_member_name; pub(crate) mod external_module_declaration_body; pub(crate) mod index_signature_modifier; pub(crate) mod method_signature_modifier; diff --git a/crates/biome_js_formatter/src/ts/mod.rs b/crates/biome_js_formatter/src/ts/mod.rs index fcddebf06402..318061cb4a3e 100644 --- a/crates/biome_js_formatter/src/ts/mod.rs +++ b/crates/biome_js_formatter/src/ts/mod.rs @@ -10,5 +10,6 @@ pub(crate) mod declarations; pub(crate) mod expressions; pub(crate) mod lists; pub(crate) mod module; +pub(crate) mod objects; pub(crate) mod statements; pub(crate) mod types; diff --git a/crates/biome_js_formatter/src/ts/objects/literal_enum_member_name.rs b/crates/biome_js_formatter/src/ts/objects/literal_enum_member_name.rs new file mode 100644 index 000000000000..145eb8f0a56b --- /dev/null +++ b/crates/biome_js_formatter/src/ts/objects/literal_enum_member_name.rs @@ -0,0 +1,26 @@ +use crate::prelude::*; +use crate::utils::{FormatLiteralStringToken, StringLiteralParentKind}; +use biome_formatter::write; +use biome_js_syntax::{JsSyntaxKind, TsLiteralEnumMemberName, TsLiteralEnumMemberNameFields}; + +#[derive(Debug, Clone, Default)] +pub(crate) struct FormatTsLiteralEnumMemberName; + +impl FormatNodeRule for FormatTsLiteralEnumMemberName { + fn fmt_fields(&self, node: &TsLiteralEnumMemberName, f: &mut JsFormatter) -> FormatResult<()> { + let TsLiteralEnumMemberNameFields { value } = node.as_fields(); + let value = value?; + match value.kind() { + JsSyntaxKind::JS_STRING_LITERAL => { + write![ + f, + [FormatLiteralStringToken::new( + &value, + StringLiteralParentKind::Member + )] + ] + } + _ => write![f, [value.format()]], + } + } +} diff --git a/crates/biome_js_formatter/src/ts/objects/mod.rs b/crates/biome_js_formatter/src/ts/objects/mod.rs new file mode 100644 index 000000000000..e551791f91f2 --- /dev/null +++ b/crates/biome_js_formatter/src/ts/objects/mod.rs @@ -0,0 +1,3 @@ +//! This is a generated file. Don't modify it by hand! Run 'cargo codegen formatter' to re-generate the file. + +pub(crate) mod literal_enum_member_name; diff --git a/crates/biome_js_formatter/src/utils/mod.rs b/crates/biome_js_formatter/src/utils/mod.rs index b4363a48fe9f..296fa83b779b 100644 --- a/crates/biome_js_formatter/src/utils/mod.rs +++ b/crates/biome_js_formatter/src/utils/mod.rs @@ -10,7 +10,7 @@ pub(crate) mod format_node_without_comments; pub(crate) mod function_body; pub mod jsx; pub(crate) mod member_chain; -mod object; +pub(crate) mod object; mod object_like; mod object_pattern_like; #[cfg(test)] diff --git a/crates/biome_js_formatter/src/utils/object.rs b/crates/biome_js_formatter/src/utils/object.rs index 1cf073fad3e3..fbe04e23792c 100644 --- a/crates/biome_js_formatter/src/utils/object.rs +++ b/crates/biome_js_formatter/src/utils/object.rs @@ -14,12 +14,8 @@ declare_node_union! { impl Format for AnyJsMemberName { fn fmt(&self, f: &mut Formatter) -> FormatResult<()> { match self { - AnyJsMemberName::AnyJsObjectMemberName(node) => { - write!(f, [node.format()]) - } - AnyJsMemberName::AnyJsClassMemberName(node) => { - write!(f, [node.format()]) - } + Self::AnyJsObjectMemberName(name) => name.format().fmt(f), + Self::AnyJsClassMemberName(name) => name.format().fmt(f), } } } diff --git a/crates/biome_js_parser/src/syntax/typescript/statement.rs b/crates/biome_js_parser/src/syntax/typescript/statement.rs index fcd80801f768..ef24b43e289e 100644 --- a/crates/biome_js_parser/src/syntax/typescript/statement.rs +++ b/crates/biome_js_parser/src/syntax/typescript/statement.rs @@ -34,14 +34,15 @@ fn parse_literal_as_ts_enum_member(p: &mut JsParser) -> ParsedSyntax { JS_NUMBER_LITERAL => { let err = p.err_builder("An enum member cannot have a numeric name", p.cur_range()); p.error(err); - p.bump_any() + m.abandon(p); + return Absent; } _ => { m.abandon(p); return Absent; } } - Present(m.complete(p, JS_LITERAL_MEMBER_NAME)) + Present(m.complete(p, TS_LITERAL_ENUM_MEMBER_NAME)) } /// An individual enum member diff --git a/crates/biome_js_parser/test_data/inline/err/decorator_enum_export_default_declaration_clause.rast b/crates/biome_js_parser/test_data/inline/err/decorator_enum_export_default_declaration_clause.rast index fd6b5c7a4e1a..df3c8f12704f 100644 --- a/crates/biome_js_parser/test_data/inline/err/decorator_enum_export_default_declaration_clause.rast +++ b/crates/biome_js_parser/test_data/inline/err/decorator_enum_export_default_declaration_clause.rast @@ -31,21 +31,21 @@ JsModule { l_curly_token: L_CURLY@33..35 "{" [] [Whitespace(" ")], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@35..36 "X" [] [], }, initializer: missing (optional), }, COMMA@36..38 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@38..39 "Y" [] [], }, initializer: missing (optional), }, COMMA@39..41 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@41..43 "Z" [] [Whitespace(" ")], }, initializer: missing (optional), @@ -85,17 +85,17 @@ JsModule { 3: L_CURLY@33..35 "{" [] [Whitespace(" ")] 4: TS_ENUM_MEMBER_LIST@35..43 0: TS_ENUM_MEMBER@35..36 - 0: JS_LITERAL_MEMBER_NAME@35..36 + 0: TS_LITERAL_ENUM_MEMBER_NAME@35..36 0: IDENT@35..36 "X" [] [] 1: (empty) 1: COMMA@36..38 "," [] [Whitespace(" ")] 2: TS_ENUM_MEMBER@38..39 - 0: JS_LITERAL_MEMBER_NAME@38..39 + 0: TS_LITERAL_ENUM_MEMBER_NAME@38..39 0: IDENT@38..39 "Y" [] [] 1: (empty) 3: COMMA@39..41 "," [] [Whitespace(" ")] 4: TS_ENUM_MEMBER@41..43 - 0: JS_LITERAL_MEMBER_NAME@41..43 + 0: TS_LITERAL_ENUM_MEMBER_NAME@41..43 0: IDENT@41..43 "Z" [] [Whitespace(" ")] 1: (empty) 5: R_CURLY@43..44 "}" [] [] diff --git a/crates/biome_js_parser/test_data/inline/err/enum_decl_no_id.rast b/crates/biome_js_parser/test_data/inline/err/enum_decl_no_id.rast index bc812f94db89..1d61665affad 100644 --- a/crates/biome_js_parser/test_data/inline/err/enum_decl_no_id.rast +++ b/crates/biome_js_parser/test_data/inline/err/enum_decl_no_id.rast @@ -10,21 +10,21 @@ JsModule { l_curly_token: L_CURLY@5..6 "{" [] [], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@6..7 "A" [] [], }, initializer: missing (optional), }, COMMA@7..8 "," [] [], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@8..9 "B" [] [], }, initializer: missing (optional), }, COMMA@9..10 "," [] [], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@10..11 "C" [] [], }, initializer: missing (optional), @@ -43,21 +43,21 @@ JsModule { l_curly_token: L_CURLY@20..21 "{" [] [], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@21..22 "A" [] [], }, initializer: missing (optional), }, COMMA@22..23 "," [] [], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@23..24 "B" [] [], }, initializer: missing (optional), }, COMMA@24..25 "," [] [], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@25..26 "C" [] [], }, initializer: missing (optional), @@ -81,17 +81,17 @@ JsModule { 3: L_CURLY@5..6 "{" [] [] 4: TS_ENUM_MEMBER_LIST@6..11 0: TS_ENUM_MEMBER@6..7 - 0: JS_LITERAL_MEMBER_NAME@6..7 + 0: TS_LITERAL_ENUM_MEMBER_NAME@6..7 0: IDENT@6..7 "A" [] [] 1: (empty) 1: COMMA@7..8 "," [] [] 2: TS_ENUM_MEMBER@8..9 - 0: JS_LITERAL_MEMBER_NAME@8..9 + 0: TS_LITERAL_ENUM_MEMBER_NAME@8..9 0: IDENT@8..9 "B" [] [] 1: (empty) 3: COMMA@9..10 "," [] [] 4: TS_ENUM_MEMBER@10..11 - 0: JS_LITERAL_MEMBER_NAME@10..11 + 0: TS_LITERAL_ENUM_MEMBER_NAME@10..11 0: IDENT@10..11 "C" [] [] 1: (empty) 5: R_CURLY@11..12 "}" [] [] @@ -103,17 +103,17 @@ JsModule { 3: L_CURLY@20..21 "{" [] [] 4: TS_ENUM_MEMBER_LIST@21..26 0: TS_ENUM_MEMBER@21..22 - 0: JS_LITERAL_MEMBER_NAME@21..22 + 0: TS_LITERAL_ENUM_MEMBER_NAME@21..22 0: IDENT@21..22 "A" [] [] 1: (empty) 1: COMMA@22..23 "," [] [] 2: TS_ENUM_MEMBER@23..24 - 0: JS_LITERAL_MEMBER_NAME@23..24 + 0: TS_LITERAL_ENUM_MEMBER_NAME@23..24 0: IDENT@23..24 "B" [] [] 1: (empty) 3: COMMA@24..25 "," [] [] 4: TS_ENUM_MEMBER@25..26 - 0: JS_LITERAL_MEMBER_NAME@25..26 + 0: TS_LITERAL_ENUM_MEMBER_NAME@25..26 0: IDENT@25..26 "C" [] [] 1: (empty) 5: R_CURLY@26..27 "}" [] [] diff --git a/crates/biome_js_parser/test_data/inline/err/ts_export_default_enum.rast b/crates/biome_js_parser/test_data/inline/err/ts_export_default_enum.rast index d5c1002a9f98..fcac7f7e43b5 100644 --- a/crates/biome_js_parser/test_data/inline/err/ts_export_default_enum.rast +++ b/crates/biome_js_parser/test_data/inline/err/ts_export_default_enum.rast @@ -19,21 +19,21 @@ JsModule { l_curly_token: L_CURLY@22..24 "{" [] [Whitespace(" ")], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@24..25 "X" [] [], }, initializer: missing (optional), }, COMMA@25..27 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@27..28 "Y" [] [], }, initializer: missing (optional), }, COMMA@28..30 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@30..32 "Z" [] [Whitespace(" ")], }, initializer: missing (optional), @@ -67,17 +67,17 @@ JsModule { 3: L_CURLY@22..24 "{" [] [Whitespace(" ")] 4: TS_ENUM_MEMBER_LIST@24..32 0: TS_ENUM_MEMBER@24..25 - 0: JS_LITERAL_MEMBER_NAME@24..25 + 0: TS_LITERAL_ENUM_MEMBER_NAME@24..25 0: IDENT@24..25 "X" [] [] 1: (empty) 1: COMMA@25..27 "," [] [Whitespace(" ")] 2: TS_ENUM_MEMBER@27..28 - 0: JS_LITERAL_MEMBER_NAME@27..28 + 0: TS_LITERAL_ENUM_MEMBER_NAME@27..28 0: IDENT@27..28 "Y" [] [] 1: (empty) 3: COMMA@28..30 "," [] [Whitespace(" ")] 4: TS_ENUM_MEMBER@30..32 - 0: JS_LITERAL_MEMBER_NAME@30..32 + 0: TS_LITERAL_ENUM_MEMBER_NAME@30..32 0: IDENT@30..32 "Z" [] [Whitespace(" ")] 1: (empty) 5: R_CURLY@32..33 "}" [] [] diff --git a/crates/biome_js_parser/test_data/inline/ok/ts_ambient_enum_statement.rast b/crates/biome_js_parser/test_data/inline/ok/ts_ambient_enum_statement.rast index 4316436eaec3..3e114785ffe5 100644 --- a/crates/biome_js_parser/test_data/inline/ok/ts_ambient_enum_statement.rast +++ b/crates/biome_js_parser/test_data/inline/ok/ts_ambient_enum_statement.rast @@ -14,21 +14,21 @@ JsModule { l_curly_token: L_CURLY@15..17 "{" [] [Whitespace(" ")], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@17..18 "X" [] [], }, initializer: missing (optional), }, COMMA@18..20 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@20..21 "Y" [] [], }, initializer: missing (optional), }, COMMA@21..23 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@23..25 "Z" [] [Whitespace(" ")], }, initializer: missing (optional), @@ -48,21 +48,21 @@ JsModule { l_curly_token: L_CURLY@48..50 "{" [] [Whitespace(" ")], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@50..51 "X" [] [], }, initializer: missing (optional), }, COMMA@51..53 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@53..54 "Y" [] [], }, initializer: missing (optional), }, COMMA@54..56 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@56..58 "Z" [] [Whitespace(" ")], }, initializer: missing (optional), @@ -90,17 +90,17 @@ JsModule { 3: L_CURLY@15..17 "{" [] [Whitespace(" ")] 4: TS_ENUM_MEMBER_LIST@17..25 0: TS_ENUM_MEMBER@17..18 - 0: JS_LITERAL_MEMBER_NAME@17..18 + 0: TS_LITERAL_ENUM_MEMBER_NAME@17..18 0: IDENT@17..18 "X" [] [] 1: (empty) 1: COMMA@18..20 "," [] [Whitespace(" ")] 2: TS_ENUM_MEMBER@20..21 - 0: JS_LITERAL_MEMBER_NAME@20..21 + 0: TS_LITERAL_ENUM_MEMBER_NAME@20..21 0: IDENT@20..21 "Y" [] [] 1: (empty) 3: COMMA@21..23 "," [] [Whitespace(" ")] 4: TS_ENUM_MEMBER@23..25 - 0: JS_LITERAL_MEMBER_NAME@23..25 + 0: TS_LITERAL_ENUM_MEMBER_NAME@23..25 0: IDENT@23..25 "Z" [] [Whitespace(" ")] 1: (empty) 5: R_CURLY@25..26 "}" [] [] @@ -114,17 +114,17 @@ JsModule { 3: L_CURLY@48..50 "{" [] [Whitespace(" ")] 4: TS_ENUM_MEMBER_LIST@50..58 0: TS_ENUM_MEMBER@50..51 - 0: JS_LITERAL_MEMBER_NAME@50..51 + 0: TS_LITERAL_ENUM_MEMBER_NAME@50..51 0: IDENT@50..51 "X" [] [] 1: (empty) 1: COMMA@51..53 "," [] [Whitespace(" ")] 2: TS_ENUM_MEMBER@53..54 - 0: JS_LITERAL_MEMBER_NAME@53..54 + 0: TS_LITERAL_ENUM_MEMBER_NAME@53..54 0: IDENT@53..54 "Y" [] [] 1: (empty) 3: COMMA@54..56 "," [] [Whitespace(" ")] 4: TS_ENUM_MEMBER@56..58 - 0: JS_LITERAL_MEMBER_NAME@56..58 + 0: TS_LITERAL_ENUM_MEMBER_NAME@56..58 0: IDENT@56..58 "Z" [] [Whitespace(" ")] 1: (empty) 5: R_CURLY@58..59 "}" [] [] diff --git a/crates/biome_js_parser/test_data/inline/ok/ts_export_enum_declaration.rast b/crates/biome_js_parser/test_data/inline/ok/ts_export_enum_declaration.rast index bcc9d18a69dd..5700dc112211 100644 --- a/crates/biome_js_parser/test_data/inline/ok/ts_export_enum_declaration.rast +++ b/crates/biome_js_parser/test_data/inline/ok/ts_export_enum_declaration.rast @@ -15,14 +15,14 @@ JsModule { l_curly_token: L_CURLY@14..16 "{" [] [Whitespace(" ")], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@16..17 "X" [] [], }, initializer: missing (optional), }, COMMA@17..19 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@19..21 "Y" [] [Whitespace(" ")], }, initializer: missing (optional), @@ -43,14 +43,14 @@ JsModule { l_curly_token: L_CURLY@43..45 "{" [] [Whitespace(" ")], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@45..46 "X" [] [], }, initializer: missing (optional), }, COMMA@46..48 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@48..50 "Y" [] [Whitespace(" ")], }, initializer: missing (optional), @@ -79,12 +79,12 @@ JsModule { 3: L_CURLY@14..16 "{" [] [Whitespace(" ")] 4: TS_ENUM_MEMBER_LIST@16..21 0: TS_ENUM_MEMBER@16..17 - 0: JS_LITERAL_MEMBER_NAME@16..17 + 0: TS_LITERAL_ENUM_MEMBER_NAME@16..17 0: IDENT@16..17 "X" [] [] 1: (empty) 1: COMMA@17..19 "," [] [Whitespace(" ")] 2: TS_ENUM_MEMBER@19..21 - 0: JS_LITERAL_MEMBER_NAME@19..21 + 0: TS_LITERAL_ENUM_MEMBER_NAME@19..21 0: IDENT@19..21 "Y" [] [Whitespace(" ")] 1: (empty) 5: R_CURLY@21..22 "}" [] [] @@ -99,12 +99,12 @@ JsModule { 3: L_CURLY@43..45 "{" [] [Whitespace(" ")] 4: TS_ENUM_MEMBER_LIST@45..50 0: TS_ENUM_MEMBER@45..46 - 0: JS_LITERAL_MEMBER_NAME@45..46 + 0: TS_LITERAL_ENUM_MEMBER_NAME@45..46 0: IDENT@45..46 "X" [] [] 1: (empty) 1: COMMA@46..48 "," [] [Whitespace(" ")] 2: TS_ENUM_MEMBER@48..50 - 0: JS_LITERAL_MEMBER_NAME@48..50 + 0: TS_LITERAL_ENUM_MEMBER_NAME@48..50 0: IDENT@48..50 "Y" [] [Whitespace(" ")] 1: (empty) 5: R_CURLY@50..51 "}" [] [] diff --git a/crates/biome_js_parser/test_data/inline/ok/typescript_enum.rast b/crates/biome_js_parser/test_data/inline/ok/typescript_enum.rast index 97972e49ea6e..42bdc51923c5 100644 --- a/crates/biome_js_parser/test_data/inline/ok/typescript_enum.rast +++ b/crates/biome_js_parser/test_data/inline/ok/typescript_enum.rast @@ -22,21 +22,21 @@ JsModule { l_curly_token: L_CURLY@17..19 "{" [] [Whitespace(" ")], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@19..20 "a" [] [], }, initializer: missing (optional), }, COMMA@20..22 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@22..23 "b" [] [], }, initializer: missing (optional), }, COMMA@23..25 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@25..27 "c" [] [Whitespace(" ")], }, initializer: missing (optional), @@ -53,7 +53,7 @@ JsModule { l_curly_token: L_CURLY@42..44 "{" [] [Whitespace(" ")], members: TsEnumMemberList [ TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@44..46 "A" [] [Whitespace(" ")], }, initializer: JsInitializerClause { @@ -65,7 +65,7 @@ JsModule { }, COMMA@49..51 "," [] [Whitespace(" ")], TsEnumMember { - name: JsLiteralMemberName { + name: TsLiteralEnumMemberName { value: IDENT@51..53 "B" [] [Whitespace(" ")], }, initializer: JsInitializerClause { @@ -128,17 +128,17 @@ JsModule { 3: L_CURLY@17..19 "{" [] [Whitespace(" ")] 4: TS_ENUM_MEMBER_LIST@19..27 0: TS_ENUM_MEMBER@19..20 - 0: JS_LITERAL_MEMBER_NAME@19..20 + 0: TS_LITERAL_ENUM_MEMBER_NAME@19..20 0: IDENT@19..20 "a" [] [] 1: (empty) 1: COMMA@20..22 "," [] [Whitespace(" ")] 2: TS_ENUM_MEMBER@22..23 - 0: JS_LITERAL_MEMBER_NAME@22..23 + 0: TS_LITERAL_ENUM_MEMBER_NAME@22..23 0: IDENT@22..23 "b" [] [] 1: (empty) 3: COMMA@23..25 "," [] [Whitespace(" ")] 4: TS_ENUM_MEMBER@25..27 - 0: JS_LITERAL_MEMBER_NAME@25..27 + 0: TS_LITERAL_ENUM_MEMBER_NAME@25..27 0: IDENT@25..27 "c" [] [Whitespace(" ")] 1: (empty) 5: R_CURLY@27..28 "}" [] [] @@ -150,7 +150,7 @@ JsModule { 3: L_CURLY@42..44 "{" [] [Whitespace(" ")] 4: TS_ENUM_MEMBER_LIST@44..73 0: TS_ENUM_MEMBER@44..49 - 0: JS_LITERAL_MEMBER_NAME@44..46 + 0: TS_LITERAL_ENUM_MEMBER_NAME@44..46 0: IDENT@44..46 "A" [] [Whitespace(" ")] 1: JS_INITIALIZER_CLAUSE@46..49 0: EQ@46..48 "=" [] [Whitespace(" ")] @@ -158,7 +158,7 @@ JsModule { 0: JS_NUMBER_LITERAL@48..49 "1" [] [] 1: COMMA@49..51 "," [] [Whitespace(" ")] 2: TS_ENUM_MEMBER@51..60 - 0: JS_LITERAL_MEMBER_NAME@51..53 + 0: TS_LITERAL_ENUM_MEMBER_NAME@51..53 0: IDENT@51..53 "B" [] [Whitespace(" ")] 1: JS_INITIALIZER_CLAUSE@53..60 0: EQ@53..55 "=" [] [Whitespace(" ")] diff --git a/crates/biome_js_semantic/src/events.rs b/crates/biome_js_semantic/src/events.rs index 9b01519b8b33..501e8f3c5448 100644 --- a/crates/biome_js_semantic/src/events.rs +++ b/crates/biome_js_semantic/src/events.rs @@ -2,7 +2,8 @@ use biome_js_syntax::binding_ext::{AnyJsBindingDeclaration, AnyJsIdentifierBinding}; use biome_js_syntax::{ - AnyJsIdentifierUsage, JsLanguage, JsSyntaxKind, JsSyntaxNode, TextRange, TsTypeParameterName, + inner_string_text, AnyJsIdentifierUsage, JsLanguage, JsSyntaxKind, JsSyntaxNode, TextRange, + TsTypeParameterName, }; use biome_js_syntax::{AnyJsImportClause, AnyJsNamedImportSpecifier, AnyTsType}; use biome_rowan::{syntax::Preorder, AstNode, SyntaxNodeOptionExt, TokenText}; @@ -297,7 +298,10 @@ impl SemanticEventExtractor { // If you push a scope for a given node type, don't forget to also update `Self::leave`. // You should also edit [SemanticModelBuilder::push_node]. match node.kind() { - JS_IDENTIFIER_BINDING | TS_IDENTIFIER_BINDING | TS_TYPE_PARAMETER_NAME => { + JS_IDENTIFIER_BINDING + | TS_IDENTIFIER_BINDING + | TS_TYPE_PARAMETER_NAME + | TS_LITERAL_ENUM_MEMBER_NAME => { self.enter_identifier_binding(&AnyJsIdentifierBinding::unwrap_cast(node.clone())); } @@ -428,6 +432,12 @@ impl SemanticEventExtractor { hoisted_scope_id = self.scope_index_to_hoist_declarations(1); self.push_binding(hoisted_scope_id, BindingName::Value(name), info); } + AnyJsBindingDeclaration::TsEnumMember(_) => { + // Handle quoted names. + let name = inner_string_text(&name_token); + self.push_binding(None, BindingName::Value(name.clone()), info.clone()); + self.push_binding(None, BindingName::Type(name), info); + } AnyJsBindingDeclaration::JsClassExpression(_) | AnyJsBindingDeclaration::JsFunctionExpression(_) => { self.push_binding(None, BindingName::Value(name.clone()), info.clone()); diff --git a/crates/biome_js_semantic/src/semantic_model/builder.rs b/crates/biome_js_semantic/src/semantic_model/builder.rs index 665c5ceded69..35d966b85a5e 100644 --- a/crates/biome_js_semantic/src/semantic_model/builder.rs +++ b/crates/biome_js_semantic/src/semantic_model/builder.rs @@ -54,6 +54,7 @@ impl SemanticModelBuilder { | JS_REFERENCE_IDENTIFIER | JSX_REFERENCE_IDENTIFIER | TS_TYPE_PARAMETER_NAME + | TS_LITERAL_ENUM_MEMBER_NAME | JS_IDENTIFIER_ASSIGNMENT => { self.node_by_range.insert(node.text_range(), node.clone()); } diff --git a/crates/biome_js_syntax/src/binding_ext.rs b/crates/biome_js_syntax/src/binding_ext.rs index ca2f61f5d418..9b53b4914f82 100644 --- a/crates/biome_js_syntax/src/binding_ext.rs +++ b/crates/biome_js_syntax/src/binding_ext.rs @@ -12,11 +12,12 @@ use crate::{ JsSyntaxNode, JsSyntaxToken, JsVariableDeclarator, TsCallSignatureTypeMember, TsConstructSignatureTypeMember, TsConstructorSignatureClassMember, TsConstructorType, TsDeclareFunctionDeclaration, TsDeclareFunctionExportDefaultDeclaration, TsEnumDeclaration, - TsFunctionType, TsIdentifierBinding, TsImportEqualsDeclaration, TsIndexSignatureClassMember, - TsIndexSignatureParameter, TsInferType, TsInterfaceDeclaration, TsMappedType, - TsMethodSignatureClassMember, TsMethodSignatureTypeMember, TsModuleDeclaration, - TsPropertyParameter, TsSetterSignatureClassMember, TsSetterSignatureTypeMember, - TsTypeAliasDeclaration, TsTypeParameter, TsTypeParameterName, + TsEnumMember, TsFunctionType, TsIdentifierBinding, TsImportEqualsDeclaration, + TsIndexSignatureClassMember, TsIndexSignatureParameter, TsInferType, TsInterfaceDeclaration, + TsLiteralEnumMemberName, TsMappedType, TsMethodSignatureClassMember, + TsMethodSignatureTypeMember, TsModuleDeclaration, TsPropertyParameter, + TsSetterSignatureClassMember, TsSetterSignatureTypeMember, TsTypeAliasDeclaration, + TsTypeParameter, TsTypeParameterName, }; use biome_rowan::{declare_node_union, AstNode, SyntaxResult}; @@ -38,6 +39,8 @@ declare_node_union! { // functions | JsFunctionDeclaration | JsFunctionExpression | TsDeclareFunctionDeclaration + // enum member + | TsEnumMember // classes, objects, interface, type, enum, module | JsClassDeclaration | JsClassExpression | TsInterfaceDeclaration | TsTypeAliasDeclaration | TsEnumDeclaration | TsModuleDeclaration @@ -235,7 +238,7 @@ impl AnyJsBindingDeclaration { } declare_node_union! { - pub AnyJsIdentifierBinding = JsIdentifierBinding | TsIdentifierBinding | TsTypeParameterName + pub AnyJsIdentifierBinding = JsIdentifierBinding | TsIdentifierBinding | TsTypeParameterName | TsLiteralEnumMemberName } fn declaration(node: JsSyntaxNode) -> Option { @@ -310,9 +313,10 @@ fn is_under_object_pattern_binding(node: &JsSyntaxNode) -> Option { impl AnyJsIdentifierBinding { pub fn name_token(&self) -> SyntaxResult { match self { - AnyJsIdentifierBinding::JsIdentifierBinding(binding) => binding.name_token(), - AnyJsIdentifierBinding::TsIdentifierBinding(binding) => binding.name_token(), - AnyJsIdentifierBinding::TsTypeParameterName(binding) => binding.ident_token(), + Self::JsIdentifierBinding(binding) => binding.name_token(), + Self::TsIdentifierBinding(binding) => binding.name_token(), + Self::TsTypeParameterName(binding) => binding.ident_token(), + Self::TsLiteralEnumMemberName(binding) => binding.value(), } } @@ -335,7 +339,7 @@ impl AnyJsIdentifierBinding { /// Returns true if this binding is only a type and not a runtime value. pub fn is_type_only(&self) -> bool { match self { - AnyJsIdentifierBinding::JsIdentifierBinding(binding) => { + Self::JsIdentifierBinding(binding) => { if let Some(specifier) = binding.parent::() { return specifier.imports_only_types(); } @@ -347,11 +351,11 @@ impl AnyJsIdentifierBinding { return clause.type_token().is_some(); } } - AnyJsIdentifierBinding::TsIdentifierBinding(binding) => { + Self::TsIdentifierBinding(binding) => { // ignore TypeScript namespaces return binding.parent::().is_none(); } - AnyJsIdentifierBinding::TsTypeParameterName(_) => {} + Self::TsTypeParameterName(_) | Self::TsLiteralEnumMemberName(_) => {} } false } @@ -367,6 +371,9 @@ impl AnyJsIdentifierBinding { Self::TsTypeParameterName(binding) => { Self::TsTypeParameterName(binding.with_ident_token(name_token)) } + Self::TsLiteralEnumMemberName(binding) => { + Self::TsLiteralEnumMemberName(binding.with_value_token(name_token)) + } } } } diff --git a/crates/biome_js_syntax/src/expr_ext.rs b/crates/biome_js_syntax/src/expr_ext.rs index 4d0425ea4a4f..4314645a7aa1 100644 --- a/crates/biome_js_syntax/src/expr_ext.rs +++ b/crates/biome_js_syntax/src/expr_ext.rs @@ -4,15 +4,15 @@ use crate::static_value::StaticValue; use crate::{ inner_string_text, AnyJsArrowFunctionParameters, AnyJsCallArgument, AnyJsClassMemberName, AnyJsExpression, AnyJsFunctionBody, AnyJsLiteralExpression, AnyJsName, AnyJsObjectMemberName, - AnyJsTemplateElement, JsArrayExpression, JsArrayHole, JsAssignmentExpression, - JsBinaryExpression, JsCallArgumentList, JsCallArguments, JsCallExpression, - JsComputedMemberAssignment, JsComputedMemberExpression, JsConditionalExpression, - JsDoWhileStatement, JsForStatement, JsIfStatement, JsLiteralMemberName, JsLogicalExpression, - JsNewExpression, JsNumberLiteralExpression, JsObjectExpression, JsPostUpdateExpression, - JsReferenceIdentifier, JsRegexLiteralExpression, JsStaticMemberExpression, - JsStringLiteralExpression, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, JsTemplateChunkElement, - JsTemplateExpression, JsUnaryExpression, JsWhileStatement, OperatorPrecedence, - TsStringLiteralType, T, + AnyJsTemplateElement, AnyTsEnumMemberName, JsArrayExpression, JsArrayHole, + JsAssignmentExpression, JsBinaryExpression, JsCallArgumentList, JsCallArguments, + JsCallExpression, JsComputedMemberAssignment, JsComputedMemberExpression, + JsConditionalExpression, JsDoWhileStatement, JsForStatement, JsIfStatement, + JsLiteralMemberName, JsLogicalExpression, JsNewExpression, JsNumberLiteralExpression, + JsObjectExpression, JsPostUpdateExpression, JsReferenceIdentifier, JsRegexLiteralExpression, + JsStaticMemberExpression, JsStringLiteralExpression, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken, + JsTemplateChunkElement, JsTemplateExpression, JsUnaryExpression, JsWhileStatement, + OperatorPrecedence, TsStringLiteralType, T, }; use crate::{JsPreUpdateExpression, JsSyntaxKind::*}; use biome_rowan::{ @@ -1533,6 +1533,57 @@ impl AnyJsObjectMemberName { } } +impl AnyTsEnumMemberName { + /// Returns the member name of the current node + /// if it is a literal member name or a computed member with a literal value. + /// + /// ## Examples + /// + /// ``` + /// use biome_js_syntax::{AnyTsEnumMemberName, AnyJsExpression, AnyJsLiteralExpression, T}; + /// use biome_js_factory::make; + /// + /// let name = make::ts_literal_enum_member_name(make::ident("a")); + /// let name = AnyTsEnumMemberName::TsLiteralEnumMemberName(name); + /// assert_eq!(name.name().unwrap().text(), "a"); + /// + /// let quoted_name = make::ts_literal_enum_member_name(make::js_string_literal("a")); + /// let quoted_name = AnyTsEnumMemberName::TsLiteralEnumMemberName(quoted_name); + /// assert_eq!(quoted_name.name().unwrap().text(), "a"); + /// + /// let number_name = make::ts_literal_enum_member_name(make::js_number_literal(42)); + /// let number_name = AnyTsEnumMemberName::TsLiteralEnumMemberName(number_name); + /// assert_eq!(number_name.name().unwrap().text(), "42"); + /// + /// let string_literal = make::js_string_literal_expression(make::js_string_literal("a")); + /// let string_literal = AnyJsExpression::AnyJsLiteralExpression(AnyJsLiteralExpression::from(string_literal)); + /// let computed = make::js_computed_member_name(make::token(T!['[']), string_literal, make::token(T![']'])); + /// let computed = AnyTsEnumMemberName::JsComputedMemberName(computed); + /// assert_eq!(computed.name().unwrap().text(), "a"); + /// ``` + pub fn name(&self) -> Option { + let token = match self { + AnyTsEnumMemberName::JsComputedMemberName(expr) => { + let expr = expr.expression().ok()?; + match expr.omit_parentheses() { + AnyJsExpression::AnyJsLiteralExpression(expr) => expr.value_token().ok()?, + AnyJsExpression::JsTemplateExpression(expr) => { + if !expr.is_constant() { + return None; + } + let chunk = expr.elements().first()?; + let chunk = chunk.as_js_template_chunk_element()?; + chunk.template_chunk_token().ok()? + } + _ => return None, + } + } + AnyTsEnumMemberName::TsLiteralEnumMemberName(expr) => expr.value().ok()?, + }; + Some(inner_string_text(&token)) + } +} + impl AnyJsClassMemberName { /// Returns the member name of the current node /// if it is a literal, a computed, or a private class member with a literal value. diff --git a/crates/biome_js_syntax/src/generated/kind.rs b/crates/biome_js_syntax/src/generated/kind.rs index dd426589255f..02393005942a 100644 --- a/crates/biome_js_syntax/src/generated/kind.rs +++ b/crates/biome_js_syntax/src/generated/kind.rs @@ -462,6 +462,7 @@ pub enum JsSyntaxKind { TS_ENUM_DECLARATION, TS_ENUM_MEMBER_LIST, TS_ENUM_MEMBER, + TS_LITERAL_ENUM_MEMBER_NAME, TS_IMPORT_EQUALS_DECLARATION, TS_EXTERNAL_MODULE_REFERENCE, TS_DECLARE_FUNCTION_DECLARATION, diff --git a/crates/biome_js_syntax/src/generated/macros.rs b/crates/biome_js_syntax/src/generated/macros.rs index 27c066bb9bd5..0f348aaacc1b 100644 --- a/crates/biome_js_syntax/src/generated/macros.rs +++ b/crates/biome_js_syntax/src/generated/macros.rs @@ -974,6 +974,10 @@ macro_rules! map_syntax_node { let $pattern = unsafe { $crate::TsIntersectionType::new_unchecked(node) }; $body } + $crate::JsSyntaxKind::TS_LITERAL_ENUM_MEMBER_NAME => { + let $pattern = unsafe { $crate::TsLiteralEnumMemberName::new_unchecked(node) }; + $body + } $crate::JsSyntaxKind::TS_MAPPED_TYPE => { let $pattern = unsafe { $crate::TsMappedType::new_unchecked(node) }; $body diff --git a/crates/biome_js_syntax/src/generated/nodes.rs b/crates/biome_js_syntax/src/generated/nodes.rs index 31a4001e774e..6ca50e2cca98 100644 --- a/crates/biome_js_syntax/src/generated/nodes.rs +++ b/crates/biome_js_syntax/src/generated/nodes.rs @@ -9445,7 +9445,7 @@ impl TsEnumMember { initializer: self.initializer(), } } - pub fn name(&self) -> SyntaxResult { + pub fn name(&self) -> SyntaxResult { support::required_node(&self.syntax, 0usize) } pub fn initializer(&self) -> Option { @@ -9463,7 +9463,7 @@ impl Serialize for TsEnumMember { } #[cfg_attr(feature = "serde", derive(Serialize))] pub struct TsEnumMemberFields { - pub name: SyntaxResult, + pub name: SyntaxResult, pub initializer: Option, } #[derive(Clone, PartialEq, Eq, Hash)] @@ -10707,6 +10707,42 @@ pub struct TsIntersectionTypeFields { pub types: TsIntersectionTypeElementList, } #[derive(Clone, PartialEq, Eq, Hash)] +pub struct TsLiteralEnumMemberName { + pub(crate) syntax: SyntaxNode, +} +impl TsLiteralEnumMemberName { + #[doc = r" Create an AstNode from a SyntaxNode without checking its kind"] + #[doc = r""] + #[doc = r" # Safety"] + #[doc = r" This function must be guarded with a call to [AstNode::can_cast]"] + #[doc = r" or a match on [SyntaxNode::kind]"] + #[inline] + pub const unsafe fn new_unchecked(syntax: SyntaxNode) -> Self { + Self { syntax } + } + pub fn as_fields(&self) -> TsLiteralEnumMemberNameFields { + TsLiteralEnumMemberNameFields { + value: self.value(), + } + } + pub fn value(&self) -> SyntaxResult { + support::required_token(&self.syntax, 0usize) + } +} +#[cfg(feature = "serde")] +impl Serialize for TsLiteralEnumMemberName { + fn serialize(&self, serializer: S) -> Result + where + S: Serializer, + { + self.as_fields().serialize(serializer) + } +} +#[cfg_attr(feature = "serde", derive(Serialize))] +pub struct TsLiteralEnumMemberNameFields { + pub value: SyntaxResult, +} +#[derive(Clone, PartialEq, Eq, Hash)] pub struct TsMappedType { pub(crate) syntax: SyntaxNode, } @@ -15611,6 +15647,26 @@ impl AnyJsxTag { } #[derive(Clone, PartialEq, Eq, Hash)] #[cfg_attr(feature = "serde", derive(Serialize))] +pub enum AnyTsEnumMemberName { + JsComputedMemberName(JsComputedMemberName), + TsLiteralEnumMemberName(TsLiteralEnumMemberName), +} +impl AnyTsEnumMemberName { + pub fn as_js_computed_member_name(&self) -> Option<&JsComputedMemberName> { + match &self { + AnyTsEnumMemberName::JsComputedMemberName(item) => Some(item), + _ => None, + } + } + pub fn as_ts_literal_enum_member_name(&self) -> Option<&TsLiteralEnumMemberName> { + match &self { + AnyTsEnumMemberName::TsLiteralEnumMemberName(item) => Some(item), + _ => None, + } + } +} +#[derive(Clone, PartialEq, Eq, Hash)] +#[cfg_attr(feature = "serde", derive(Serialize))] pub enum AnyTsExternalModuleDeclarationBody { TsEmptyExternalModuleDeclarationBody(TsEmptyExternalModuleDeclarationBody), TsModuleBlock(TsModuleBlock), @@ -26676,6 +26732,44 @@ impl From for SyntaxElement { n.syntax.into() } } +impl AstNode for TsLiteralEnumMemberName { + type Language = Language; + const KIND_SET: SyntaxKindSet = + SyntaxKindSet::from_raw(RawSyntaxKind(TS_LITERAL_ENUM_MEMBER_NAME as u16)); + fn can_cast(kind: SyntaxKind) -> bool { + kind == TS_LITERAL_ENUM_MEMBER_NAME + } + fn cast(syntax: SyntaxNode) -> Option { + if Self::can_cast(syntax.kind()) { + Some(Self { syntax }) + } else { + None + } + } + fn syntax(&self) -> &SyntaxNode { + &self.syntax + } + fn into_syntax(self) -> SyntaxNode { + self.syntax + } +} +impl std::fmt::Debug for TsLiteralEnumMemberName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("TsLiteralEnumMemberName") + .field("value", &support::DebugSyntaxResult(self.value())) + .finish() + } +} +impl From for SyntaxNode { + fn from(n: TsLiteralEnumMemberName) -> SyntaxNode { + n.syntax + } +} +impl From for SyntaxElement { + fn from(n: TsLiteralEnumMemberName) -> SyntaxElement { + n.syntax.into() + } +} impl AstNode for TsMappedType { type Language = Language; const KIND_SET: SyntaxKindSet = @@ -35042,6 +35136,70 @@ impl From for SyntaxElement { node.into() } } +impl From for AnyTsEnumMemberName { + fn from(node: JsComputedMemberName) -> AnyTsEnumMemberName { + AnyTsEnumMemberName::JsComputedMemberName(node) + } +} +impl From for AnyTsEnumMemberName { + fn from(node: TsLiteralEnumMemberName) -> AnyTsEnumMemberName { + AnyTsEnumMemberName::TsLiteralEnumMemberName(node) + } +} +impl AstNode for AnyTsEnumMemberName { + type Language = Language; + const KIND_SET: SyntaxKindSet = + JsComputedMemberName::KIND_SET.union(TsLiteralEnumMemberName::KIND_SET); + fn can_cast(kind: SyntaxKind) -> bool { + matches!(kind, JS_COMPUTED_MEMBER_NAME | TS_LITERAL_ENUM_MEMBER_NAME) + } + fn cast(syntax: SyntaxNode) -> Option { + let res = match syntax.kind() { + JS_COMPUTED_MEMBER_NAME => { + AnyTsEnumMemberName::JsComputedMemberName(JsComputedMemberName { syntax }) + } + TS_LITERAL_ENUM_MEMBER_NAME => { + AnyTsEnumMemberName::TsLiteralEnumMemberName(TsLiteralEnumMemberName { syntax }) + } + _ => return None, + }; + Some(res) + } + fn syntax(&self) -> &SyntaxNode { + match self { + AnyTsEnumMemberName::JsComputedMemberName(it) => &it.syntax, + AnyTsEnumMemberName::TsLiteralEnumMemberName(it) => &it.syntax, + } + } + fn into_syntax(self) -> SyntaxNode { + match self { + AnyTsEnumMemberName::JsComputedMemberName(it) => it.syntax, + AnyTsEnumMemberName::TsLiteralEnumMemberName(it) => it.syntax, + } + } +} +impl std::fmt::Debug for AnyTsEnumMemberName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + match self { + AnyTsEnumMemberName::JsComputedMemberName(it) => std::fmt::Debug::fmt(it, f), + AnyTsEnumMemberName::TsLiteralEnumMemberName(it) => std::fmt::Debug::fmt(it, f), + } + } +} +impl From for SyntaxNode { + fn from(n: AnyTsEnumMemberName) -> SyntaxNode { + match n { + AnyTsEnumMemberName::JsComputedMemberName(it) => it.into(), + AnyTsEnumMemberName::TsLiteralEnumMemberName(it) => it.into(), + } + } +} +impl From for SyntaxElement { + fn from(n: AnyTsEnumMemberName) -> SyntaxElement { + let node: SyntaxNode = n.into(); + node.into() + } +} impl From for AnyTsExternalModuleDeclarationBody { fn from(node: TsEmptyExternalModuleDeclarationBody) -> AnyTsExternalModuleDeclarationBody { AnyTsExternalModuleDeclarationBody::TsEmptyExternalModuleDeclarationBody(node) @@ -37212,6 +37370,11 @@ impl std::fmt::Display for AnyJsxTag { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for AnyTsEnumMemberName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for AnyTsExternalModuleDeclarationBody { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) @@ -38427,6 +38590,11 @@ impl std::fmt::Display for TsIntersectionType { std::fmt::Display::fmt(self.syntax(), f) } } +impl std::fmt::Display for TsLiteralEnumMemberName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + std::fmt::Display::fmt(self.syntax(), f) + } +} impl std::fmt::Display for TsMappedType { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { std::fmt::Display::fmt(self.syntax(), f) diff --git a/crates/biome_js_syntax/src/generated/nodes_mut.rs b/crates/biome_js_syntax/src/generated/nodes_mut.rs index cfe2816a78f3..c8b529cb5476 100644 --- a/crates/biome_js_syntax/src/generated/nodes_mut.rs +++ b/crates/biome_js_syntax/src/generated/nodes_mut.rs @@ -4289,7 +4289,7 @@ impl TsEnumDeclaration { } } impl TsEnumMember { - pub fn with_name(self, element: AnyJsObjectMemberName) -> Self { + pub fn with_name(self, element: AnyTsEnumMemberName) -> Self { Self::unwrap_cast( self.syntax .splice_slots(0usize..=0usize, once(Some(element.into_syntax().into()))), @@ -4910,6 +4910,14 @@ impl TsIntersectionType { ) } } +impl TsLiteralEnumMemberName { + pub fn with_value_token(self, element: SyntaxToken) -> Self { + Self::unwrap_cast( + self.syntax + .splice_slots(0usize..=0usize, once(Some(element.into()))), + ) + } +} impl TsMappedType { pub fn with_l_curly_token(self, element: SyntaxToken) -> Self { Self::unwrap_cast( diff --git a/xtask/codegen/js.ungram b/xtask/codegen/js.ungram index ff0e8e4f4561..492f66255c87 100644 --- a/xtask/codegen/js.ungram +++ b/xtask/codegen/js.ungram @@ -1582,7 +1582,6 @@ JsComputedMemberName = expression: AnyJsExpression ']' - JsParameters = '(' items: JsParameterList @@ -1752,9 +1751,15 @@ TsEnumDeclaration = TsEnumMemberList = (TsEnumMember (',' TsEnumMember)* ','?) TsEnumMember = - name: AnyJsObjectMemberName + name: AnyTsEnumMemberName initializer: JsInitializerClause? +AnyTsEnumMemberName = + TsLiteralEnumMemberName + | JsComputedMemberName + +TsLiteralEnumMemberName = value: ('ident' | 'js_string_literal') + TsImportEqualsDeclaration = 'import' 'type'? diff --git a/xtask/codegen/src/js_kinds_src.rs b/xtask/codegen/src/js_kinds_src.rs index ee98d0b2567c..1be9e8ddea35 100644 --- a/xtask/codegen/src/js_kinds_src.rs +++ b/xtask/codegen/src/js_kinds_src.rs @@ -470,6 +470,7 @@ pub const JS_KINDS_SRC: KindsSrc = KindsSrc { "TS_ENUM_DECLARATION", "TS_ENUM_MEMBER_LIST", "TS_ENUM_MEMBER", + "TS_LITERAL_ENUM_MEMBER_NAME", "TS_IMPORT_EQUALS_DECLARATION", "TS_EXTERNAL_MODULE_REFERENCE", "TS_DECLARE_FUNCTION_DECLARATION",