From bd1427b1161079dd91b6886c1f07ddf056351a81 Mon Sep 17 00:00:00 2001 From: pjh456 <147148383@qq.com> Date: Wed, 9 Sep 2026 19:52:03 +0800 Subject: [PATCH] =?UTF-8?q?test:=20TDZ=20=E5=8D=95=E6=B5=8B=E8=A1=A5?= =?UTF-8?q?=E5=B5=8C=E5=A5=97=E5=9D=97/=E8=87=AA=E5=BC=95=E7=94=A8/hoisted?= =?UTF-8?q?=20=E8=AF=BB=E5=BD=A2=E7=8A=B6=20+=20=E5=9B=BA=E5=8C=96?= =?UTF-8?q?=E5=8D=95=E8=AF=AD=E5=8F=A5=E4=BD=93=20lexical=20=E5=A3=B0?= =?UTF-8?q?=E6=98=8E=E8=A7=A3=E6=9E=90=E6=8B=92=E7=BB=9D=E5=A5=91=E7=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- crates/oxide_emit/src/prepass.rs | 5 ++-- crates/oxide_vm/tests/scoping_tests.rs | 37 ++++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 2 deletions(-) diff --git a/crates/oxide_emit/src/prepass.rs b/crates/oxide_emit/src/prepass.rs index 945ce405..d6c8de2a 100644 --- a/crates/oxide_emit/src/prepass.rs +++ b/crates/oxide_emit/src/prepass.rs @@ -537,8 +537,9 @@ impl Emitter { /// /// # 边界与前提 /// - 只扫描直接子语句 + 递归 switch case(switch 不推 scope,case 内 lexical - /// 声明属于外层作用域);不递归块/if/for/while body(嵌套块自预声明, - /// 单语句 body 不含 lexical 声明)。 + /// 声明属于外层作用域);不递归块/if/for/while body(嵌套块自预声明; + /// 单语句 body 不接受 lexical 声明——lexical 属 Declaration、非 Statement + /// 子产生式,parser 按语法错误直接拒绝,这些形状不会进入 emit)。 /// - 跳过 for 头声明(循环作用域由 for 分支内联 declare)。 pub(crate) fn predeclare_lexical_declarations(&self, statements: &[Statement], ctx: &mut CompileCtx) { for statement in statements { diff --git a/crates/oxide_vm/tests/scoping_tests.rs b/crates/oxide_vm/tests/scoping_tests.rs index 5be104d3..60901751 100644 --- a/crates/oxide_vm/tests/scoping_tests.rs +++ b/crates/oxide_vm/tests/scoping_tests.rs @@ -158,3 +158,40 @@ fn typeof_tdz_throws() { let err = eval("typeof x; let x;").unwrap_err(); assert!(err.contains("ReferenceError"), "got: {}", err); } + +#[test] +fn tdz_nested_block_read_throws() { + // 嵌套块读:块内声明点前的读命中块级 TDZ 占位,抛 ReferenceError。 + let err = eval("{ x; let x; }").unwrap_err(); + assert!(err.contains("ReferenceError"), "got: {}", err); +} + +#[test] +fn tdz_declaration_self_reference_throws() { + // 声明语句自引用:初始化器读 x 时 x 尚未初始化,抛 ReferenceError。 + let err = eval("let x = x + 1").unwrap_err(); + assert!(err.contains("ReferenceError"), "got: {}", err); +} + +#[test] +fn tdz_hoisted_function_reads_later_let_throws() { + // hoisted 函数读声明点之后的 let:调用时 x 仍在 TDZ,抛 ReferenceError。 + let err = eval("function f(){return x} f(); let x;").unwrap_err(); + assert!(err.contains("ReferenceError"), "got: {}", err); +} + +#[test] +fn lexical_decl_in_single_statement_body_rejected_at_parse() { + // 文法契约:单语句体取 Statement,lexical 声明属 Declaration(非 Statement), + // `if (c) let x = 5;` 等无括号控制流体体直属 lexical 是语法错误,解析期即拒绝, + // 不会进入 emit——TDZ 窗口读在该形状上不可达。此前提若被未来 parser 变更 + // 打破(开始接受这些形状),须同步补 lexical 预声明的体递归覆盖。 + let err = eval("if (true) let x = 5;").unwrap_err(); + assert!(err.contains("Parse"), "got: {}", err); + let err = eval("if (true) const x = 5;").unwrap_err(); + assert!(err.contains("Parse"), "got: {}", err); + let err = eval("for (let i = 0; i < 1; i++) let x = 5;").unwrap_err(); + assert!(err.contains("Parse"), "got: {}", err); + let err = eval("lab: let x = 5;").unwrap_err(); + assert!(err.contains("Parse"), "got: {}", err); +}