# 块注释嵌套(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 = "/* 不是注释 */";`) | | **不算** | 行注释里写 /*(编译器不报,本闸也不报) | | **不算** | 本仓既有的尖括号写法 ``(**推荐改法**) | ## 2. 为什么要它 - **只在重编译那个 TU 时才暴露**:改了某个头里的注释,只有编到它的包含者才看得见; 历史「0 错误 0 警告」不代表现在这份是干净的(与 `bom-guard` 完全同形的坑)。 - **按包含者数量刷屏**:一处嵌套写在被广泛 include 的头里,每个包含者各打一行。 本仓实测一例扩散到 **16 条警告**,把同批 10 条**真**警告挤到看不见—— 等于把「能不能看清报错」这件事本身弄坏。 - 本闸**不需要构建**:提交时就报,不必等 @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 > ``` > > ⚠ 该日志每次 @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/), 或按本仓既有做法用尖括号包住() ``` - `[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. 编译器侧的真实警告仍以 `@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 标题、配置声明,外加索引行。