refactor(gate): bsp-err 四项检查改用自描述标签,废掉 R1~R4 这组撞车代号

纯改名,检查行为一个字没动:规则逻辑、阈值、豁免形状全部原样。

改名原因(用户一句「R4 到底是什么」问出来的):R1~R4 是我在 check_bsp_err.py 注释里
自造的序号,而这个裸形在本仓另有含义——R1~R8 已在 scripts/id_registry.json 登记为
"专项风险"的文档内编号(保护规划与看门狗的风险表都在用),带连字符的那一族又是审查
报告的可靠性 / 内存优化项编号(编号规范 §1 的"连字符承载语义"一行正是这么写的)。
**同一个裸形四种意思**。最刺人的一点:编号门禁**不会报**这种撞法,因为编号规范 §4.3
已把这些裸形声明为"同号不同义"、走 collides_with 白名单,校验 4 不计事故——同一天里
我起的另一组自造号被当场拦下、这组代号却一路绿灯,差别不在我做对了什么,而在白名单
挡的是"事故"、不是"可读性"。

新名同时是基线键的第二字段(格式 文件|标签|名字):
· err-type        错误通道不外泄(对外接口不拿 HAL_StatusTypeDef / bool 当成败)
· timeout-name    超时宏不取负值
· return-doc      公开函数必须写「返回值:」
· consume-result  返回值不得裸丢弃(须接住或显式 (void))
老名对照:R1=err-type、R2=timeout-name、R3=return-doc、R4=consume-result。

同步面:脚本里的规则码与 38 条基线键的字段**一起换**(中途不跑 --prune,否则改名会被
读成"38 条全还清 + 38 条新账"),换完 --emit-baseline 复核仍是 38/38/0、RC=0。现状类
文本全部换新名:AGENTS.md、pre-commit使用说明.md §2.27(新增"检查项叫什么"一行含老新
对照与撞号说明)、bsp_err.h 边界纪律、报告 §0.4 / §9 / 附录 A,以及代码注释里的 5 处
(gd5f2gq5ue.c/.h、nand_ftl.h、ui_menu_tab.h)。已提交的变更记录行里的老名按"冻结历史
不回改"处理,以 §2.27 对照行为准。ui_menu_tab.h 带 BOM,Edit 又剥掉、已按原样补回。

写说明文字时又被编号门禁拦了一次:我为了举例"老名撞车",在那段话里写了两个编号字面,
被判"未紧邻文档名 / 未登记前缀"共 3 类新账——连批评自造编号的段落本身都在自造编号。
改法是不写号、只描述形态并指向权威条目。

验证:@build **0 错 0 警告**,Program Size 与上一笔**逐字段相同**(Code 213,676、RO
18,860、RW 1,188、ZI 131,500)→ 证明确为注释与文案,故不跑 @test(无指令变化);
pre-commit run 全绿 **17 项 Passed**;check_doc_links --all --sections-strict 147
  篇 0
问题(中途我自己写的 §4.3 没紧邻文档名,被判成本文自引用,已补全路径);9 个文件字节
卫生 0 异常(UI 头 BOM 仍在、无 CR、无 U+FFFD、均有尾换行)。
This commit is contained in:
2026-10-23 09:36:01 +08:00
parent d9b887c0af
commit 2575177e81
9 changed files with 102 additions and 71 deletions
+15 -7
View File
@@ -487,33 +487,33 @@ cppcheck 是开源 C/C++ 静态分析工具,**不运行代码**,通过语法
| **约定正文在哪** | `Drivers/BSP/bsp_err.h`(**header-only,无 `.c`** → 不占体积、不需双端注册)。
`0 = BSP_OK`、负值失败、`BSP_ERR_CAN_RETRY(e)` 把"能不能重试"变成跨模块可判定(这是本契约
真正值钱的一条:以前"可重试"只写在个别注释里) |
| **四条规则** | **R1** 公开 API 不得用 `HAL_StatusTypeDef` / `bool` 当错误通道(`is_/has_/can_/valid`
| **四条检查** | **`err-type`**(错误通道不外泄)公开 API 不得用 `HAL_StatusTypeDef` / `bool` 当错误通道(`is_/has_/can_/valid`
这类**谓词**白名单放行——第一版误判了 7 个正当谓词,我们把规则改窄,**不拿基线去洗规则**);
**R2** `*_TIMEOUT` 宏不得取负值(名字叫 TIMEOUT 却是返回码 → 一律改 `BSP_ERR_TIMEOUT`);
**R3** 非 void 的公开 API 必须有「返回值:」注释(约定要能被读到才谈得上审查;匹配时**忽略空格**,
**`timeout-name`**(超时宏不取负值)`*_TIMEOUT` 宏不得取负值(名字叫 TIMEOUT 却是返回码 → 一律改 `BSP_ERR_TIMEOUT`);
**`return-doc`**(公开函数必须写返回值)非 void 的公开 API 必须有「返回值:」注释(约定要能被读到才谈得上审查;匹配时**忽略空格**,
因为规范 V1.0 允许列对齐写法「返 回 值:」——门禁去规定排版会把合规注释判成违规,实测就有两条因为
**函数说明**里顺口出现"返回值"三个字而误判通过);
**R4** 返回错误码的函数,调用点必须**接住返回值**或**显式 `(void)` 丢弃**(裸调用即违规)。must-use
**`consume-result`**(返回值不得裸丢弃)返回错误码的函数,调用点必须**接住返回值**或**显式 `(void)` 丢弃**(裸调用即违规)。must-use
名单**自动取**、不手写:① 返回 `bsp_err_t` 的公开 API;② 返回 `int`/`int32_t` 且文档块出现"错误码 /
失败 / BSP_ERR / `_FAIL`"的函数;③ 已知返回 `HAL_StatusTypeDef` 的 HAL 传输/读写族。**名单必须同时
覆盖 `.c` 里的 static 定义**——只扫头文件时 `gd5f_set_feature` 这类 static 原语在名单之外,等于只守
跨编译单元的边界、看不见编译单元内部的丢弃(实测漏掉后靠人工才发现一处 ECC 丢弃)。豁免两类形状:
**谓词**(`is_/has_/can_/should_/need_/valid_`)与 **setter**(`xxx_set_yyy`,实跑误判出 `net_set_errno`
八处)——同 R1 的原则:**规则按真实反例收窄,不拿基线洗假阳性**。已知限度:只认**单行整句**的裸调用
八处)——同 `err-type` 的原则:**规则按真实反例收窄,不拿基线洗假阳性**。已知限度:只认**单行整句**的裸调用
(`foo(...);` 一行写完,行尾注释允许),跨行写的调用暂测不到 |
| **范围** | 只扫 `git ls-files` 里 `Drivers/BSP/**/*.h`(对外 API 面);**lftpd 按第三方排除**——
它的 `226/425/450/550` 是 FTP 协议码不是内部错误码;**`net_*` BSD 兼容层不纳入**(用户裁定
2026-10-23):调用方要的就是"返回字节数 + 每任务 errno 槽"那个形状,模拟器的 Winsock
后端也照同一形状实现 |
| **增量拦截** | 存量 **38 条**钉 `BASELINE`(首清点 19,已还 18(首清点 19 条:2 个 `*_BUSY_TIMEOUT = -2` + 16 个缺「返回值:」+ 1 个 bool;**现仅剩
`bool ui_content_page_open()` 一条**——它需要改签名并处理 3 个调用点,与"还注释"不是一档事)。**R4 是同日新加的一条,上线即暴露 37 条存量丢弃**(`gd5f2gq5ue.c` 29 处:14 处
`bool ui_content_page_open()` 一条**——它需要改签名并处理 3 个调用点,与"还注释"不是一档事)。**`consume-result` 是同日新加的一条,上线即暴露 37 条存量丢弃**(`gd5f2gq5ue.c` 29 处:14 处
丢弃 `HAL_SPI_*` 状态 + 15 处丢弃自家原语;`NET/net_socket.c` 8、`UI/ui_net_sink.c` 4 处 `net_close`、
`RS485/rs485.c` 2、`nand_ftl.c` 1)——这些是**覆盖面扩大而新暴露的历史债**,按增量拦截的老规矩入基线
(不算"新增违规"),修一条删一行。**只减不增**、脚本没有 `--write`;修完跑 `--prune` 会点名"这条已消失,
请从基线删除" |
| **配置** | `files: ^Drivers/BSP/\|^scripts/check_bsp_err\.py$\|^\.pre-commit-config\.yaml$`、
`pass_filenames: false`(恒扫全库,要按 git 清单建公开头索引) |
| **为什么 R4 只能自己做**(两条实测负结果) | ① `__attribute__((warn_unused_result))` **在 ARMCC v5 上
| **为什么 `consume-result` 只能自己做**(两条实测负结果) | ① `__attribute__((warn_unused_result))` **在 ARMCC v5 上
不生效**:属性挂上、调用点裸调不接返回值,构建仍 0 错 0 警告。② **cppcheck 也照不出来**:按本仓自己的
`--enable=warning,style,performance,portability` 跑 `gd5f2gq5ue.c`,对 14 处丢弃 `HAL_SPI_*` 返回值、以及
"未初始化局部量直接进 `P_FAIL` 判定"都**一条不报**(它假定 `gd5f_read_status(&status)` 会写出参,而失败
@@ -521,6 +521,14 @@ cppcheck 是开源 C/C++ 静态分析工具,**不运行代码**,通过语法
必须写 `(void)`";评审时看到既没 `(void)` 也没接住的调用就是漏判嫌疑 |
| **踩坑提醒** | pre-commit 会 **stash 未暂存改动**再跑钩子。所以"改代码 + 同时更新基线"要在
一次提交里**把改动 `git add` 了再跑**,否则钩子看到的是没有你改动的树,会假报警 |
| **检查项叫什么** | 四项用自描述标签(就是基线键的第二字段,格式 `文件|标签|名字`):
`err-type` 错误通道不外泄、`timeout-name` 超时宏不取负值、`return-doc` 公开函数必须写返回值、
`consume-result` 返回值不得裸丢弃。**2026-10-23 之前这四项叫 R1/R2/R3/R4,已整体改名**——
`R1~R8` 在本仓是"专项风险"的裸形编号(`scripts/id_registry.json` 已登记该族,保护规划与看门狗
的风险表都在用)、**带连字符的那一族是审查报告的可靠性 / 内存优化项编号**(见同一裸形四种意思;而编号门禁因为 `docs/编号规范.md` §4.3 已把这些裸形声明为
"同号不同义"、走 `collides_with` 白名单,**所以这种撞法不会报**(§1 的"连字符承载语义"
一行正是这么写的),等于机器放行、人看不懂(对照:`R1`=err-type、`R2`=timeout-name、
| `R3`=return-doc、`R4`=consume-result;已提交的变更记录里出现的老名按冻结历史不回改)。 |
| **运行器** | `scripts/check_bsp_err.py`(纯标准库;`--verbose` 列全部命中归类、`--prune` 只报
可删基线、`--emit-baseline` 建基线用;退出码 0/1/2) |
@@ -55,7 +55,7 @@
`net_msg_queue_adopt()` 绑定(创建点不能外移——`osKernelInitialize()` 之前的创建类 API 会杀死启动;**故意不新建第二个队列**:那会白占 CCM 里 8×`net_msg_t`
的堆空间,而 CCM 正是当前红线)。**两类从来不算债**:`stm32f4xx_it.c:241-256` 是 CubeMX「External variables」生成块;`:183/195/207/220` 是 `__asm` Handler
里的汇编标签引用(ARMCC `__asm` 取不到 C 数据符号)。 | 无(后续由 `layer-deps` 与 `cubemx-survival` 两条门禁守着不回退:新增 `net-msgq-adopt` 存活项) |
| 驱动规范 | **S-5 🟡 主体已落地**(2026-10-23:契约 + 门禁 + RS485/SD2506/TPAFE 收编 + GD5F 改名 + dhara 三类码) | 我原先写的"无统一错误码"**不准**,实测是 **六套并存**:0/−1(SD2506、NETCFG、nand_ftl)、自研枚举 `CH395F_STATUS_*`、**芯片寄存器原生码** `CH395F_ERR_*`(与前一套同模块并存)、`HAL_StatusTypeDef`(`rs485.h` 曾直接暴露)、`bool`,外加第三方 dhara 出参 `dhara_error_t` 与 FatFs `FRESULT`。超时侧是**同形两义**:`SD2506_I2C_TIMEOUT_MS` 带单位后缀而 `GD5F_SPI_TIMEOUT 100` 不带(**逐行核过:它也是毫秒**,我先前记成"自旋圈数"是错的)、`GD5F/TPAFE5160_BUSY_TIMEOUT = -2` 其实是**返回码**;`osWaitForever` 与 `HAL_MAX_DELAY` **同为 0xFFFFFFFFU**(值同域不同,跨层转手不报错也不变慢)。真正值钱的不是码名,是"**能不能重试**"以前只写在个别注释里 | `Drivers/BSP/bsp_err.h`(header-only,含 `BSP_ERR_CAN_RETRY`)+ `bsp-err` 钩子(**四条规则**,基线 19 → 1 → **38**:R4 上线又暴露 37 条存量丢弃);已收编 **RS485(HAL 枚举不再外泄)/ SD2506(11 个公共 API,I2C 超时终于归成可重试)/ TPAFE5160(含 E-44 的 FRSTDATA → `BSP_ERR_NO_DEVICE`)**,GD5F 的 `GD5F_BUSY_TIMEOUT` 改名 `GD5F_WAIT_EXPIRED` 消掉同形两义,FTL 坍缩点按 **ECC / 预算耗尽 / 完整性** 三类就地分类并打可读日志(§10.4)。`net_*` BSD 层与 `tpafe5160_snapshot()` 三态按裁定**不纳入**。**已修第一处静默降级**(2026-10-23):`gd5f2gq5ue_bbt_rebuild()` 的 ECC 关闭/恢复不再裸丢弃,并补 E-10 同款**回读
| 驱动规范 | **S-5 🟡 主体已落地**(2026-10-23:契约 + 门禁 + RS485/SD2506/TPAFE 收编 + GD5F 改名 + dhara 三类码) | 我原先写的"无统一错误码"**不准**,实测是 **六套并存**:0/−1(SD2506、NETCFG、nand_ftl)、自研枚举 `CH395F_STATUS_*`、**芯片寄存器原生码** `CH395F_ERR_*`(与前一套同模块并存)、`HAL_StatusTypeDef`(`rs485.h` 曾直接暴露)、`bool`,外加第三方 dhara 出参 `dhara_error_t` 与 FatFs `FRESULT`。超时侧是**同形两义**:`SD2506_I2C_TIMEOUT_MS` 带单位后缀而 `GD5F_SPI_TIMEOUT 100` 不带(**逐行核过:它也是毫秒**,我先前记成"自旋圈数"是错的)、`GD5F/TPAFE5160_BUSY_TIMEOUT = -2` 其实是**返回码**;`osWaitForever` 与 `HAL_MAX_DELAY` **同为 0xFFFFFFFFU**(值同域不同,跨层转手不报错也不变慢)。真正值钱的不是码名,是"**能不能重试**"以前只写在个别注释里 | `Drivers/BSP/bsp_err.h`(header-only,含 `BSP_ERR_CAN_RETRY`)+ `bsp-err` 钩子(**四条检查**:`err-type` / `timeout-name` / `return-doc` / `consume-result`;基线 19 → 1 → **38**,其中 `consume-result` 上线又暴露 37 条存量丢弃);已收编 **RS485(HAL 枚举不再外泄)/ SD2506(11 个公共 API,I2C 超时终于归成可重试)/ TPAFE5160(含 E-44 的 FRSTDATA → `BSP_ERR_NO_DEVICE`)**,GD5F 的 `GD5F_BUSY_TIMEOUT` 改名 `GD5F_WAIT_EXPIRED` 消掉同形两义,FTL 坍缩点按 **ECC / 预算耗尽 / 完整性** 三类就地分类并打可读日志(§10.4)。`net_*` BSD 层与 `tpafe5160_snapshot()` 三态按裁定**不纳入**。**已修第一处静默降级**(2026-10-23):`gd5f2gq5ue_bbt_rebuild()` 的 ECC 关闭/恢复不再裸丢弃,并补 E-10 同款**回读
B0h 验 `ECC_EN`**——原先那两行的注释自己写着"否则后续 I/O 在无 ECC 下运行",风险却正落在它下面那行。
**待还**:GD5F 私有码集整体收编(169 处、含板级套件 48 处,需真机 + 套件回归)、C4 失败类快照(让卷
健康探针按类分支)、`bool ui_content_page_open()` 一条(需改签名 + 处理 3 个调用点,与还注释不是一档事)、
@@ -330,7 +330,7 @@ W6 簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止
| 波次 | 本报告条目(今天状态) | 备注 |
|---|---|---|
| ~~W3 预算与内存~~ ⏸ | R-9 ✅ / R-2 后半、R-3、R-4、R-6 暂缓 | 暂缓期间无验收动作,观测照常 |
| W4 结构债 | N-14 ⏸、F-5 🟡、S-1/S-2/S-4/S-5、N-25 | `--strict` 已默认;S-5 契约/门禁已落地并收编三模块,R4 已把"丢弃返回值"纳入机械检查(基线 19→1→38;剩 GD5F 码集收编、C4 快照、37 条丢弃点、标坏策略) |
| W4 结构债 | N-14 ⏸、F-5 🟡、S-1/S-2/S-4/S-5、N-25 | `--strict` 已默认;S-5 契约/门禁已落地并收编三模块,`consume-result` 已把"丢弃返回值"纳入机械检查(基线 19→1→38;剩 GD5F 码集收编、C4 快照、37 条丢弃点、标坏策略) |
| W5 验证常态 | 覆盖率、`malloc` 正则、TC 差异 13 处升门禁 | 逢窗口顺手做 |
| W6 UI 扩展 | U2 残余、U5 页量 | 随产品需求,不再单列专项 |
| W7 仓库决策 | D-1(N-29) | 已决策暂不处理;对外交付前重评 |
@@ -389,7 +389,7 @@ W6 簇按 U4 → U1 → U3 → U2 的顺序执行完毕(U2 停在"够用即止
| **S-2** | 跨模块裸 `extern` | ✅ **已闭环 2026-10-22**(24 处手写全消;两类本就不算债见 §0.4) | 本文变更记录 2026-10-22 三行;契约见 `App/task/task_handles.h`、`gd5f_spi1_notify_*`、`net_msg_queue_adopt` |
| **S-4** | `app_cfg.h` 未接线占位开关 | ✅ **已闭环 2026-10-22**(4 个零消费点宏删除) | `App/app/app_cfg.h` 修改记录 v1.2;口径同步见 `../环境与部署/项目部署环境要求.md` §3.2/§3.5、`../产品需求/产品需求设计文档.md`、`../任务规划/排期详述.md` C5 |
| **S-3** | `.ioc` 侧 USART1 配置 | ❌ 在策 | 登记簿 S-3 |
| **S-5** | 统一 `bsp_err_t` 错误码与超时命名约定 + **返回值不得裸丢弃** | 🟡 主体已落地(2026-10-23:契约 + `bsp-err` 四规则 + RS485/SD2506/TPAFE 收编 + GD5F 改名 + dhara 三类码 + R4 照出 37 条丢弃 + ECC 回读校验;基线 19 → 1 → 38) | 本文 §0.4、`pre-commit使用说明.md` §2.27、`STM32F4-Base存储架构说明.md` §10.4、`Drivers/BSP/bsp_err.h` |
| **S-5** | 统一 `bsp_err_t` 错误码与超时命名约定 + **返回值不得裸丢弃** | 🟡 主体已落地(2026-10-23:契约 + `bsp-err` 四规则 + RS485/SD2506/TPAFE 收编 + GD5F 改名 + dhara 三类码 + `consume-result` 照出 37 条丢弃 + ECC 回读校验;基线 19 → 1 → 38) | 本文 §0.4、`pre-commit使用说明.md` §2.27、`STM32F4-Base存储架构说明.md` §10.4、`Drivers/BSP/bsp_err.h` |
| R-1 ~ R-9 | 内存优化项(R-9 已落地、R-5 判定不可行、其余随簇暂缓) | 见 §5.3 | 本文 §5.3、`../内存管理/RAM预算台账.md` |
| F-1 / F-2 / F-3 | LVGL 死代码裁剪,省 64,576 B | ✅ 已落地 2026-10-11 | `../内存管理/RAM使用分析与优化方案.md` §2.6.4 |
| **F-4 / F-5 / F-6** | Flash 双精度与死代码簇 | ✅/🟡/❌ 见 §5.4 | 本文 §5.4、`App/algo/prot_set.h` 注释 |
@@ -448,6 +448,20 @@ admin/admin(SHA-256 `pwd_hash`)→ 该脚本**必然登录失败**;它是
| | 数字联动照做:两份 `data/` 快照重跑、`ram_baseline --update/--write`、`check_mem_numbers --write`、FLASH 与
任务栈两张配图重算重生成(逐文本复查越界 = 0)、本文 §1.3/§5.1/§5.4 与部署文档 §3.3 同步;§0.4 的 S-2 行按
「完成即销项」改写、§9 拆出 S-2 行标 ✅。 |
| **2026-10-23** | **术语迁移**:`bsp-err` 的四项检查从 `R1/R2/R3/R4` 改成语义标签 | 用户一句「R4
| | 到底是什么」问出来的。这四个号**是我自己造的**,而 `R1~R8` 在本仓是"专项风险"的裸形编号
| | (`id_registry.json` 已登记那一族,保护规划 `R1~R8`、看门狗 `R1~R7` 的风险表都在用),`R-2`
| | 还是内存优化项——**同一裸形四种意思**。最刺人的是:**编号门禁不会报这种撞法**,因为
| | `docs/编号规范.md` §4.3 已把这些裸形声明为"同号不同义"、走 `collides_with` 白名单,于是机器一路
| | 放行、人完全看不懂。
| | 同一天里我给这批缺陷起的"字母加数字"自造号被门禁当场拦下,而这套代号却一路绿灯——说明白名单
| | 挡的是"事故"、不是"可读性"。
| | 新名(同时是基线键的第二字段):`err-type` 错误通道不外泄 / `timeout-name` 超时宏不取负值 /
| | `return-doc` 公开函数必须写返回值 / `consume-result` 返回值不得裸丢弃。改名**不动任何检查行为**:
| | 脚本里规则码与基线键一起换(38 条键的字段同步替换,换完 `--emit-baseline` 复核仍是 38/38/0,
| | 中途不跑 `--prune`,否则会把改名误读成"全部还清 + 全部新账");现状类文档(AGENTS、§2.27、
| | `bsp_err.h`、本报告 §0.4/§9/附录 A)全部改用新名,**已提交的变更记录行里的老名按冻结历史不回改**,
| | 以 `docs/代码规范/pre-commit使用说明.md` §2.27 的对照行为准。 |
| **2026-10-23** | **S-5 第三波**:给 `bsp-err` 加 **R4(返回值不得裸丢弃)**,并修掉它照出来的第一处
| | ECC 静默降级 | 用户裁定"B 按你说的进行"(先上 R4,再修最小最值的那处)。R4 之所以必须自己做,是两条实测
| | 负结果:`warn_unused_result` 在 ARMCC v5 不生效;cppcheck 按本仓 `--enable` 跑同一文件,对 14 处丢弃