Skip to content

Commit 6c9c580

Browse files
committed
fix(parser): panic when parsing interface with missing implements (#11898)
1 parent 404cb14 commit 6c9c580

File tree

6 files changed

+48
-18
lines changed

6 files changed

+48
-18
lines changed

crates/oxc_parser/src/js/class.rs

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ impl<'a> ParserImpl<'a> {
107107
type_parameters,
108108
super_class,
109109
super_type_parameters,
110-
implements,
110+
implements.map_or_else(|| self.ast.vec(), |(_, implements)| implements),
111111
body,
112112
modifiers.contains_abstract(),
113113
modifiers.contains_declare(),
@@ -116,9 +116,9 @@ impl<'a> ParserImpl<'a> {
116116

117117
pub(crate) fn parse_heritage_clause(
118118
&mut self,
119-
) -> (Option<Extends<'a>>, Vec<'a, TSClassImplements<'a>>) {
119+
) -> (Option<Extends<'a>>, Option<(Span, Vec<'a, TSClassImplements<'a>>)>) {
120120
let mut extends = None;
121-
let mut implements = self.ast.vec();
121+
let mut implements: Option<(Span, Vec<'a, TSClassImplements<'a>>)> = None;
122122

123123
loop {
124124
match self.cur_kind() {
@@ -127,20 +127,28 @@ impl<'a> ParserImpl<'a> {
127127
self.error(diagnostics::extends_clause_already_seen(
128128
self.cur_token().span(),
129129
));
130-
} else if !implements.is_empty() {
130+
} else if implements.is_some() {
131131
self.error(diagnostics::extends_clause_must_precede_implements(
132132
self.cur_token().span(),
133133
));
134134
}
135135
extends = Some(self.parse_extends_clause());
136136
}
137137
Kind::Implements => {
138-
if !implements.is_empty() {
138+
if implements.is_some() {
139139
self.error(diagnostics::implements_clause_already_seen(
140140
self.cur_token().span(),
141141
));
142142
}
143-
implements.extend(self.parse_ts_implements_clause());
143+
let implements_kw_span = self.cur_token().span();
144+
if let Some((_, implements)) = implements.as_mut() {
145+
implements.extend(self.parse_ts_implements_clause());
146+
} else {
147+
implements = Some((
148+
implements_kw_span,
149+
self.ast.vec_from_iter(self.parse_ts_implements_clause()),
150+
));
151+
}
144152
}
145153
_ => break,
146154
}

crates/oxc_parser/src/ts/statement.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -199,11 +199,8 @@ impl<'a> ParserImpl<'a> {
199199
ModifierFlags::DECLARE,
200200
diagnostics::modifier_cannot_be_used_here,
201201
);
202-
if !implements.is_empty() {
203-
self.error(diagnostics::interface_implements(Span::new(
204-
implements.first().unwrap().span.start,
205-
implements.last().unwrap().span.end,
206-
)));
202+
if let Some((implements_kw_span, _)) = implements {
203+
self.error(diagnostics::interface_implements(implements_kw_span));
207204
}
208205
for extend in &extends {
209206
if !extend.expression.is_entity_name_expression() {
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface i<>implements
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
interface i<>implements K {}

tasks/coverage/snapshots/parser_misc.snap

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
parser_misc Summary:
22
AST Parsed : 43/43 (100.00%)
33
Positive Passed: 43/43 (100.00%)
4-
Negative Passed: 45/45 (100.00%)
4+
Negative Passed: 47/47 (100.00%)
55

66
× Identifier `b` has already been declared
77
╭─[misc/fail/oxc-10159.js:1:22]
@@ -118,6 +118,29 @@ Negative Passed: 45/45 (100.00%)
118118
· ╰── `{` expected
119119
╰────
120120
121+
× TS(1098): Type parameter list cannot be empty.
122+
╭─[misc/fail/oxc-11789-1.ts:1:12]
123+
1 │ interface i<>implements
124+
· ──
125+
╰────
126+
127+
× Unexpected token
128+
╭─[misc/fail/oxc-11789-1.ts:1:25]
129+
1 │ interface i<>implements
130+
╰────
131+
132+
× TS(1098): Type parameter list cannot be empty.
133+
╭─[misc/fail/oxc-11789-2.ts:1:12]
134+
1 │ interface i<>implements K {}
135+
· ──
136+
╰────
137+
138+
× TS(1176): Interface declaration cannot have 'implements' clause.
139+
╭─[misc/fail/oxc-11789-2.ts:1:14]
140+
1 │ interface i<>implements K {}
141+
· ──────────
142+
╰────
143+
121144
× Unexpected token
122145
╭─[misc/fail/oxc-169.js:2:1]
123146
11<(V=82<<t-j0<(V=$<LBI<(V=ut<I<(V=$<LBI<(V=uIV=82<<t-j0<(V=$<LBI<(V=ut<I<(V=$<LBI<(V<II>

tasks/coverage/snapshots/parser_typescript.snap

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13180,10 +13180,10 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
1318013180
help: Try insert a semicolon here
1318113181

1318213182
× TS(1176): Interface declaration cannot have 'implements' clause.
13183-
╭─[typescript/tests/cases/compiler/interfaceWithImplements1.ts:3:27]
13183+
╭─[typescript/tests/cases/compiler/interfaceWithImplements1.ts:3:16]
1318413184
2 │
1318513185
3 │ interface IBar implements IFoo {
13186-
· ────
13186+
· ──────────
1318713187
4 │ }
1318813188
╰────
1318913189

@@ -25475,10 +25475,10 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2547525475
╰────
2547625476

2547725477
× TS(1176): Interface declaration cannot have 'implements' clause.
25478-
╭─[typescript/tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatInheritsFromItself.ts:10:26]
25478+
╭─[typescript/tests/cases/conformance/interfaces/interfaceDeclarations/interfaceThatInheritsFromItself.ts:10:15]
2547925479
9 │
2548025480
10 │ interface Bar implements Bar { // error
25481-
· ───
25481+
· ──────────
2548225482
11 │ }
2548325483
╰────
2548425484

@@ -27522,9 +27522,9 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/conformance/salsa/private
2752227522
╰────
2752327523

2752427524
× TS(1176): Interface declaration cannot have 'implements' clause.
27525-
╭─[typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration2.ts:1:24]
27525+
╭─[typescript/tests/cases/conformance/parser/ecmascript5/InterfaceDeclarations/parserInterfaceDeclaration2.ts:1:13]
2752627526
1 │ interface I implements A {
27527-
·
27527+
· ─────────
2752827528
2 │ }
2752927529
╰────
2753027530

0 commit comments

Comments
 (0)