From ef7214319920188071cef9ac46ae2091a4c6df9a Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Fri, 16 May 2025 11:22:13 +0000 Subject: [PATCH] fix(parser): parse index signature with multiple parameter (#11068) fixes #11052 --- crates/oxc_codegen/examples/codegen.rs | 26 +++++++++---------- crates/oxc_codegen/src/gen.rs | 3 ++- crates/oxc_parser/src/cursor.rs | 3 +-- crates/oxc_parser/src/diagnostics.rs | 23 +++++----------- crates/oxc_parser/src/ts/types.rs | 23 ++++++++++------ .../coverage/snapshots/parser_typescript.snap | 4 +-- 6 files changed, 38 insertions(+), 44 deletions(-) diff --git a/crates/oxc_codegen/examples/codegen.rs b/crates/oxc_codegen/examples/codegen.rs index f374c3c65d108..697ff661ad207 100644 --- a/crates/oxc_codegen/examples/codegen.rs +++ b/crates/oxc_codegen/examples/codegen.rs @@ -2,8 +2,9 @@ use std::path::Path; use oxc_allocator::Allocator; +use oxc_ast::ast::Program; use oxc_codegen::{Codegen, CodegenOptions}; -use oxc_parser::{ParseOptions, Parser, ParserReturn}; +use oxc_parser::{ParseOptions, Parser}; use oxc_span::SourceType; use pico_args::Arguments; @@ -25,8 +26,8 @@ fn main() -> std::io::Result<()> { let mut allocator = Allocator::default(); let printed = { - let Some(ret) = parse(&allocator, &source_text, source_type) else { return Ok(()) }; - codegen(&ret, minify) + let program = parse(&allocator, &source_text, source_type); + codegen(&program, minify) }; println!("First time:"); println!("{printed}"); @@ -35,9 +36,9 @@ fn main() -> std::io::Result<()> { // Reset the allocator as we don't need the first AST any more allocator.reset(); - let Some(ret) = parse(&allocator, &printed, source_type) else { return Ok(()) }; + let program = parse(&allocator, &printed, source_type); println!("Second time:"); - let printed2 = codegen(&ret, minify); + let printed2 = codegen(&program, minify); println!("{printed2}"); // Check syntax error parse(&allocator, &printed2, source_type); @@ -51,25 +52,22 @@ fn parse<'a>( allocator: &'a Allocator, source_text: &'a str, source_type: SourceType, -) -> Option> { +) -> Program<'a> { let ret = Parser::new(allocator, source_text, source_type) .with_options(ParseOptions { allow_return_outside_function: true, ..ParseOptions::default() }) .parse(); - if !ret.errors.is_empty() { - for error in ret.errors { - println!("{:?}", error.with_source_code(source_text.to_string())); - } - return None; + for error in ret.errors { + println!("{:?}", error.with_source_code(source_text.to_string())); } - Some(ret) + ret.program } -fn codegen(ret: &ParserReturn<'_>, minify: bool) -> String { +fn codegen(program: &Program<'_>, minify: bool) -> String { Codegen::new() .with_options(if minify { CodegenOptions::minify() } else { CodegenOptions::default() }) - .build(&ret.program) + .build(program) .code } diff --git a/crates/oxc_codegen/src/gen.rs b/crates/oxc_codegen/src/gen.rs index 0c7107e4d426e..5c81b7b647d39 100644 --- a/crates/oxc_codegen/src/gen.rs +++ b/crates/oxc_codegen/src/gen.rs @@ -3488,7 +3488,8 @@ impl Gen for TSIndexSignature<'_> { p.print_str("["); for (index, parameter) in self.parameters.iter().enumerate() { if index != 0 { - p.print_str(" | "); + p.print_str(","); + p.print_soft_space(); } p.print_str(parameter.name.as_str()); p.print_colon(); diff --git a/crates/oxc_parser/src/cursor.rs b/crates/oxc_parser/src/cursor.rs index ec7b52aaa8784..57dd9f0fbda3e 100644 --- a/crates/oxc_parser/src/cursor.rs +++ b/crates/oxc_parser/src/cursor.rs @@ -373,8 +373,7 @@ impl<'a> ParserImpl<'a> { let mut list = self.ast.vec(); let mut first = true; loop { - let kind = self.cur_kind(); - if kind == close || self.has_fatal_error() { + if self.cur_kind() == close || self.has_fatal_error() { break; } if first { diff --git a/crates/oxc_parser/src/diagnostics.rs b/crates/oxc_parser/src/diagnostics.rs index 14718c9de8951..236cb4be1d982 100644 --- a/crates/oxc_parser/src/diagnostics.rs +++ b/crates/oxc_parser/src/diagnostics.rs @@ -504,7 +504,6 @@ pub fn import_arguments(span: Span) -> OxcDiagnostic { OxcDiagnostic::error("Dynamic imports can only accept a module specifier and an optional set of attributes as arguments").with_label(span) } -/// TS(2566) #[cold] pub fn rest_element_property_name(span: Span) -> OxcDiagnostic { ts_error("2566", "A rest element cannot have a property name.").with_label(span) @@ -520,15 +519,12 @@ pub fn import_requires_a_specifier(span: Span) -> OxcDiagnostic { OxcDiagnostic::error("import() requires a specifier.").with_label(span) } -// ================================= MODIFIERS ================================= - #[cold] pub fn modifier_cannot_be_used_here(modifier: &Modifier) -> OxcDiagnostic { OxcDiagnostic::error(format!("'{}' modifier cannot be used here.", modifier.kind)) .with_label(modifier.span) } -/// TS(1028) #[cold] pub fn accessibility_modifier_already_seen(modifier: &Modifier) -> OxcDiagnostic { ts_error("1028", "Accessibility modifier already seen.") @@ -536,7 +532,6 @@ pub fn accessibility_modifier_already_seen(modifier: &Modifier) -> OxcDiagnostic .with_help("Remove the duplicate modifier.") } -/// TS(1030) #[cold] pub fn modifier_already_seen(modifier: &Modifier) -> OxcDiagnostic { ts_error("1030", format!("{}' modifier already seen.", modifier.kind)) @@ -544,26 +539,22 @@ pub fn modifier_already_seen(modifier: &Modifier) -> OxcDiagnostic { .with_help("Remove the duplicate modifier.") } -/// TS(1273) #[cold] pub fn cannot_appear_on_a_type_parameter(modifier: &Modifier) -> OxcDiagnostic { ts_error("1273", format!("'{}' modifier cannot be used on a type parameter.", modifier.kind)) .with_label(modifier.span) } -/// TS(1090) pub fn cannot_appear_on_a_parameter(modifier: &Modifier) -> OxcDiagnostic { ts_error("1090", format!("'{}' modifier cannot appear on a parameter.", modifier.kind)) .with_label(modifier.span) } -/// TS(1071) pub fn cannot_appear_on_an_index_signature(modifier: &Modifier) -> OxcDiagnostic { ts_error("1071", format!("'{}' modifier cannot appear on an index signature.", modifier.kind)) .with_label(modifier.span) } -/// TS(1243) pub fn accessor_modifier(modifier: &Modifier) -> OxcDiagnostic { ts_error( "1243", @@ -572,43 +563,41 @@ pub fn accessor_modifier(modifier: &Modifier) -> OxcDiagnostic { .with_label(modifier.span) } -/// TS(1354) #[cold] pub fn readonly_in_array_or_tuple_type(span: Span) -> OxcDiagnostic { ts_error("1354", "'readonly' type modifier is only permitted on array and tuple literal types.") .with_label(span) } -/// TS(18010) #[cold] pub fn accessibility_modifier_on_private_property(modifier: &Modifier) -> OxcDiagnostic { ts_error("18010", "An accessibility modifier cannot be used with a private identifier.") .with_label(modifier.span) } -/// TS(2206) #[cold] pub fn type_modifier_on_named_type_import(span: Span) -> OxcDiagnostic { ts_error("2206", "The 'type' modifier cannot be used on a named import when 'import type' is used on its import statement.") .with_label(span) } -/// TS(2207) #[cold] pub fn type_modifier_on_named_type_export(span: Span) -> OxcDiagnostic { ts_error("2207", "The 'type' modifier cannot be used on a named export when 'export type' is used on its export statement.") .with_label(span) } -// ================================== TS ENUMS ================================= - -/// Computed property names are not allowed in enums.ts(1164) #[cold] pub fn computed_property_names_not_allowed_in_enums(span: Span) -> OxcDiagnostic { ts_error("1164", "Computed property names are not allowed in enums.").with_label(span) } -/// An enum member cannot have a numeric name.ts(2452) + #[cold] pub fn enum_member_cannot_have_numeric_name(span: Span) -> OxcDiagnostic { ts_error("2452", "An enum member cannot have a numeric name.").with_label(span) } + +#[cold] +pub fn index_signature_one_parameter(span: Span) -> OxcDiagnostic { + ts_error("1096", "An index signature must have exactly one parameter.").with_label(span) +} diff --git a/crates/oxc_parser/src/ts/types.rs b/crates/oxc_parser/src/ts/types.rs index 1282c32986880..8f3d014ecd8c3 100644 --- a/crates/oxc_parser/src/ts/types.rs +++ b/crates/oxc_parser/src/ts/types.rs @@ -1218,16 +1218,25 @@ impl<'a> ParserImpl<'a> { ModifierFlags::READONLY | ModifierFlags::STATIC, diagnostics::cannot_appear_on_an_index_signature, ); - self.bump(Kind::LBrack); - let parameters = self.ast.vec1(self.parse_ts_index_signature_name()); + self.expect(Kind::LBrack); + let params = self.parse_delimited_list( + Kind::RBrack, + Kind::Comma, + /* trailing_separator */ + false, // An index signature cannot have a trailing comma. + Self::parse_ts_index_signature_name, + ); self.expect(Kind::RBrack); + if params.len() != 1 { + self.error(diagnostics::index_signature_one_parameter(self.end_span(span))); + } let Some(type_annotation) = self.parse_ts_type_annotation() else { return self.unexpected(); }; self.parse_type_member_semicolon(); self.ast.ts_index_signature( self.end_span(span), - parameters, + params, type_annotation, modifiers.contains(ModifierKind::Readonly), modifiers.contains(ModifierKind::Static), @@ -1248,12 +1257,10 @@ impl<'a> ParserImpl<'a> { let span = self.start_span(); let name = self.parse_identifier_name().name; let type_annotation = self.parse_ts_type_annotation(); - - if type_annotation.is_none() { - return self.unexpected(); + if let Some(type_annotation) = type_annotation { + return self.ast.ts_index_signature_name(self.end_span(span), name, type_annotation); } - - self.ast.ts_index_signature_name(self.end_span(span), name, type_annotation.unwrap()) + self.unexpected() } pub(crate) fn parse_class_element_modifiers( diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 3a6176bec8527..3dd52cd45cfb5 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -12960,12 +12960,12 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private 3 │ } ╰──── - × Expected `]` but found `=` + × Expected `,` but found `=` ╭─[typescript/tests/cases/compiler/indexSignatureWithInitializer1.ts:2:14] 1 │ class C { 2 │ [a: number = 1]: number; · ┬ - · ╰── `]` expected + · ╰── `,` expected 3 │ } ╰────