现状口径:/keil-build 与 /serial-boot-test 是 DSH 会话的技能调用名, 不是 shell 命令;「名 + 命令行参数」的命令形态改为可执行体 run_boot_test.py 的真实调用。 - AGENTS.md:别名说明重写为「技能调用名不是 shell 命令」,两条目改 「编译(/keil-build)」「串口回归(/serial-boot-test)」 - keil-build / serial-boot-test 两 skill 调用名与示例同步;task-worktree 方案文档去掉 @build/@test 惯例从句;@task 保留(不在点名范围) - 123 文件现状陈述全改;台架捕获、变更记录/时间线、rename 映射输入 等 41 行历史证据保留旧名 - gen_memory_svg.py 6 处措辞随改,3 张 SVG 产物同批重跑 - 顺手补并行方案同行漏网计数 51→52 钩子 验证:提交期快钩子对 123 文件实跑全 Passed;check_doc_links --all rc=0(3038 处、无法判定 0);check_mermaid --all rc=0;YAML 解析 52 钩子不变;skill-size rc=0;py_compile 全过;字节卫生零漂移。
8.7 KiB
块注释嵌套(nested-comment)— 注释体内不得再出现 /*
一句话:C 的块注释不嵌套。注释体内再出现一个 /* 时,ARMCC 报诊断 9-D 「nested comment is not allowed」。本仓的触发形态极其日常——在注释里写作用域或通配路径。 脚本
scripts/check_nested_comment.py(纯标准库、只读,2026-10-27 接线)。
1. 它在查什么
逐字节状态机走 代码 / 行注释 / 块注释 / 字符串 / 字符字面量 五个状态, 只有在块注释体内看到的 /* 才算问题,报「文件:行:列 + 上下文指纹 + 修法」。
| 判定 | 形态(本仓真实写法或自测用例) |
|---|---|
| 算 | 注释里写路径通配:Drivers/BSP/UI/**、test/host/**、App/algo/*(那串里的 /* 就是第二次注释的起点) |
| 算 | 块注释内部某行行首又开一个注释(复制粘贴手滑);块注释里用引号包住 /;块注释里行注释符之后再来 / |
| 不算 | 字符串字面量里的 /*(const char *s = "/* 不是注释 */";) |
| 不算 | 行注释里写 /*(编译器不报,本闸也不报) |
| 不算 | 本仓既有的尖括号写法 <Drivers/BSP/UI/ui_w_soe.c>(推荐改法) |
2. 为什么要它
- 只在重编译那个 TU 时才暴露:改了某个头里的注释,只有编到它的包含者才看得见;
历史「0 错误 0 警告」不代表现在这份是干净的(与
bom-guard完全同形的坑)。 - 按包含者数量刷屏:一处嵌套写在被广泛 include 的头里,每个包含者各打一行。 本仓实测一例扩散到 16 条警告,把同批 10 条真警告挤到看不见—— 等于把「能不能看清报错」这件事本身弄坏。
- 本闸不需要构建:提交时就报,不必等 /keil-build,也不必去猜是谁的编译单元。
2.1 接线当日实测(都可复现)
| 测项 | 结果 |
|---|---|
全仓 --all 实扫 |
403 个 .c/.h:新发 0、存量 2 处(都进基线,见 §7),退出码 0 |
| 形态电池(10 项,逐条对齐期望命中数) | 10/10:5 个真形态全照出,5 个不该报的全安静 |
--selftest 四项 |
4/4:正例照得出 / 反例不误报 / 基线吞得下 / 只减不增有牙 |
| 真字节回放(空基线) | 把 §7 那两份假件头当「今天新提交」跑 → 两条 [NEW]、退出码 1 |
| 同一批真字节 + 已登记基线 | 两条 [BASE]、退出码 0(基线只决定「算不算新的」,不决定「有没有」) |
| 形态对照(重建当日那一处) | 尖括号写法 → 0 命中;同一段退回星号形态 → 1 命中,列 29 |
| 已提交历史回放 | 近 150 个提交里被改过的 .c/.h 共 281 份旧字节:0 命中 ⇒ 这类问题此前从没被机器拦过,全靠人工在编译后清 |
编译器侧原文(冻结引用)——2026-10-27 02:22 那次 @build 的构建日志:
..\App\algo\run_par.c(267): warning: #9-D: nested comment is not allowed⚠ 该日志每次 /keil-build 都被覆盖、且不入库(02:37 已被下一次构建清掉,我写完这段再去查就没了), 所以把原文抄在这里存档;当日那三处同类没有一处在已提交历史里留下字节(上表最后一行), 本闸真正第一次拦住,会是「下一次有人在注释里写星号形态」。
3. 触发面
钩子用 types: [c](即 .c/.h),第三方与生成物由配置顶层 exclude 挡掉。
脚本的 --all 与钩子喂进来的文件集刻意同口径(2026-10-27 实测 403 个文件)——
手跑面与提交面不一致,就会变成「两个数」,本仓已经为这种事返工过好几轮。
纯文档(.md)或纯 Python 的提交不会命中本闸——types: [c] 只喂 .c/.h 两类文件,想确认全仓干净就手跑本节的自查命令。
单独自查:& .venv\Scripts\python.exe scripts/check_nested_comment.py --all
4. 报错怎么读
[NEW ] App/x/y.c:45:17 块注释体内出现 /*(出现 1 次同形)
上下文:件才能测。 * * ⚠ 仅供 test/host/** 使用,不参与 MCU 构建。 * * 为
修法:注释里别写 /**(C 块注释不嵌套)——路径写全目录就好(Drivers/BSP/UI/),
或按本仓既有做法用尖括号包住(<Drivers/BSP/UI/ui_x.c>)
[NEW ]= 基线之外的新命中 → 拦提交(退出码 1)。[BASE](要加--verbose才逐条打印)= 存量在册,不拦(见 §7)。[TIGHTEN]= 某条基线本轮实测已还清,该删了。- 退出码:0 通过或只剩存量;1 有新命中;2 用法或环境错误。
5. 好处
- 秒级、零第三方依赖(只用标准库)。这点是刻意的:
generated-fresh那天栽在 「提交环境里裸 python 不是 .venv、装不到 fontTools」,本闸不碰第三方库,任何解释器 都跑得动——不会再造一根依赖某台机器环境的闸。 - 把「注释里写路径」这种看起来人畜无害的写法变成机器可判,而不是靠 reviewer 眼力。
6. 限制与盲区
- 只管「块注释体内出现 /*」这一件事。不判注释是否闭合(未闭合是编译错误,编译器自己 会拦,不需要一根只读闸替它操心);不判行注释里的 /*(编译器也不报)。
- 不做预处理:若把 /* 藏进宏展开里(本仓没有这种写法),本闸照不到。
- 基线指纹按内容不按行号——存量那几行改了行号仍算在册;反过来,同一文件里换一个新 形态会算新发(这条有专门的自测项)。
- 只覆盖 .c/.h。Python 生成器模板将来若写出 /,本闸照得到产物**(产物是 .c/.h),
但不会指出模板——那段链由
generated-fresh保证「脚本与产物同一笔提交」。 - 编译器侧的真实警告仍以
/keil-build为准:本闸只拦「会让 ARMCC 报 9-D 的那一类」, 编译日志里其他诊断一概不管。 - 仓库根探测必须兼容 linked worktree:脚本原先用「
.git是不是目录」定位仓库根,而git worktree出来的任务工作树里.git是一个文件(写着gitdir: …)不是目录 → 本闸在任何任务树里恒 退出码 2「仓库根定位失败」,静默不可用。这不是「放过了真问题」,是根本没跑——比放过更坏, 因为调用方看到红字会以为环境有问题、看到跳过会以为干净。2026-10-29 已把该判据从isdir改成exists(scripts/check_nested_comment.py:342,修复归当日提交)。全仓scripts/只有它这么探根, 其余只读闸在任务树里一直正常。后果要说清楚:门禁修好之前那批录波改动没被它扫过,lead 手工对 改动面逐文件扫了一遍、嵌套命中 0,所以没有放过真问题——但那是人工兜底,不是门禁功劳。 通用教训:门禁脚本按isdir(.git)探根 = 在 worktree 里自杀;新增只读闸要么走git rev-parse --show-toplevel,要么同时接受.git的文件形态。
7. 豁免与基线(只减不增)
接线当日全仓 --all 实扫 403 个 .c/.h:新发 0、存量 2 处,都在主机侧假件头里:
| 文件:行 | 形态 | 为什么今天不报 | 还法 |
|---|---|---|---|
test/host/common/ch395f_fake.h:7 |
注释里写作用域 test/host/** |
该假件不参与 MCU 构建(换 GCC/clang 的 -Wcomment 就会报) | 改成 test/host 目录 这类语义短语 |
test/host/common/fatfs_fake.h:9 |
同上形态 | 同上 | 同上 |
两处都属 test/ 写域,接线时不由本道代改,按既有范式钉进脚本内 BASELINE,
除这两处外零豁免。一旦有人把这两份假件 include 进板端编译单元,就会按包含者数量刷出
9-D——这正是基线要显式挂着的原因:不是「没问题」,是「已知待还」。
还清一条就删那一行(--prune 打台账并点名已还清的)。
8. 改这套门禁本身
- 加判定形态必须同时加正例与反例:
--selftest四项(正例照得出 / 反例不误报 / 基线吞得下 / 只减不增有牙)之外,另有 10 项形态电池逐条对齐期望命中数。 - ⚠ 配置文件注释里不要复述声明行的字面量:接线当天我在
.pre-commit-config.yaml的 注释里写了一遍那个 grep 模式,文本 grep 的计数当场虚高 1(口径与自查命令见docs/门禁说明/README.md§2 首条)。数数交给 YAML 解析,注释只描述。 - 加/删一条钩子要同批改三处:AGENTS.md 的钩子数、本目录 §2 标题、配置声明,外加索引行。