From 2a1e805d55e769ac5ea4882cda5fc2dd86f36e38 Mon Sep 17 00:00:00 2001 From: camc314 <18101008+camc314@users.noreply.github.com> Date: Mon, 14 Jul 2025 19:15:23 +0000 Subject: [PATCH] fix(semantic): allow assigning to `eval` and `arguments` in ambient context (#12208) https://github.com/microsoft/typescript/blob/7f802bbca25634c6bffe316de75771274f1d9b1d/src/compiler/binder.ts#L2670 https://www.typescriptlang.org/play/?#code/CYUwxgNghgTiAEAzArgOzAFwJYHtXxADcoIAKASgC55CctgBueAemfgHkBpAKFElgQp02PPFgBzZAFsQqDAGcK1WvSasOPPtDhI0mXPkSkiJaqmkAjEDAA0YmJJlz5Zy9arxzUqzDVsuvODagnoi+OKkAN4mEHYS0rIKAL7U0cQQrt7WcQ4Jzpk+SR5ePn7wAKIwMDgwgfw6QvqiABakANoxOY6J8gC61G0l2Z5uML3Fo2WV1TBAA --- crates/oxc_semantic/src/checker/javascript.rs | 32 +++- tasks/coverage/snapshots/parser_babel.snap | 138 +----------------- .../coverage/snapshots/parser_typescript.snap | 36 +---- tasks/coverage/snapshots/semantic_babel.snap | 34 ++--- .../snapshots/semantic_typescript.snap | 8 +- 5 files changed, 46 insertions(+), 202 deletions(-) diff --git a/crates/oxc_semantic/src/checker/javascript.rs b/crates/oxc_semantic/src/checker/javascript.rs index a016e5c3d6108..ea025da81a75a 100644 --- a/crates/oxc_semantic/src/checker/javascript.rs +++ b/crates/oxc_semantic/src/checker/javascript.rs @@ -107,9 +107,37 @@ fn invalid_let_declaration(x0: &str, span1: Span) -> OxcDiagnostic { pub fn check_binding_identifier(ident: &BindingIdentifier, ctx: &SemanticBuilder<'_>) { let strict_mode = ctx.strict_mode(); - // It is a Diagnostic if the StringValue of a BindingIdentifier is "eval" or "arguments" within strict mode code. + // In strict mode, `eval` and `arguments` are banned as identifiers. if strict_mode && matches!(ident.name.as_str(), "eval" | "arguments") { - return ctx.error(unexpected_identifier_assign(&ident.name, ident.span)); + // `eval` and `arguments` are allowed as the names of declare functions as well as their arguments. + // + // declare function eval(): void; // OK + // declare function arguments(): void; // OK + // declare function f(eval: number, arguments: number): number; // OK + // declare function f(...eval): number; // OK + // declare function f(...arguments): number; // OK + // declare function g({eval, arguments}: {eval: number, arguments: number}): number; // Error + // declare function h([eval, arguments]: [number, number]): number; // Error + let is_declare_function = |kind: &AstKind| { + kind.as_function() + .is_some_and(|func| matches!(func.r#type, FunctionType::TSDeclareFunction)) + }; + + let parent = ctx.nodes.parent_node(ctx.current_node_id); + let is_ok = match parent.kind() { + AstKind::Function(func) => matches!(func.r#type, FunctionType::TSDeclareFunction), + AstKind::FormalParameter(_) => is_declare_function(&ctx.nodes.parent_kind(parent.id())), + AstKind::BindingRestElement(_) => { + let grand_parent = ctx.nodes.parent_node(parent.id()); + matches!(grand_parent.kind(), AstKind::FormalParameters(_)) + && is_declare_function(&ctx.nodes.parent_kind(grand_parent.id())) + } + _ => false, + }; + + if !is_ok { + return ctx.error(unexpected_identifier_assign(&ident.name, ident.span)); + } } // LexicalDeclaration : LetOrConst BindingList ; diff --git a/tasks/coverage/snapshots/parser_babel.snap b/tasks/coverage/snapshots/parser_babel.snap index 05808ca97a9dd..bcad150ae85aa 100644 --- a/tasks/coverage/snapshots/parser_babel.snap +++ b/tasks/coverage/snapshots/parser_babel.snap @@ -2,7 +2,7 @@ commit: 1d4546bc parser_babel Summary: AST Parsed : 2351/2362 (99.53%) -Positive Passed: 2328/2362 (98.56%) +Positive Passed: 2332/2362 (98.73%) Negative Passed: 1600/1698 (94.23%) Expect Syntax Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/core/categorized/invalid-startindex-and-startline-specified-without-startcolumn/input.js @@ -560,142 +560,6 @@ Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typesc 6 │ } ╰──── -Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/eval/input.ts - - × Cannot assign to 'eval' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval/input.ts:3:25] - 2 │ export namespace ns { - 3 │ export function eval(): void; - · ──── - 4 │ export function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval/input.ts:4:25] - 3 │ export function eval(): void; - 4 │ export function arguments(): void; - · ───────── - 5 │ } - ╰──── - - × Cannot assign to 'eval' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval/input.ts:8:18] - 7 │ - 8 │ declare function eval(): void; - · ──── - 9 │ declare function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval/input.ts:9:18] - 8 │ declare function eval(): void; - 9 │ declare function arguments(): void; - · ───────── - 10 │ - ╰──── - -Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/eval-babel-7/input.ts - - × Cannot assign to 'eval' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-babel-7/input.ts:3:25] - 2 │ export namespace ns { - 3 │ export function eval(): void; - · ──── - 4 │ export function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-babel-7/input.ts:4:25] - 3 │ export function eval(): void; - 4 │ export function arguments(): void; - · ───────── - 5 │ } - ╰──── - - × Cannot assign to 'eval' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-babel-7/input.ts:8:18] - 7 │ - 8 │ declare function eval(): void; - · ──── - 9 │ declare function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-babel-7/input.ts:9:18] - 8 │ declare function eval(): void; - 9 │ declare function arguments(): void; - · ───────── - 10 │ - ╰──── - -Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts/input.ts - - × Cannot assign to 'eval' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts/input.ts:4:25] - 3 │ export namespace ns { - 4 │ export function eval(): void; - · ──── - 5 │ export function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts/input.ts:5:25] - 4 │ export function eval(): void; - 5 │ export function arguments(): void; - · ───────── - 6 │ } - ╰──── - - × Cannot assign to 'eval' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts/input.ts:9:18] - 8 │ - 9 │ declare function eval(): void; - · ──── - 10 │ declare function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts/input.ts:10:18] - 9 │ declare function eval(): void; - 10 │ declare function arguments(): void; - · ───────── - 11 │ - ╰──── - -Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts-babel-7/input.ts - - × Cannot assign to 'eval' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts-babel-7/input.ts:4:25] - 3 │ export namespace ns { - 4 │ export function eval(): void; - · ──── - 5 │ export function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts-babel-7/input.ts:5:25] - 4 │ export function eval(): void; - 5 │ export function arguments(): void; - · ───────── - 6 │ } - ╰──── - - × Cannot assign to 'eval' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts-babel-7/input.ts:9:18] - 8 │ - 9 │ declare function eval(): void; - · ──── - 10 │ declare function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts-babel-7/input.ts:10:18] - 9 │ declare function eval(): void; - 10 │ declare function arguments(): void; - · ───────── - 11 │ - ╰──── - Expect to Parse: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/explicit-resource-management/valid-for-using-declaration-binding-of/input.js × Unexpected token diff --git a/tasks/coverage/snapshots/parser_typescript.snap b/tasks/coverage/snapshots/parser_typescript.snap index 03d0c85be372e..e3489d49ab7a2 100644 --- a/tasks/coverage/snapshots/parser_typescript.snap +++ b/tasks/coverage/snapshots/parser_typescript.snap @@ -2,7 +2,7 @@ commit: 81c95189 parser_typescript Summary: AST Parsed : 6693/6700 (99.90%) -Positive Passed: 6680/6700 (99.70%) +Positive Passed: 6681/6700 (99.72%) Negative Passed: 1422/5598 (25.40%) Expect Syntax Error: tasks/coverage/typescript/tests/cases/compiler/ExportAssignment7.ts @@ -8463,40 +8463,6 @@ Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/elidedEmbeddedSt 24 │ const enum H {} ╰──── -Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/evalOrArgumentsInDeclarationFunctions.ts - - × Cannot assign to 'eval' in strict mode - ╭─[typescript/tests/cases/compiler/evalOrArgumentsInDeclarationFunctions.ts:3:25] - 2 │ export namespace ns { - 3 │ export function eval(): void; - · ──── - 4 │ export function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[typescript/tests/cases/compiler/evalOrArgumentsInDeclarationFunctions.ts:4:25] - 3 │ export function eval(): void; - 4 │ export function arguments(): void; - · ───────── - 5 │ } - ╰──── - - × Cannot assign to 'eval' in strict mode - ╭─[typescript/tests/cases/compiler/evalOrArgumentsInDeclarationFunctions.ts:8:18] - 7 │ - 8 │ declare function eval(): void; - · ──── - 9 │ declare function arguments(): void; - ╰──── - - × Cannot assign to 'arguments' in strict mode - ╭─[typescript/tests/cases/compiler/evalOrArgumentsInDeclarationFunctions.ts:9:18] - 8 │ declare function eval(): void; - 9 │ declare function arguments(): void; - · ───────── - 10 │ - ╰──── - Expect to Parse: tasks/coverage/typescript/tests/cases/compiler/moduleResolutionWithExtensions_unexpected2.ts × Expected a semicolon or an implicit semicolon after a statement, but found none diff --git a/tasks/coverage/snapshots/semantic_babel.snap b/tasks/coverage/snapshots/semantic_babel.snap index ee32030d26e43..b6ba0a1f45ead 100644 --- a/tasks/coverage/snapshots/semantic_babel.snap +++ b/tasks/coverage/snapshots/semantic_babel.snap @@ -2,7 +2,7 @@ commit: 1d4546bc semantic_babel Summary: AST Parsed : 2362/2362 (100.00%) -Positive Passed: 1951/2362 (82.60%) +Positive Passed: 1953/2362 (82.68%) semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/comments/decorators/decorators-after-export/input.js Symbol span mismatch for "C": after transform: SymbolId(0): Span { start: 65, end: 66 } @@ -563,28 +563,20 @@ after transform: ScopeId(0): ["x", "y"] rebuilt : ScopeId(0): [] semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/eval/input.ts -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode +Bindings mismatch: +after transform: ScopeId(0): ["arguments", "eval"] +rebuilt : ScopeId(0): [] +Scope children mismatch: +after transform: ScopeId(0): [ScopeId(1), ScopeId(5), ScopeId(6)] +rebuilt : ScopeId(0): [] semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/eval-babel-7/input.ts -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode - -semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts/input.ts -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode - -semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/eval-dts-babel-7/input.ts -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode +Bindings mismatch: +after transform: ScopeId(0): ["arguments", "eval"] +rebuilt : ScopeId(0): [] +Scope children mismatch: +after transform: ScopeId(0): [ScopeId(1), ScopeId(5), ScopeId(6)] +rebuilt : ScopeId(0): [] semantic Error: tasks/coverage/babel/packages/babel-parser/test/fixtures/typescript/declare/function-rest-trailing-comma/input.ts Bindings mismatch: diff --git a/tasks/coverage/snapshots/semantic_typescript.snap b/tasks/coverage/snapshots/semantic_typescript.snap index cfe727942e501..69f16508aae0d 100644 --- a/tasks/coverage/snapshots/semantic_typescript.snap +++ b/tasks/coverage/snapshots/semantic_typescript.snap @@ -2,7 +2,7 @@ commit: 81c95189 semantic_typescript Summary: AST Parsed : 6537/6537 (100.00%) -Positive Passed: 2824/6537 (43.20%) +Positive Passed: 2825/6537 (43.22%) semantic Error: tasks/coverage/typescript/tests/cases/compiler/2dArrays.ts Symbol reference IDs mismatch for "Cell": after transform: SymbolId(0): [ReferenceId(1)] @@ -13006,12 +13006,6 @@ semantic Error: tasks/coverage/typescript/tests/cases/compiler/escapedIdentifier Namespaces exporting non-const are not supported by Babel. Change to const or see: https://babeljs.io/docs/en/babel-plugin-transform-typescript Namespaces exporting non-const are not supported by Babel. Change to const or see: https://babeljs.io/docs/en/babel-plugin-transform-typescript -semantic Error: tasks/coverage/typescript/tests/cases/compiler/evalOrArgumentsInDeclarationFunctions.ts -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode -Cannot assign to 'eval' in strict mode -Cannot assign to 'arguments' in strict mode - semantic Error: tasks/coverage/typescript/tests/cases/compiler/eventEmitterPatternWithRecordOfFunction.ts Scope children mismatch: after transform: ScopeId(0): [ScopeId(1), ScopeId(3), ScopeId(6), ScopeId(7)]