Files
STM32F4-Base/docs/门禁说明/nested-comment.md
T
wandering1 4a4ad6bc5f chore(全仓): @build/@test 旧文档别名清退为技能调用名
现状口径:/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 全过;字节卫生零漂移。
2026-10-30 13:03:12 +08:00

8.7 KiB
Raw Blame History

块注释嵌套(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. 限制与盲区

  1. 只管「块注释体内出现 /*」这一件事。不判注释是否闭合(未闭合是编译错误,编译器自己 会拦,不需要一根只读闸替它操心);不判行注释里的 /*(编译器也不报)。
  2. 不做预处理:若把 /* 藏进宏展开里(本仓没有这种写法),本闸照不到。
  3. 基线指纹按内容不按行号——存量那几行改了行号仍算在册;反过来,同一文件里换一个新 形态会算新发(这条有专门的自测项)。
  4. 只覆盖 .c/.h。Python 生成器模板将来若写出 /,本闸照得到产物**(产物是 .c/.h), 但不会指出模板——那段链由 generated-fresh 保证「脚本与产物同一笔提交」。
  5. 编译器侧的真实警告仍以 /keil-build 为准:本闸只拦「会让 ARMCC 报 9-D 的那一类」, 编译日志里其他诊断一概不管。
  6. 仓库根探测必须兼容 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 标题、配置声明,外加索引行。