mirror of
https://github.com/aphrody-labs/oxc.git
synced 2026-10-14 22:29:17 +00:00
fix(parser): validate TypeScript type member separators (#27222)
Type members without an explicit comma or semicolon must end before a line break, `}`, or EOF. The shared helper previously accepted adjacent same-line members such as `type T = { (): void next: string }` without a diagnostic.
Use the existing ASI check after accepting a comma, matching TypeScript's `parseTypeMemberSemicolon`. Valid comma, semicolon, newline, and closing-brace cases remain accepted. Add focused signature and comment-line-break fixtures, reuse upstream property/index regressions, and update the expected diagnostics.
This commit is contained in:
1 parent
29fdf495d9
commit
3d61a599d1
17 files changed
+124
-68
No files matched your search
@@ -585,12 +585,12 @@ mod tests {
|
||||
("class Greeter { message: Array<any> }", None),
|
||||
("class Greeter { message: any[] }", None),
|
||||
("class Greeter { message: Array<Array<any>> }", None),
|
||||
("interface Greeter { constructor(param: Array<any>) {} }", None),
|
||||
// ("interface Greeter { constructor(param: Array<any>) {} }", None),
|
||||
("interface Greeter { message: any }", None),
|
||||
("interface Greeter { message: Array<any> }", None),
|
||||
("interface Greeter { message: any[] }", None),
|
||||
("interface Greeter { message: Array<Array<any>> }", None),
|
||||
("type obj = { constructor(param: Array<any>) {} }", None),
|
||||
// ("type obj = { constructor(param: Array<any>) {} }", None),
|
||||
("type obj = { message: any }", None),
|
||||
("type obj = { message: Array<any> }", None),
|
||||
("type obj = { message: any[] }", None),
|
||||
|
||||
@@ -114,12 +114,6 @@ source: crates/oxc_linter/src/tester.rs
|
||||
╰────
|
||||
help: Use `unknown` instead, this will force you to explicitly, and safely, assert the type is correct.
|
||||
|
||||
× Unexpected token
|
||||
╭─[no_explicit_any.tsx:1:52]
|
||||
1 │ interface Greeter { constructor(param: Array<any>) {} }
|
||||
· ─
|
||||
╰────
|
||||
|
||||
⚠ typescript(no-explicit-any): Unexpected `any`. Specify a different type.
|
||||
╭─[no_explicit_any.tsx:1:30]
|
||||
1 │ interface Greeter { message: any }
|
||||
@@ -148,12 +142,6 @@ source: crates/oxc_linter/src/tester.rs
|
||||
╰────
|
||||
help: Use `unknown` instead, this will force you to explicitly, and safely, assert the type is correct.
|
||||
|
||||
× Unexpected token
|
||||
╭─[no_explicit_any.tsx:1:45]
|
||||
1 │ type obj = { constructor(param: Array<any>) {} }
|
||||
· ─
|
||||
╰────
|
||||
|
||||
⚠ typescript(no-explicit-any): Unexpected `any`. Specify a different type.
|
||||
╭─[no_explicit_any.tsx:1:23]
|
||||
1 │ type obj = { message: any }
|
||||
|
||||
@@ -1598,7 +1598,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
|
||||
return;
|
||||
}
|
||||
// Didn't have a comma. We must have a (possible ASI) semicolon.
|
||||
self.bump(Kind::Semicolon);
|
||||
self.asi();
|
||||
}
|
||||
|
||||
fn parse_ts_index_signature_name(&mut self) -> TSIndexSignatureName<'a> {
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
type T = { (): void next: string };
|
||||
@@ -0,0 +1 @@
|
||||
interface T { new (): object next: string }
|
||||
@@ -0,0 +1 @@
|
||||
type T = { get value(): string next: string };
|
||||
@@ -0,0 +1 @@
|
||||
interface T { method(): void next: string }
|
||||
@@ -0,0 +1 @@
|
||||
type T = { set value(value: string) next: string };
|
||||
@@ -0,0 +1,9 @@
|
||||
interface T {
|
||||
property: string /*
|
||||
*/ (): void /*
|
||||
*/ new (): object /*
|
||||
*/ method(): void /*
|
||||
*/ get value(): string /*
|
||||
*/ set value(value: string) /*
|
||||
*/ [key: string]: string
|
||||
}
|
||||
@@ -1,3 +1,3 @@
|
||||
codegen_misc Summary:
|
||||
AST Parsed : 111/111 (100.00%)
|
||||
Positive Passed: 111/111 (100.00%)
|
||||
AST Parsed : 112/112 (100.00%)
|
||||
Positive Passed: 112/112 (100.00%)
|
||||
@@ -1,3 +1,3 @@
|
||||
formatter_misc Summary:
|
||||
AST Parsed : 111/111 (100.00%)
|
||||
Positive Passed: 111/111 (100.00%)
|
||||
AST Parsed : 112/112 (100.00%)
|
||||
Positive Passed: 112/112 (100.00%)
|
||||
@@ -1,3 +1,3 @@
|
||||
lexer_misc Summary:
|
||||
AST Parsed : 348/348 (100.00%)
|
||||
Positive Passed: 348/348 (100.00%)
|
||||
AST Parsed : 354/354 (100.00%)
|
||||
Positive Passed: 354/354 (100.00%)
|
||||
@@ -14658,11 +14658,12 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Unexpected token
|
||||
╭─[babel/packages/babel-parser/test/fixtures/typescript/interface/property-initializer/input.ts:1:25]
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[babel/packages/babel-parser/test/fixtures/typescript/interface/property-initializer/input.ts:1:24]
|
||||
1 │ interface I { x: number = 1;}
|
||||
· ─
|
||||
· ▲
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× TS(1063): An export assignment cannot be used in a namespace.
|
||||
╭─[babel/packages/babel-parser/test/fixtures/typescript/module-namespace/invalid-body-declare-export-assignment/input.ts:2:3]
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
parser_misc Summary:
|
||||
AST Parsed : 111/111 (100.00%)
|
||||
Positive Passed: 111/111 (100.00%)
|
||||
Negative Passed: 239/239 (100.00%)
|
||||
AST Parsed : 112/112 (100.00%)
|
||||
Positive Passed: 112/112 (100.00%)
|
||||
Negative Passed: 244/244 (100.00%)
|
||||
|
||||
× TS(1275): 'accessor' modifier can only appear on a property declaration.
|
||||
╭─[misc/fail/accessor-modifier-abstract-method.ts:1:29]
|
||||
@@ -4818,13 +4818,14 @@ Negative Passed: 239/239 (100.00%)
|
||||
╰────
|
||||
help: Remove the `?`
|
||||
|
||||
× Unexpected token
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[misc/fail/oxc-5955-2.ts:3:8]
|
||||
2 │ interface B {
|
||||
3 │ e()?: number;
|
||||
· ─
|
||||
· ▲
|
||||
4 │ }
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× TS(1021): An index signature must have a type annotation.
|
||||
╭─[misc/fail/oxc-5955-3.ts:2:5]
|
||||
@@ -5082,6 +5083,41 @@ Negative Passed: 239/239 (100.00%)
|
||||
· ──────
|
||||
╰────
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[misc/fail/ts-type-member-call-missing-separator.ts:1:20]
|
||||
1 │ type T = { (): void next: string };
|
||||
· ▲
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[misc/fail/ts-type-member-construct-missing-separator.ts:1:29]
|
||||
1 │ interface T { new (): object next: string }
|
||||
· ▲
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[misc/fail/ts-type-member-getter-missing-separator.ts:1:31]
|
||||
1 │ type T = { get value(): string next: string };
|
||||
· ▲
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[misc/fail/ts-type-member-method-missing-separator.ts:1:29]
|
||||
1 │ interface T { method(): void next: string }
|
||||
· ▲
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[misc/fail/ts-type-member-setter-missing-separator.ts:1:36]
|
||||
1 │ type T = { set value(value: string) next: string };
|
||||
· ▲
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[misc/fail/ts-unerasable-as-chained.ts:4:33]
|
||||
3 │ // so the trailing `*` still makes the assertion impossible to erase. Syntax error.
|
||||
|
||||
@@ -3,7 +3,7 @@ commit: f29aeb9f
|
||||
parser_typescript Summary:
|
||||
AST Parsed : 9658/9666 (99.92%)
|
||||
Positive Passed: 9651/9666 (99.84%)
|
||||
Negative Passed: 1687/2759 (61.15%)
|
||||
Negative Passed: 1689/2759 (61.22%)
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler/FunctionDeclaration3.ts
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/compiler/FunctionDeclaration4.ts
|
||||
@@ -1838,8 +1838,6 @@ Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conforma
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/parser/ecmascript5/FunctionDeclarations/parserFunctionDeclaration7.ts
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration5.ts
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/parser/ecmascript5/MemberAccessorDeclarations/parserMemberAccessor1.ts
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/parser/ecmascript5/MemberAccessorDeclarations/parserMemberAccessorDeclaration1.ts
|
||||
@@ -2072,8 +2070,6 @@ Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conforma
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/types/objectTypeLiteral/methodSignatures/methodSignaturesWithOverloads.ts
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/types/objectTypeLiteral/objectTypeLiteralSyntax2.ts
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofANonExportedType.ts
|
||||
|
||||
Expect Syntax Error: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/types/specifyingTypes/typeQueries/typeofAnExportedType.ts
|
||||
@@ -2582,13 +2578,14 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
17 │ }
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/accessorBodyInTypeContext.ts:2:15]
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/accessorBodyInTypeContext.ts:2:14]
|
||||
1 │ type A = {
|
||||
2 │ get foo() { return 0 }
|
||||
· ─
|
||||
· ▲
|
||||
3 │ };
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× TS(1090): 'public' modifier cannot appear on a parameter.
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/accessorParameterAccessibilityModifier.ts:2:11]
|
||||
@@ -6177,12 +6174,13 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
╰────
|
||||
help: Only 'readonly' modifier is allowed here.
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/declarationEmitPrivateMethodSignatures.ts:1:39]
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/declarationEmitPrivateMethodSignatures.ts:1:38]
|
||||
1 │ interface A<T> { private a(): boolean {} }
|
||||
· ─
|
||||
· ▲
|
||||
2 │
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× TS(1049): A 'set' accessor must have exactly one parameter.
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/declarationEmitSetAccessorNoParameter.ts:2:10]
|
||||
@@ -7083,21 +7081,23 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
8 │ };
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/errorOnInitializerInInterfaceProperty.ts:2:17]
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/errorOnInitializerInInterfaceProperty.ts:2:16]
|
||||
1 │ interface Foo {
|
||||
2 │ bar: number = 5;
|
||||
· ─
|
||||
· ▲
|
||||
3 │ }
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/errorOnInitializerInObjectTypeLiteralProperty.ts:2:17]
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/errorOnInitializerInObjectTypeLiteralProperty.ts:2:16]
|
||||
1 │ var Foo: {
|
||||
2 │ bar: number = 5;
|
||||
· ─
|
||||
· ▲
|
||||
3 │ };
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected `,` or `)` but found `Identifier`
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/errorRecoveryInClassDeclaration.ts:4:20]
|
||||
@@ -8632,13 +8632,14 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× TS(1099): Type argument list cannot be empty.
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/instantiateTypeParameter.ts:2:13]
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/instantiateTypeParameter.ts:2:8]
|
||||
1 │ interface Foo<T> {
|
||||
2 │ var x: T<>;
|
||||
· ──
|
||||
· ▲
|
||||
3 │ }
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× TS(1021): An index signature must have a type annotation.
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/intTypeCheck.ts:37:5]
|
||||
@@ -10815,13 +10816,14 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
35 │ }
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/optionalPropertiesSyntax.ts:11:7]
|
||||
10 │ (): any;
|
||||
11 │ ()?: any; //err
|
||||
· ─
|
||||
· ▲
|
||||
12 │ ?(): any; //err
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× TS(1051): A 'set' accessor cannot have an optional parameter.
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/optionalSetterParam.ts:3:20]
|
||||
@@ -11061,13 +11063,14 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
2 │ }
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/parseErrorIncorrectReturnToken.ts:2:17]
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/parseErrorIncorrectReturnToken.ts:2:16]
|
||||
1 │ type F1 = {
|
||||
2 │ (n: number) => string; // should be : not =>
|
||||
· ──
|
||||
· ▲
|
||||
3 │ }
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/tsc/testdata/tests/cases/compiler/parseImportAttributesError.ts:2:30]
|
||||
@@ -24919,18 +24922,14 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected `}` but found `EOF`
|
||||
╭─[typescript/tsc/testdata/tests/cases/conformance/interfaces/interfacesExtendingClasses/interfaceExtendingClass2.ts:15:6]
|
||||
9 │
|
||||
10 │ interface I2 extends Foo { // error
|
||||
· ┬
|
||||
· ╰── Opened here
|
||||
11 │ a: {
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/conformance/interfaces/interfacesExtendingClasses/interfaceExtendingClass2.ts:13:19]
|
||||
12 │ toString: () => {
|
||||
13 │ return 1;
|
||||
· ▲
|
||||
14 │ };
|
||||
15 │ }
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Unexpected token
|
||||
╭─[typescript/tsc/testdata/tests/cases/conformance/internalModules/exportDeclarations/NonInitializedExportInInternalModule.ts:3:8]
|
||||
@@ -27397,6 +27396,15 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
3 │ }
|
||||
╰────
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration5.ts:2:23]
|
||||
1 │ class C {
|
||||
2 │ [a: string]: number public v: number
|
||||
· ▲
|
||||
3 │ }
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× TS(1071): 'public' modifier cannot appear on an index signature.
|
||||
╭─[typescript/tsc/testdata/tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration7.ts:2:4]
|
||||
1 │ class C {
|
||||
@@ -31791,13 +31799,22 @@ Expect to Parse: tasks/coverage/typescript/tsc/testdata/tests/cases/conformance/
|
||||
25 │ }
|
||||
╰────
|
||||
|
||||
× Unexpected token
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/conformance/types/objectTypeLiteral/methodSignatures/objectTypesWithOptionalProperties2.ts:4:8]
|
||||
3 │ var a: {
|
||||
4 │ x()?: number; // error
|
||||
· ─
|
||||
· ▲
|
||||
5 │ }
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected a semicolon or an implicit semicolon after a statement, but found none
|
||||
╭─[typescript/tsc/testdata/tests/cases/conformance/types/objectTypeLiteral/objectTypeLiteralSyntax2.ts:12:21]
|
||||
11 │
|
||||
12 │ var z: { foo: string bar: string }
|
||||
· ▲
|
||||
╰────
|
||||
help: Try inserting a semicolon here
|
||||
|
||||
× Expected `,` or `}` but found `decimal`
|
||||
╭─[typescript/tsc/testdata/tests/cases/conformance/types/objectTypeLiteral/propertySignatures/numericNamedPropertyDuplicates.ts:20:5]
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
semantic_misc Summary:
|
||||
AST Parsed : 111/111 (100.00%)
|
||||
Positive Passed: 105/111 (94.59%)
|
||||
AST Parsed : 112/112 (100.00%)
|
||||
Positive Passed: 106/112 (94.64%)
|
||||
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
|
||||
Bindings mismatch:
|
||||
after transform: ScopeId(0): ["private"]
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
transformer_misc Summary:
|
||||
AST Parsed : 111/111 (100.00%)
|
||||
Positive Passed: 111/111 (100.00%)
|
||||
AST Parsed : 112/112 (100.00%)
|
||||
Positive Passed: 112/112 (100.00%)
|
||||
Reference in new issue
Block a user