fix(parser): recognize contextual binding names in type lookaheads (#26532)

Recognize `await` and `yield` as binding names when looking ahead for TypeScript function types and index signatures. This allows valid syntax such as `(await: number) => void` and `interface I { [await: string]: unknown }`, while leaving contextual legality to actual parsing.
This commit is contained in:
camc314
2026-09-10 14:05:25 +00:00
parent 1d681b0d4c
commit 47b83115f0
13 changed files with 96 additions and 16 deletions
+2 -2
View File
@@ -410,10 +410,10 @@ impl<'a, C: Config> ParserImpl<'a, C> {
}
if self.cur_kind().is_modifier_kind() {
self.bump_any();
if self.cur_kind().is_identifier() {
if self.cur_kind().is_binding_identifier() {
return true;
}
} else if !self.cur_kind().is_identifier() {
} else if !self.cur_kind().is_binding_identifier() {
return false;
} else {
self.bump_any();
+2 -1
View File
@@ -128,7 +128,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
self.parse_modifiers(false, false);
}
let kind = self.cur_kind();
if kind.is_identifier() || kind == Kind::This {
if kind.is_binding_identifier() || kind == Kind::This {
self.bump_any();
return true;
}
@@ -1603,6 +1603,7 @@ impl<'a, C: Config> ParserImpl<'a, C> {
fn parse_ts_index_signature_name(&mut self) -> TSIndexSignatureName<'a> {
let start = self.cur_start();
self.check_identifier(self.cur_kind(), self.ctx);
let name = self.parse_identifier_name().name;
if self.at(Kind::Question) {
self.error(diagnostics::index_signature_question_mark(self.cur_token().span()));
@@ -0,0 +1 @@
let fn: (break: number) => void;
@@ -0,0 +1,9 @@
function* generatorInterface() {
interface I { [yield: string]: unknown }
}
function* generatorClass() {
class C { [yield: string]: unknown }
}
async function asyncClass() {
class C { [await: string]: unknown }
}
@@ -0,0 +1 @@
interface I { [public await: string]: unknown }
@@ -0,0 +1 @@
interface I { [break: string]: unknown }
@@ -0,0 +1,24 @@
// First parameters take the function-type lookahead; later and generic parameters do not.
let firstAwait: (await: number) => void;
let laterAwait: (value: number, await: number) => void;
let genericAwait: <T>(await: T) => void;
let firstYield: (yield: number) => void;
let laterYield: (value: number, yield: number) => void;
let genericYield: <T>(yield: T) => void;
let optionalAwait: (await?: number) => void;
let optionalYield: (yield?: number) => void;
let untypedAwait: (await) => void;
let untypedYield: (yield) => void;
// Parenthesized types and computed properties must retain their interpretation.
type await = number;
type yield = number;
type AwaitType = (await);
type YieldType = (yield);
const key = "key";
interface Computed { [key]: unknown }
interface AwaitIndex { [await: string]: unknown }
interface YieldIndex { [yield: string]: unknown }
type AwaitIndexType = { [await: string]: unknown };
type YieldIndexType = { [yield: string]: unknown };
+2 -2
View File
@@ -1,3 +1,3 @@
codegen_misc Summary:
AST Parsed : 104/104 (100.00%)
Positive Passed: 104/104 (100.00%)
AST Parsed : 105/105 (100.00%)
Positive Passed: 105/105 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
formatter_misc Summary:
AST Parsed : 104/104 (100.00%)
Positive Passed: 104/104 (100.00%)
AST Parsed : 105/105 (100.00%)
Positive Passed: 105/105 (100.00%)
+2 -2
View File
@@ -1,3 +1,3 @@
lexer_misc Summary:
AST Parsed : 314/314 (100.00%)
Positive Passed: 314/314 (100.00%)
AST Parsed : 319/319 (100.00%)
Positive Passed: 319/319 (100.00%)
+46 -3
View File
@@ -1,7 +1,7 @@
parser_misc Summary:
AST Parsed : 104/104 (100.00%)
Positive Passed: 104/104 (100.00%)
Negative Passed: 210/210 (100.00%)
AST Parsed : 105/105 (100.00%)
Positive Passed: 105/105 (100.00%)
Negative Passed: 214/214 (100.00%)
× Cannot assign to 'arguments' in strict mode
╭─[misc/fail/arguments-eval-ambient.ts:2:13]
@@ -383,6 +383,49 @@ Negative Passed: 210/210 (100.00%)
3 │ constructor() {}
╰────
× Expected `)` but found `:`
╭─[misc/fail/contextual-binding-lookahead-function-reserved.ts:1:15]
1 │ let fn: (break: number) => void;
· ┬
· ╰── `)` expected
╰────
× Cannot use `yield` as an identifier in a generator context
╭─[misc/fail/contextual-binding-lookahead-index-context.ts:2:18]
1 │ function* generatorInterface() {
2 │ interface I { [yield: string]: unknown }
· ─────
3 │ }
╰────
× Cannot use `yield` as an identifier in a generator context
╭─[misc/fail/contextual-binding-lookahead-index-context.ts:5:14]
4 │ function* generatorClass() {
5 │ class C { [yield: string]: unknown }
· ─────
6 │ }
╰────
× Cannot use `await` as an identifier in an async context
╭─[misc/fail/contextual-binding-lookahead-index-context.ts:8:14]
7 │ async function asyncClass() {
8 │ class C { [await: string]: unknown }
· ─────
9 │ }
╰────
× Unexpected token
╭─[misc/fail/contextual-binding-lookahead-index-modifier.ts:1:23]
1 │ interface I { [public await: string]: unknown }
· ─────
╰────
× Unexpected token
╭─[misc/fail/contextual-binding-lookahead-index-reserved.ts:1:16]
1 │ interface I { [break: string]: unknown }
· ─────
╰────
× A `continue` statement can only jump to a label of an enclosing `for`, `while` or `do while` statement.
╭─[misc/fail/continue-label-chain-non-iteration.js:1:1]
1 │ a: b: {
+2 -2
View File
@@ -1,6 +1,6 @@
semantic_misc Summary:
AST Parsed : 104/104 (100.00%)
Positive Passed: 100/104 (96.15%)
AST Parsed : 105/105 (100.00%)
Positive Passed: 101/105 (96.19%)
semantic Error: tasks/coverage/misc/pass/declare-let-private.ts
Bindings mismatch:
after transform: ScopeId(0): ["private"]
@@ -1,3 +1,3 @@
transformer_misc Summary:
AST Parsed : 104/104 (100.00%)
Positive Passed: 104/104 (100.00%)
AST Parsed : 105/105 (100.00%)
Positive Passed: 105/105 (100.00%)