diff --git a/crates/oxide_compiler/src/hash/class.rs b/crates/oxide_compiler/src/hash/class.rs index 6c2c30a..f780921 100644 --- a/crates/oxide_compiler/src/hash/class.rs +++ b/crates/oxide_compiler/src/hash/class.rs @@ -8,6 +8,8 @@ pub(super) fn hash_class_element(element: &ClassElement, h: &mut rustc_hash::FxH method.r#static.hash(h); method.computed.hash(h); std::mem::discriminant(&method.kind).hash(h); + method.value.r#async.hash(h); + method.value.generator.hash(h); property::hash_property_key(&method.key, h, include_binding_names); if method.computed { property::hash_property_key(&method.key, h, include_binding_names); diff --git a/crates/oxide_compiler/src/hash/expression.rs b/crates/oxide_compiler/src/hash/expression.rs index 65d4494..b93ed36 100644 --- a/crates/oxide_compiler/src/hash/expression.rs +++ b/crates/oxide_compiler/src/hash/expression.rs @@ -82,6 +82,7 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i } } Expression::ArrowFunctionExpression(arrow) => { + arrow.r#async.hash(h); (arrow.params.items.len() as u32).hash(h); if include_binding_names { for param in &arrow.params.items { @@ -91,6 +92,8 @@ pub(super) fn hash_expression(expr: &Expression, h: &mut rustc_hash::FxHasher, i hash_function_body(&arrow.body, h, include_binding_names); } Expression::FunctionExpression(fe) => { + fe.r#async.hash(h); + fe.generator.hash(h); (fe.params.items.len() as u32).hash(h); if include_binding_names { if let Some(id) = &fe.id { diff --git a/crates/oxide_compiler/src/hash/statement.rs b/crates/oxide_compiler/src/hash/statement.rs index 9c9976e..ea7c04e 100644 --- a/crates/oxide_compiler/src/hash/statement.rs +++ b/crates/oxide_compiler/src/hash/statement.rs @@ -194,6 +194,8 @@ fn hash_for_statement(fr: &oxide_parser::ForStatement<'_>, h: &mut rustc_hash::F } fn hash_function_declaration(fd: &Function<'_>, h: &mut rustc_hash::FxHasher, include_binding_names: bool) { + fd.r#async.hash(h); + fd.generator.hash(h); if include_binding_names { if let Some(id) = &fd.id { id.name.as_str().hash(h); diff --git a/crates/oxide_compiler/tests/hash_collision_tests.rs b/crates/oxide_compiler/tests/hash_collision_tests.rs index 3167e85..dd9cfcc 100644 --- a/crates/oxide_compiler/tests/hash_collision_tests.rs +++ b/crates/oxide_compiler/tests/hash_collision_tests.rs @@ -130,3 +130,49 @@ fn function_body_directives_distinguished() { "identical sources must hash equally" ); } + +#[test] +fn function_async_generator_flags_distinguished() { + // 函数声明四形态:body 结构相同(return 1),仅标志不同,键必须互异。 + // 修复前 async/generator 不入 hash → 四键全同 → 下方 assert_ne 全红。 + let sync = compiled("function f(){ return 1 }"); + let async_fn = compiled("async function f(){ return 1 }"); + let gen = compiled("function* f(){ return 1 }"); + let async_gen = compiled("async function* f(){ return 1 }"); + assert_ne!(sync, async_fn, "sync vs async function declaration"); + assert_ne!(sync, gen, "sync vs generator declaration"); + assert_ne!(sync, async_gen, "sync vs async generator declaration"); + assert_ne!(async_fn, gen, "async vs generator declaration"); + assert_ne!(async_fn, async_gen, "async vs async generator declaration"); + assert_ne!(gen, async_gen, "generator vs async generator declaration"); + + // 函数表达式(含对象字面量方法经 FunctionExpression 分支的覆盖路径) + assert_ne!( + compiled("var f = function(){ return 1 }"), + compiled("var f = async function(){ return 1 }"), + "function expression async flag" + ); + assert_ne!( + compiled("var f = function(){ return 1 }"), + compiled("var f = function*(){ return 1 }"), + "function expression generator flag" + ); + + // 箭头函数(仅 async 标志;箭头无 generator) + assert_ne!(compiled("var f = () => 1"), compiled("var f = async () => 1"), "arrow async flag"); + + // 类方法(kind 已入 hash,但 async/generator 与 kind 正交) + assert_ne!(compiled("class A { m(){ return 1 } }"), compiled("class A { async m(){ return 1 } }")); + assert_ne!(compiled("class A { m(){ return 1 } }"), compiled("class A { *m(){ return 1 } }")); + assert_ne!(compiled("class A { m(){ return 1 } }"), compiled("class A { async *m(){ return 1 } }")); + + // 对象字面量方法 + assert_ne!( + compiled("var o = { m(){ return 1 } }"), + compiled("var o = { async m(){ return 1 } }"), + "object literal method async flag" + ); + + // 同源等价回归 + assert_eq!(compiled("async function f(){ return 1 }"), compiled("async function f(){ return 1 }")); +}