perf(parser): skip checkpoint for infer T extends U constraint in disallow context (#23128)

## What

`parse_constraint_of_infer_type` always took a parser-level `checkpoint()` to speculatively parse the `extends U` constraint of an `infer T extends U` type, rewinding when a trailing `?` meant the `extends` actually belonged to an enclosing conditional type (`infer T extends U ? A : B`).

## Why the checkpoint was usually wasted

`infer` normally appears in a conditional type's `extends` clause, which is parsed under `DisallowConditionalTypes` (see `parse_ts_type`). In that context a trailing `?` cannot reinterpret the `extends`, so the old post-parse check short-circuited to `Some(constraint)` through the `||`:

```rust
if self.ctx.has_disallow_conditional_types() || !self.at(Kind::Question) {
    Some(constraint)   // <- always taken in the common case
} else {
    self.rewind(checkpoint);
    None
}
```

So the checkpoint was taken on every `infer T extends U` but never actually used.

## Change

Guard on `has_disallow_conditional_types()` up front and parse the constraint directly when it is set, taking the checkpoint only on the genuinely-speculative path — the rare case where `infer` is parsed in a conditional-*allowed* position and a trailing `?` reinterprets the `extends`.

Behavior-preserving:
- `cargo coverage -- parser` — test262 100%, snapshots unchanged.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
This commit is contained in:
Boshen committed 2026-06-08 17:48:16 +00:00
1 parent 7464dceaa4
commit b435c6af2e
1 file changed
+11 -3
+11 -3
View File
@@ -323,14 +323,22 @@ impl<'a, C: Config> ParserImpl<'a, C> {
if !self.at(Kind::Extends) {
return None;
}
// When conditional types are already disallowed by the enclosing context — the normal case,
// since `infer` lives in a conditional's `extends` clause which is parsed with
// `DisallowConditionalTypes` — a trailing `?` cannot reinterpret `extends` as a conditional.
// The constraint is then unambiguous, so parse it without a checkpoint/rewind.
if self.ctx.has_disallow_conditional_types() {
self.bump_any();
return Some(self.context_add(Context::DisallowConditionalTypes, Self::parse_ts_type));
}
let checkpoint = self.checkpoint();
self.bump_any();
let constraint = self.context_add(Context::DisallowConditionalTypes, Self::parse_ts_type);
if self.ctx.has_disallow_conditional_types() || !self.at(Kind::Question) {
Some(constraint)
} else {
if self.at(Kind::Question) {
self.rewind(checkpoint);
None
} else {
Some(constraint)
}
}