feat(迭代27 T5): 波五a 类型闸拉起+存量收口——审计器 0 存量+warning 配置在位
P1-6 类型闸(须 T1-T4 全落后——本波按执行顺序锁殿后): - tools/audit_type_hints.py 新机器闸(V7): R1 无类型容器注解/R2 有值返回缺返回类型/ R3 参数缺类型三规则+行级 # type-hint: ignore 显式白名单通道;产品码 core/data/presentation - 存量收口: 368 处显式 Array/Dictionary 非参数化注解→机械参数化(Array[Variant]/ Dictionary[Variant, Variant] 零行为面);13 处参数真类型(SaveManagerCore/ConfigDb/ BattleOrchestrator/MinesweeperRules/LevelRunner/SettingsStore 等) - 白名单豁免四处: board_generator generate/_attempt/legality/_finalize 的 db 参—— 测量面 _BoardRowStub 冒烟夹具鸭子型注入(typed 参拒 stub) - project.godot 拉起 gdscript/warnings/untyped_declaration=1(V7 源码断言在 smoke _smoke_type_gate) - static_scan 新 S9 类型闸规则;顺修历史 cd 错位(9-24 布局迁移后 /.. 落 game/ 致守卫 恒跳过静默空转——修复后 S1-S4/S9 首次实跑全绿)+S1/S2 增 ADR-0007-exempt 标记豁免通道 (main _seed_value 引擎熵源=文档化豁免面) 验收: 冒烟 1081/0(+V7×2)+run_all 27 套+static_scan 全绿+probe click+render golden+对拍三变体全同
This commit is contained in:
1 parent
8b9e55673f
commit
b605022001
58 files changed
+503
-384
No files matched your search
@@ -0,0 +1,95 @@
|
||||
#!/usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
"""audit_type_hints.py — 迭代27 T5 类型闸机器审计(code-conventions §4 语言纪律的机器闸)。
|
||||
|
||||
产品码 = game/core + game/data + game/presentation(smoke/tests 豁免——测量面容器直构是惯例)。
|
||||
三条规则:
|
||||
R1 无类型容器声明:显式 Dictionary/Array 非参数化类型注解(审计 §2.2「73 处」同口径——
|
||||
`:= {}` 推断形态属类型推断合法面不算债)。
|
||||
R2 有值返回缺返回类型:func 无 `->` 且函数体内存在 `return <非空值>`(纯副作用 -> void
|
||||
不属本闸危害域,归静态写法面)。
|
||||
R3 参数缺类型:func 声明参数无 `: Type`(_init 豁免——构造注入面形态自由)。
|
||||
白名单:行内 `# type-hint: ignore` 豁免该行(V7 显式白名单通道)。
|
||||
退出码:0=归零,1=有存量(逐条列 file:line)。用法:python tools/audit_type_hints.py
|
||||
"""
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parent.parent
|
||||
SCAN_DIRS = ["game/core", "game/data", "game/presentation"]
|
||||
|
||||
R1A_UNPARAM = re.compile(r"\bvar\s+\w+\s*:\s*(Dictionary|Array)\b(?!\[)")
|
||||
R1B_LITERAL = re.compile(r"^\s*var\s+\w+\s*(:?=)\s*[\{\[]")
|
||||
R1B_TYPED = re.compile(r"^\s*var\s+\w+\s*:\s*[\w\[\]]+")
|
||||
FUNC_DECL = re.compile(r"^(\s*)func\s+(_?\w+)\s*\((.*)\)\s*(->\s*.+)?\s*(#.*)?$")
|
||||
PARAM_SPLIT = re.compile(r",(?![^(\[]*[)\]])")
|
||||
RETURN_VALUE = re.compile(r"^\s*return\s+\S")
|
||||
|
||||
|
||||
def strip_strings(line):
|
||||
line = re.sub(r'"[^"]*"', '""', line)
|
||||
line = re.sub(r"'[^']*'", "''", line)
|
||||
if line.lstrip().startswith("#"):
|
||||
return ""
|
||||
return line.split("#")[0]
|
||||
|
||||
|
||||
def audit_file(path):
|
||||
hits = []
|
||||
lines = path.read_text(encoding="utf-8").split("\n")
|
||||
for idx, raw in enumerate(lines):
|
||||
if "# type-hint: ignore" in raw:
|
||||
continue
|
||||
code = strip_strings(raw)
|
||||
# R1a 显式非参数化容器类型
|
||||
if R1A_UNPARAM.search(code):
|
||||
hits.append((idx + 1, "R1", raw.strip()[:90]))
|
||||
continue
|
||||
fm = FUNC_DECL.match(code)
|
||||
if not fm:
|
||||
continue
|
||||
indent, fname, params, ret = fm.group(1), fm.group(2), fm.group(3), fm.group(4)
|
||||
# R3 参数缺类型(_init 豁免)
|
||||
if fname != "_init" and params.strip():
|
||||
for prm in PARAM_SPLIT.split(params):
|
||||
prm = prm.strip()
|
||||
if prm and ":" not in prm:
|
||||
hits.append((idx + 1, "R3", "%s(参数 %s)" % (raw.strip()[:80], prm.split("=")[0].strip())))
|
||||
# R2 有值返回缺返回类型(函数体 = 缩进更深的后续连续块)
|
||||
if not ret and fname != "_init":
|
||||
body = []
|
||||
j = idx + 1
|
||||
while j < len(lines):
|
||||
l = lines[j]
|
||||
if l.strip() == "":
|
||||
j += 1
|
||||
continue
|
||||
if len(l) - len(l.lstrip("\t")) > len(indent):
|
||||
body.append(strip_strings(l))
|
||||
j += 1
|
||||
continue
|
||||
break
|
||||
if any(RETURN_VALUE.match(b) for b in body):
|
||||
hits.append((idx + 1, "R2", raw.strip()[:90]))
|
||||
return hits
|
||||
|
||||
|
||||
def main():
|
||||
if hasattr(sys.stdout, "reconfigure"):
|
||||
sys.stdout.reconfigure(encoding="utf-8", errors="replace")
|
||||
total = 0
|
||||
for d in SCAN_DIRS:
|
||||
for p in sorted((ROOT / d).rglob("*.gd")):
|
||||
for line, rule, text in audit_file(p):
|
||||
print("[%s] %s:%d: %s" % (rule, p.relative_to(ROOT).as_posix(), line, text))
|
||||
total += 1
|
||||
if total:
|
||||
print("[audit_type_hints] FAIL——存量 %d 处(R1 无类型容器/R2 有值返回缺类型/R3 参数缺类型)" % total)
|
||||
return 1
|
||||
print("[audit_type_hints] PASS——产品码类型闸归零(R1/R2/R3)")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user