Skip to content

Commit 31c794f

Browse files
fix(parser): never repeat a rejected TypeScript speculation (#199)
1 parent 02d716f commit 31c794f

3 files changed

Lines changed: 15 additions & 3 deletions

File tree

‎src/parser/parser.zig‎

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,9 @@ pub const Parser = struct {
105105
ts_context: TsContext = .{},
106106
state: ParserState = .{},
107107

108+
// token offsets whose speculation failed, so a rewinding ancestor never re-parses them
109+
ts_rejected_speculations: std.AutoHashMapUnmanaged(u32, void) = .empty,
110+
108111
pub fn init(child_allocator: std.mem.Allocator, source: []const u8, options: Options) Parser {
109112
var b = ast.Tree.init(child_allocator, source);
110113
b.source_type = options.source_type;

‎src/parser/syntax/ts/arrows.zig‎

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,10 +93,14 @@ pub fn parseArrow(parser: *Parser, is_async: bool, arrow_start: u32) Error!?ast.
9393

9494
// rewind on failure so jsx or a `<T>` assertion can win
9595
pub fn tryParseArrow(parser: *Parser, is_async: bool, arrow_start: u32) Error!?ast.NodeIndex {
96+
const head = parser.current_token.span.start;
97+
if (parser.ts_rejected_speculations.contains(head)) return null;
98+
9699
const cp = parser.checkpoint();
97100

98101
const arrow = (try parseArrow(parser, is_async, arrow_start)) orelse {
99102
parser.rewind(cp);
103+
try parser.ts_rejected_speculations.putNoClobber(parser.allocator(), head, {});
100104
return null;
101105
};
102106

‎src/parser/syntax/ts/types/core.zig‎

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -503,6 +503,12 @@ fn parseInferType(parser: *Parser) Error!?ast.NodeIndex {
503503
fn parseInferConstraint(parser: *Parser) Error!ast.NodeIndex {
504504
if (parser.current_token.tag != .extends) return .null;
505505

506+
const extends_offset = parser.current_token.span.start;
507+
const conditional_allowed = !parser.ts_context.disallow_conditional_types;
508+
if (conditional_allowed and parser.ts_rejected_speculations.contains(extends_offset)) {
509+
return .null;
510+
}
511+
506512
const cp = parser.checkpoint();
507513
try parser.advance() orelse return .null;
508514

@@ -513,12 +519,11 @@ fn parseInferConstraint(parser: *Parser) Error!ast.NodeIndex {
513519
};
514520
parser.ts_context.disallow_conditional_types = cp.ts_context.disallow_conditional_types;
515521

516-
const yields_to_conditional =
517-
!cp.ts_context.disallow_conditional_types and
518-
parser.current_token.tag == .question;
522+
const yields_to_conditional = conditional_allowed and parser.current_token.tag == .question;
519523

520524
if (yields_to_conditional) {
521525
parser.rewind(cp);
526+
try parser.ts_rejected_speculations.putNoClobber(parser.allocator(), extends_offset, {});
522527
return .null;
523528
}
524529

0 commit comments

Comments
 (0)