Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions crates/oxc_parser/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -343,6 +343,18 @@ pub fn const_class_member(span: Span) -> OxcDiagnostic {
.with_label(span)
}

// A required element cannot follow an optional element. ts(1257)
#[cold]
pub fn required_element_cannot_follow_optional_element(
span: Span,
optional_span: Span,
) -> OxcDiagnostic {
ts_error("1257", "A required element cannot follow an optional element.").with_labels([
span.label("Required element here"),
optional_span.label("Optional element seen here"),
])
}

/// A rest element cannot follow another rest element. ts(1265)
#[cold]
pub fn rest_element_cannot_follow_another_rest_element(
Expand Down
28 changes: 21 additions & 7 deletions crates/oxc_parser/src/ts/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -899,7 +899,8 @@ impl<'a> ParserImpl<'a> {
let opening_span = self.cur_token().span();
self.expect(Kind::LBrack);

let mut seen_type_span: Option<Span> = None;
let mut seen_rest_span: Option<Span> = None;
let mut seen_optional_span: Option<Span> = None;
let (elements, _) =
self.parse_delimited_list(Kind::RBrack, Kind::Comma, opening_span, |me| {
let tuple = me.parse_tuple_element();
Expand All @@ -918,23 +919,36 @@ impl<'a> ParserImpl<'a> {
_ => false,
}
{
if let Some(seen_span) = seen_type_span {
if let Some(seen_span) = seen_rest_span {
me.error(diagnostics::rest_element_cannot_follow_another_rest_element(
seen_span,
tuple.span(),
));
}
seen_type_span = Some(tuple.span());
seen_rest_span = Some(tuple.span());
}

if let Some(seen_rest_span) = seen_type_span
&& matches!(tuple, TSTupleElement::TSOptionalType(_))
if !matches!(
tuple,
TSTupleElement::TSOptionalType(_) | TSTupleElement::TSRestType(_)
) && let Some(seen_optional_span) = seen_optional_span
{
me.error(diagnostics::optional_element_cannot_follow_rest_element(
me.error(diagnostics::required_element_cannot_follow_optional_element(
tuple.span(),
seen_rest_span,
seen_optional_span,
));
}

if matches!(tuple, TSTupleElement::TSOptionalType(_)) {
if let Some(seen_rest_span) = seen_rest_span {
me.error(diagnostics::optional_element_cannot_follow_rest_element(
tuple.span(),
seen_rest_span,
));
}
seen_optional_span = Some(tuple.span());
}

tuple
});
self.expect(Kind::RBrack);
Expand Down
12 changes: 9 additions & 3 deletions tasks/coverage/snapshots/parser_babel.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ commit: fc58af40
parser_babel Summary:
AST Parsed : 2224/2230 (99.73%)
Positive Passed: 2207/2230 (98.97%)
Negative Passed: 1649/1697 (97.17%)
Negative Passed: 1650/1697 (97.23%)
Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/es2026/explicit-resource-management/invalid-for-using-of-no-initializer/input.js

Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/estree/class-private-property/typescript-invalid-abstract/input.ts
Expand Down Expand Up @@ -96,8 +96,6 @@ Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/ty

Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/invalid-import-type-options-with-spread-element/input.ts

Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/tuple-optional-invalid/input.ts

Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/types/tuple-required-after-labeled-optional/input.ts

Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/opts/allowNewTargetOutsideFunction-true/input.js
Expand Down Expand Up @@ -14373,6 +14371,14 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
· ──
╰────

× TS(1257): A required element cannot follow an optional element.
╭─[babel/packages/babel-parser/test/fixtures/typescript/types/tuple-optional-invalid/input.ts:1:9]
1 │ let x: [string?, number]
· ───┬─── ───┬──
· │ ╰── Required element here
· ╰── Optional element seen here
╰────

× TS(1273): 'public' modifier cannot be used on a type parameter.
╭─[babel/packages/babel-parser/test/fixtures/typescript/types/variance-annotations/input.ts:95:10]
94 │
Expand Down
36 changes: 31 additions & 5 deletions tasks/coverage/snapshots/parser_typescript.snap
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ commit: f5ccf434
parser_typescript Summary:
AST Parsed : 9844/9845 (99.99%)
Positive Passed: 9836/9845 (99.91%)
Negative Passed: 1496/2549 (58.69%)
Negative Passed: 1498/2549 (58.77%)
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration3.ts

Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/FunctionDeclaration4.ts
Expand Down Expand Up @@ -2070,10 +2070,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/thi

Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/tuple/contextualTypeTupleEnd.ts

Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/tuple/optionalTupleElements1.ts

Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/tuple/restTupleElements1.ts

Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/tuple/unionsOfTupleTypes1.ts

Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/types/typeAliases/intrinsicKeyword.ts
Expand Down Expand Up @@ -26472,6 +26468,16 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/statements/Va
17 │
╰────

× TS(1257): A required element cannot follow an optional element.
╭─[typescript/tests/cases/conformance/types/tuple/optionalTupleElements1.ts:11:20]
10 │
11 │ type T5 = [number, string?, boolean]; // Error
· ───┬─── ───┬───
· │ ╰── Required element here
· ╰── Optional element seen here
12 │
╰────

× TS(1354): 'readonly' type modifier is only permitted on array and tuple literal types.
╭─[typescript/tests/cases/conformance/types/tuple/readonlyArraysAndTuples.ts:9:12]
8 │
Expand Down Expand Up @@ -26504,6 +26510,16 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/statements/Va
13 │
╰────

× TS(1257): A required element cannot follow an optional element.
╭─[typescript/tests/cases/conformance/types/tuple/restTupleElements1.ts:3:13]
2 │ type T01 = [string, string?];
3 │ type T02 = [string?, string]; // Error
· ───┬─── ───┬──
· │ ╰── Required element here
· ╰── Optional element seen here
4 │ type T03 = [...string[]];
╰────

× TS(1265): A rest element cannot follow another rest element.
╭─[typescript/tests/cases/conformance/types/tuple/variadicTuples2.ts:7:21]
6 │
Expand All @@ -26524,6 +26540,16 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/statements/Va
9 │ type V12 = [number, string?, boolean]; // Error
╰────

× TS(1257): A required element cannot follow an optional element.
╭─[typescript/tests/cases/conformance/types/tuple/variadicTuples2.ts:9:21]
8 │ type V11 = [number, ...string[], boolean?]; // Error
9 │ type V12 = [number, string?, boolean]; // Error
· ───┬─── ───┬───
· │ ╰── Required element here
· ╰── Optional element seen here
10 │
╰────

× TS(1265): A rest element cannot follow another rest element.
╭─[typescript/tests/cases/conformance/types/tuple/variadicTuples2.ts:11:13]
10 │
Expand Down
Loading