Skip to content

Commit abd6463

Browse files
committed
fix(parser): Report errors for duplicate extends/implements clauses (TS1172/TS1173/TS1175)
1 parent 4c246fb commit abd6463

File tree

4 files changed

+108
-12
lines changed

4 files changed

+108
-12
lines changed

crates/oxc_parser/src/diagnostics.rs

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,24 @@ pub fn const_class_member(span: Span) -> OxcDiagnostic {
183183
.with_label(span)
184184
}
185185

186+
// 'extends' clause already seen. ts(1172)
187+
#[cold]
188+
pub fn extends_clause_already_seen(span: Span) -> OxcDiagnostic {
189+
ts_error("1172", "'extends' clause already seen").with_label(span)
190+
}
191+
192+
// 'extends' clause must precede 'implements' clause. ts(1173)
193+
#[cold]
194+
pub fn extends_clause_must_precede_implements(span: Span) -> OxcDiagnostic {
195+
ts_error("1173", "'extends' clause must precede 'implements' clause").with_label(span)
196+
}
197+
198+
// 'implements' clause already seen. ts(1175)
199+
#[cold]
200+
pub fn implements_clause_already_seen(span: Span) -> OxcDiagnostic {
201+
ts_error("1175", "'implements' clause already seen").with_label(span)
202+
}
203+
186204
#[cold]
187205
pub fn binding_rest_element_last(span: Span) -> OxcDiagnostic {
188206
OxcDiagnostic::error("A rest element must be last in a destructuring pattern").with_label(span)

crates/oxc_parser/src/js/class.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,23 @@ impl<'a> ParserImpl<'a> {
117117
loop {
118118
match self.cur_kind() {
119119
Kind::Extends => {
120+
if extends.is_some() {
121+
self.error(diagnostics::extends_clause_already_seen(
122+
self.cur_token().span(),
123+
));
124+
} else if implements.is_some() {
125+
self.error(diagnostics::extends_clause_must_precede_implements(
126+
self.cur_token().span(),
127+
));
128+
}
120129
extends = Some(self.parse_extends_clause()?);
121130
}
122131
Kind::Implements => {
132+
if implements.is_some() {
133+
self.error(diagnostics::implements_clause_already_seen(
134+
self.cur_token().span(),
135+
));
136+
}
123137
implements = Some(self.parse_ts_implements_clause()?);
124138
}
125139
_ => break,

tasks/coverage/snapshots/estree_typescript.snap

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2338,7 +2338,8 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmasc
23382338
Unexpected token
23392339
Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/IndexSignatures/parserIndexSignature9.ts
23402340
Unexpected token
2341-
Mismatch: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration1.ts
2341+
Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration1.ts
2342+
'extends' clause already seen
23422343
Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration6.ts
23432344
'export' modifier cannot be used here.
23442345
Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/MemberAccessorDeclarations/parserMemberAccessorDeclaration10.ts

tasks/coverage/snapshots/parser_typescript.snap

Lines changed: 74 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ commit: 15392346
33
parser_typescript Summary:
44
AST Parsed : 6522/6531 (99.86%)
55
Positive Passed: 6511/6531 (99.69%)
6-
Negative Passed: 1297/5754 (22.54%)
6+
Negative Passed: 1307/5754 (22.71%)
77
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ClassDeclaration24.ts
88
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment7.ts
99
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment8.ts
@@ -827,8 +827,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/extendGlobal
827827
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/extendNonClassSymbol2.ts
828828
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/extendPrivateConstructorClass.ts
829829
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/extendedInterfacesWithDuplicateTypeParameters.ts
830-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/extendsClauseAlreadySeen.ts
831-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/extendsClauseAlreadySeen2.ts
832830
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/externSyntax.ts
833831
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/externalModuleExportingGenericClass.ts
834832
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/externalModuleImmutableBindings.ts
@@ -997,10 +995,8 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/identityForS
997995
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ifElseWithStatements1.ts
998996
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ignoredJsxAttributes.tsx
999997
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/implementArrayInterface.ts
1000-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/implementClausePrecedingExtends.ts
1001998
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/implementGenericWithMismatchedTypes.ts
1002999
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/implementPublicPropertyAsPrivate.ts
1003-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/implementsClauseAlreadySeen.ts
10041000
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/implementsIncorrectlyNoAssertion.ts
10051001
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/implicitAnyAmbients.ts
10061002
Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/implicitAnyCastedValue.ts
@@ -3686,14 +3682,9 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec
36863682
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/CatchClauses/parserCatchClauseWithTypeAnnotation1.ts
36873683
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClass1.ts
36883684
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClass2.ts
3689-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration1.ts
36903685
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration12.ts
36913686
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration18.ts
3692-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration2.ts
36933687
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration24.ts
3694-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration3.ts
3695-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration4.ts
3696-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration5.ts
36973688
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration6.ts
36983689
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration7.ts
36993690
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/ComputedPropertyNames/parserES5ComputedPropertyName1.ts
@@ -3763,7 +3754,6 @@ Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ec
37633754
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/IndexMemberDeclarations/parserIndexMemberDeclaration5.ts
37643755
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/IndexSignatures/parserIndexSignature4.ts
37653756
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/IndexSignatures/parserIndexSignature5.ts
3766-
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration1.ts
37673757
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration2.ts
37683758
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration8.ts
37693759
Expect Syntax Error: tasks/coverage/typescript/tests/cases/conformance/parser/ecmascript5/MemberAccessorDeclarations/parserMemberAccessor1.ts
@@ -8125,6 +8115,22 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
81258115
5 │ }
81268116
╰────
81278117

8118+
× TS(1172): 'extends' clause already seen
8119+
╭─[typescript/tests/cases/compiler/extendsClauseAlreadySeen.ts:4:19]
8120+
3 │ }
8121+
4 │ class D extends C extends C {
8122+
· ───────
8123+
5 │ baz() { }
8124+
╰────
8125+
8126+
× TS(1172): 'extends' clause already seen
8127+
╭─[typescript/tests/cases/compiler/extendsClauseAlreadySeen2.ts:4:30]
8128+
3 │ }
8129+
4 │ class D<T> extends C<number> extends C<string> {
8130+
· ───────
8131+
5 │ baz() { }
8132+
╰────
8133+
81288134
× Expected a semicolon or an implicit semicolon after a statement, but found none
81298135
╭─[typescript/tests/cases/compiler/extendsUntypedModule.ts:3:5]
81308136
2 │
@@ -8420,6 +8426,21 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
84208426
17 │ }
84218427
╰────
84228428

8429+
× TS(1173): 'extends' clause must precede 'implements' clause
8430+
╭─[typescript/tests/cases/compiler/implementClausePrecedingExtends.ts:2:22]
8431+
1 │ class C { foo: number }
8432+
2 │ class D implements C extends C { }
8433+
· ───────
8434+
╰────
8435+
8436+
× TS(1175): 'implements' clause already seen
8437+
╭─[typescript/tests/cases/compiler/implementsClauseAlreadySeen.ts:4:22]
8438+
3 │ }
8439+
4 │ class D implements C implements C {
8440+
· ──────────
8441+
5 │ baz() { }
8442+
╰────
8443+
84238444
× Identifier `x` has already been declared
84248445
╭─[typescript/tests/cases/compiler/importAndVariableDeclarationConflict3.ts:5:8]
84258446
4 │
@@ -21037,6 +21058,13 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2103721058
· ╰── `,` expected
2103821059
╰────
2103921060

21061+
× TS(1172): 'extends' clause already seen
21062+
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration1.ts:1:19]
21063+
1 │ class C extends A extends B {
21064+
· ───────
21065+
2 │ }
21066+
╰────
21067+
2104021068
× Constructor implementation is missing.
2104121069
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration10.ts:2:4]
2104221070
1 │ class C {
@@ -21093,6 +21121,13 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2109321121
3 │ constructor() { }
2109421122
╰────
2109521123

21124+
× TS(1175): 'implements' clause already seen
21125+
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration2.ts:1:22]
21126+
1 │ class C implements A implements B {
21127+
· ──────────
21128+
2 │ }
21129+
╰────
21130+
2109621131
× Function implementation is missing or not immediately following the declaration.
2109721132
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration21.ts:2:5]
2109821133
1 │ class C {
@@ -21125,6 +21160,27 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2112521160
8 │ }
2112621161
╰────
2112721162

21163+
× TS(1173): 'extends' clause must precede 'implements' clause
21164+
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration3.ts:1:22]
21165+
1 │ class C implements A extends B {
21166+
· ───────
21167+
2 │ }
21168+
╰────
21169+
21170+
× TS(1172): 'extends' clause already seen
21171+
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration4.ts:1:32]
21172+
1 │ class C extends A implements B extends C {
21173+
· ───────
21174+
2 │ }
21175+
╰────
21176+
21177+
× TS(1175): 'implements' clause already seen
21178+
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration5.ts:1:32]
21179+
1 │ class C extends A implements B implements C {
21180+
· ──────────
21181+
2 │ }
21182+
╰────
21183+
2112821184
× Constructor implementation is missing.
2112921185
╭─[typescript/tests/cases/conformance/parser/ecmascript5/ClassDeclarations/parserClassDeclaration8.ts:2:3]
2113021186
1 │ class C {
@@ -22058,6 +22114,13 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2205822114
3 │ }
2205922115
╰────
2206022116

22117+
× TS(1172): 'extends' clause already seen
22118+
╭─[typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration1.ts:1:23]
22119+
1 │ interface I extends A extends B {
22120+
· ───────
22121+
2 │ }
22122+
╰────
22123+
2206122124
× 'public' modifier cannot be used here.
2206222125
╭─[typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration3.ts:1:1]
2206322126
1 │ public interface I {

0 commit comments

Comments
 (0)