diff --git a/game/tests/static_scan.sh b/game/tests/static_scan.sh index 4074a72a..a104e618 100644 --- a/game/tests/static_scan.sh +++ b/game/tests/static_scan.sh @@ -51,6 +51,36 @@ else fail=1 fi +# ── S6 — core 信号连接禁内联 lambda(架构文档 S 表:core↔core 信号连接只许方法 Callable,禁 lambda 捕获 self)── +# 扫描面:$DIR_CORE(game/core,core↔core 规则域=core 文件集;单点变量防空转)。 +# 判据=信号连接的内联 lambda 形态,保守集合(误报零容忍,拿不准的形态宁可不抓不红): +# ①同行形态:`.connect(` 后紧接 `func`——`sig.connect(func(a): ...)`; +# ②跨行形态:行尾以 `.connect(` 结束、下一行以 `func(` 起始(awk 前行状态机实现)。 +# 拿不准不抓的形态:bind/Callable 变量中转等间接 lambda 通道、注释外的字符串字面量内 `.connect(func` 同行 +# 全行注释已排除、行尾注记已豁免——间接通道归人工评审,本闸只防直接内联 lambda 回归。 +# 豁免通道:行内注记 s6-signal-exempt(仿 S2 rng-seed-exempt)——确需内联 lambda 的在册例外逐处留档。 +# 规则第二句「发射者禁持有订阅者反向引用」:静态判据不成熟(持有引用形态难可靠识别),不造弱判据, +# 该句归人工评审面承接,本闸只防内联 lambda 回归(2026-10-24 人工 grep 盘面干净,闸为防回归防线)。 +if require_scan_face "S6" "$DIR_CORE"; then +S6_HITS="$( + grep -rnE '\.connect[[:space:]]*\([[:space:]]*func\b' "$DIR_CORE" --include='*.gd' 2>/dev/null \ + | grep -vE '^[^:]+:[0-9]+:[[:space:]]*#' | grep -v 's6-signal-exempt'; + find "$DIR_CORE" -name '*.gd' -print0 2>/dev/null | xargs -0 awk ' + prev ~ /\.connect[ \t]*\([ \t]*$/ && $0 ~ /^[ \t]*func[ \t]*\(/ && + prev !~ /^[ \t]*#/ && prev !~ /s6-signal-exempt/ && $0 !~ /s6-signal-exempt/ { + print FILENAME ":" FNR ":" $0 " (跨行 lambda——上一行行尾 .connect( )" + } + { prev = $0 }' +)" +if [ -n "$S6_HITS" ]; then + echo "[static_scan] S6 FAIL: core 信号连接内联 lambda 命中(只许方法 Callable,禁 lambda 捕获 self)" + echo "$S6_HITS" + fail=1 +else + echo "[static_scan] S6 ok(扫描面 $DIR_CORE 共 $(scan_files "$DIR_CORE") 个 .gd,内联 lambda 零命中)" +fi +fi + # S1/S2 — 玩法代码禁 randomize()/自建 RandomNumberGenerator/全局随机函数(绕开 RngService 的口子) # 违规签名=裸 randomize/rand* 调用(实例方法 .randi() 排除) @@ -89,7 +119,6 @@ if [ "$hits" -gt 0 ]; then echo "[static_scan] S5 FAIL: core 裸引用 autoload fi # TODO(正式工程落地后精化为扫描配置并升格为硬失败): -# S6 core↔core 信号连接只许方法 Callable(禁 lambda 捕获 self);发射者禁反向引用 # S7 B2/B3/B4 API 符号命中文件 ⊆ B1 文件集(编排唯一性) # ── S11 — 代码注释禁用词 ──