Skip to content

Commit b518f00

Browse files
committed
Fix parser panic on malformed tuple literals
When parse_expression failed to consume any tokens during tuple element collection (e.g. for input `(,{`), the forward-progress assertion aborted the parser. Break out of the loop instead so the diagnostic is reported normally.
1 parent d3f0dbb commit b518f00

File tree

3 files changed

+59
-4
lines changed

3 files changed

+59
-4
lines changed

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Added a warning for unnecessary `let` bindings where a variable is
1111
bound and immediately returned (e.g. `let x = foo() x` can be
1212
simplified to `foo()`), with an autofix.
1313

14+
## Parsing
15+
16+
Fixed a parser panic on malformed tuple literals such as `(,{` where
17+
the parser failed to make forward progress after recovering from an
18+
invalid element.
19+
1420
## Commands
1521

1622
Added :load to evaluate all definitions in a file and switch to that

src/parser.rs

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,12 @@ fn parse_tuple_literal_or_parentheses(
266266

267267
let start_idx = tokens.idx;
268268
exprs.push(parse_expression(tokens, id_gen, diagnostics));
269-
assert!(
270-
tokens.idx > start_idx,
271-
"The parser should always make forward progress."
272-
);
269+
if tokens.idx == start_idx {
270+
// `parse_expression` could not consume any tokens and
271+
// has already emitted a diagnostic. Stop collecting
272+
// tuple elements to avoid an infinite loop.
273+
break;
274+
}
273275
}
274276

275277
let close_paren = require_token(tokens, diagnostics, ")");
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
(,{
2+
3+
// args: dump-ast
4+
// expected stdout:
5+
// TupleLiteral(
6+
// [
7+
// Expression {
8+
// position: Position { ... },
9+
// expr_: Invalid,
10+
// value_is_used: true,
11+
// id: SyntaxId(0),
12+
// },
13+
// Expression {
14+
// position: Position { ... },
15+
// expr_: Invalid,
16+
// value_is_used: true,
17+
// id: SyntaxId(1),
18+
// },
19+
// ],
20+
// )
21+
// Block(
22+
// Block {
23+
// open_brace: Position { ... },
24+
// exprs: [],
25+
// close_brace: Position { ... },
26+
// },
27+
// )
28+
29+
// expected stderr:
30+
// Error: Parse error: Expected an expression after this.
31+
// --| src/test_files/parser/tuple_no_progress.gdn:1:1
32+
// 1| (,{
33+
// | ^
34+
// 2|
35+
// 3| // args: dump-ast
36+
// Error: Parse error: Expected an expression after this.
37+
// --| src/test_files/parser/tuple_no_progress.gdn:1:2
38+
// 1| (,{
39+
// 2| ^
40+
// 3| // args: dump-ast
41+
// Error: Parse error: Expected `)` after this.
42+
// --| src/test_files/parser/tuple_no_progress.gdn:1:2
43+
// 1| (,{
44+
// 2| ^
45+
// 3| // args: dump-ast
46+
// Parse error (incomplete input): Expected `}`, but got EOF.
47+

0 commit comments

Comments
 (0)