From 2e1d832365d4094607f18395a2c4e708f2daee17 Mon Sep 17 00:00:00 2001 From: Wanderingss <1624155937@qq.com> Date: Tue, 1 Sep 2026 14:51:30 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8B=86=E5=88=86cppcheck=E8=AF=A6=E8=A7=A3?= =?UTF-8?q?=E5=88=B0=E7=8B=AC=E7=AB=8B=E6=96=87=E6=A1=A3=EF=BC=8C=E7=B2=BE?= =?UTF-8?q?=E7=AE=80pre-commit=E4=BD=BF=E7=94=A8=E8=AF=B4=E6=98=8E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit docs/cppcheck使用说明.md: - 新增:style 95 项规则详解、配置参数、YAML 引号陷阱、 误报抑制方法、问题处理记录 docs/pre-commit使用说明.md: - 2.6 节精简为概览表 + 指向新文档 - 更新 3.2 失败处理表与文件清单 --- docs/cppcheck使用说明.md | 238 +++++++++++++++++++++++++++++++++++++ docs/pre-commit使用说明.md | 211 ++------------------------------ 2 files changed, 248 insertions(+), 201 deletions(-) create mode 100644 docs/cppcheck使用说明.md diff --git a/docs/cppcheck使用说明.md b/docs/cppcheck使用说明.md new file mode 100644 index 00000000..8b59c07a --- /dev/null +++ b/docs/cppcheck使用说明.md @@ -0,0 +1,238 @@ +# cppcheck C 静态分析 — 使用说明 + +> 本文档是 `docs/pre-commit使用说明.md` 的配套详解,聚焦 cppcheck 钩子的检查规则、配置参数与误报抑制方法。 +> pre-commit 的整体部署架构与其他钩子规则见 `docs/pre-commit使用说明.md`。 + +--- + +## 1. 概述 + +cppcheck 是开源 C/C++ 静态分析工具,**不运行代码**,通过语法分析和数据流推理发现潜在缺陷。 + +**工作原理**:逐个读取 C 文件 → 分析语法/数据流/控制流 → 发现问题则报错。 + +**与 pre-commit 的关系**:`pre-commit` 阶段的 `cppcheck` 钩子调用 cppcheck 命令行,任一已启用类别发现问题即**阻止提交**(`--error-exitcode=1` 对 warning/performance/portability/style 全部生效)。 + +### 日常速度 + +- **增量扫描**(`git commit` 时):只检查 staged 文件,单文件约 1~2 秒 +- **全库扫描**(`pre-commit run --all-files`):约 5 秒,用于配置变更后验证 + +--- + +## 2. 检查类别 + +### 本项目开启的类别 + +| 类别 | 含义 | 典型问题 | +|---|---|---| +| `warning` | 通用警告 | 变量未使用、除零风险、空指针解引用、unreachable code | +| `performance` | 性能问题 | 不必要的值拷贝、字符串重复查找、低效的函数调用 | +| `portability` | 可移植性问题 | 不同平台行为不一致的代码、隐式类型转换 | +| `style` | 代码风格 | 变量作用域过大、冗余赋值、恒假条件、自赋值等可疑写法(详见第 3 节)| + +### 不开启的常见类别 + +| 类别 | 为什么不开 | +|---|---| +| `error` | 严重错误(数组越界、内存泄漏),cppcheck 默认已包含,不需要显式加 | +| `information` | 信息类(未解析的头文件等),无实际价值 | + +--- + +## 3. style 类别具体检查什么(cppcheck 2.21.0) + +`style` 是"风格/质量"级别——报告的问题**不一定是 bug**,但绝大多数是**冗余代码、死代码、可疑写法、可以写得更安全/更清晰的代码**。下面按主题列出本项目(纯 C)实际会命中的规则(`--enable=style` 已开启): + +**① 冗余与死代码(redundant & dead code)— 最值得修** + +| 规则 | 检查什么 | 反例 | +|---|---|---| +| `redundantAssignment` | 变量赋值后被新值覆盖、旧值从未被读取 | `ret = OK; ret = f();`(首个赋值无意义)| +| `unreadVariable` | 变量赋值后从未被读取 | `s = -1;` 随后循环里被覆盖、从未用到 | +| `redundantCondition` | 短路求值使某条件恒真 | `if (a && a > 5)`(a 非零时前半已恒真)| +| `duplicateCondition` | if/else 分支条件重复 | `if (x){} else if (x){}` | +| `duplicateBreak` | 连续重复 break | `break; break;` | +| `duplicateExpression` / `duplicateExpressionTernary` | 运算符两侧表达式相同 | `a = a && b`、`x = x ? 1 : 2` | +| `unreachableCode` | return 之后不会执行的代码 | `return 0; foo();` | +| `unassignedVariable` | 变量声明后从未赋值 | `int x; use(x);` | +| `unusedLabel` | 从未被 goto 的标签 | `label:` 无对应 goto | + +**② 恒真/恒假条件(always-true / always-false)— 常藏逻辑 Bug** + +| 规则 | 检查什么 | 反例 | +|---|---|---| +| `knownConditionTrueFalse` | 依据数据流推断条件恒真/恒假 | `while (n>=365){ ... if (n<360) break; }` | +| `knownArgument` | 参数固定时函数结果可推断 | `strlen("")` 恒为 0 | +| `knownPointerToBool` | 已知指针当布尔用 | p 恒非空时 `if (p)` | +| `compareValueOutOfTypeRangeError` | 与类型范围外的值比较 | `uint16_t x; if (x > 65535)` 恒假 | +| `unsignedLessThanZero` | 无符号 < 0 恒假 | 无符号 `n` 的 `if (n < 0)` | +| `unsignedPositive` | 无符号 >= 0 / > -1 恒真 | `if (n >= 0)` | +| `pointerLessThanZero` / `pointerPositive` | 指针 < 0 / > 0 | `if (p < 0)` | + +**③ const 化与作用域(const & scope)** + +| 规则 | 检查什么 | 备注 | +|---|---|---| +| `constVariable` / `constVariablePointer` | 变量/指针可加 const 防误改 | `constVariablePointer` 当前**已抑制**(改动量大)| +| `constParameter` / `constParameterPointer` / `constParameterCallback` | 参数可加 const | 回调相关两条**已抑制**(HAL 签名不可改)| +| `variableScope` | 变量作用域可缩小(声明移入实际使用的最内层块)| 已全库修复 | +| `functionStatic` / `staticFunction` | 函数可加 static 限制可见性 | 本库函数多被外部引用,命中少 | + +**④ 可疑写法(suspicious patterns)— 疑似笔误** + +| 规则 | 检查什么 | +|---|---| +| `clarifyCalculation` | 位运算与逻辑运算易混(`|` vs `\|\|`、`&` vs `&&`)| +| `clarifyCondition` / `assignmentInCondition` | 条件里出现赋值(疑似 `=` 误写 `==`)、链式比较 | +| `bitwiseOnBoolean` | 对布尔量做位运算 | +| `selfAssignment` | 自赋值 `x = x` | +| `oppositeExpression` | 同一条件内互相矛盾的表达式 | +| `moduloofone` | `x % 1`(恒为 0)| +| `mismatchingBitAnd` | 位运算不匹配 | +| `suspiciousFloatingPointCast` | 浮点强转整型(可能丢精度)| +| `incrementboolean` | 对布尔量自增 | +| `shadowVariable` / `shadowArgument` / `shadowFunction` | 局部变量遮蔽外层变量/参数/函数 | +| `arrayIndexThenCheck` | 数组下标先使用、后越界检查 | +| `truncLongCastAssignment` / `truncLongCastReturn` | long 强转导致截断 | +| `funcArgNamesDifferent` | 函数参数名与声明不一致 | +| `nanInArithmeticExpression` | 算术表达式中出现 NaN | +| `ignoredReturnErrorCode` | 忽略带错误码的返回值 | +| `unpreciseMathCall` | 调用不精确的数学函数 | +| `returnNonBoolInBooleanFunction` | 布尔函数返回非布尔值 | +| `multiCondition` | 可疑的多条件组合 | +| `duplicateAssignExpression` | 同一表达式赋给多个变量 | +| `redundantBitwiseOperationInSwitch` / `redundantAssignInSwitch` | switch 中冗余位运算/赋值 | + +**⑤ 不适用本 C 工程的规则(C++ 专属,自动跳过)** + +`catchExceptionByValue`、`cstyleCast`、`missingOverride`、`noConstructor`、`noExplicitConstructor`、`operatorEqRetRefThis`、`unusedScopedObject`、`virtualCallInConstructor`、`useStlAlgorithm`、`constParameterReference` 等(异常/类/STL 相关,纯 C 代码不会命中)。 + +> 说明: +> - 上表规则均出自 cppcheck 2.21.0 `--errorlist` 中 `severity=style` 的真实清单。 +> - 部分规则(如 `duplicateBranch`、`suspiciousSemicolon`、`checkConst`)需要额外 `--inconclusive` 才会激活,为减少误报**未开启**。 +> - `unusedFunction`、`unusedStructMember`、`useStlAlgorithm`、`noExplicitConstructor` 等已在 `--suppress` 中抑制,原因见第 4.3 节「抑制规则说明」。 +> - `--error-exitcode=1` 对**所有已启用级别**(warning/performance/portability/style)生效:任何一级发现问题都会**阻止提交**。style 级别的问题若确属误报,用行内抑制(见第 5 节)。 + +--- + +## 4. 配置 + +### 4.1 配置参数 + +``` +--std=c99 # 按 C99 标准检查(Keil 工程常用) +--error-exitcode=1 # 发现问题返回非零退出码(pre-commit 用来拦截提交) +--inline-suppr # 启用行内抑制注释(// cppcheck-suppress,默认关闭) +--quiet # 只报问题,不显示扫描进度 + +# --- 必须抑制(误报/不可修改) --- +--suppress=missingInclude # HAL/CMSIS 路径在 Keil 工程配置里,cppcheck 不知道 +--suppress=invalidPrintfArgType_sint # printf 的 %d 传 uint32_t —— 实际安全 +--suppress=invalidPrintfArgType_uint # printf 的 %u 传 int —— 实际安全 +--suppress=constParameterPointer # HAL 回调签名固定(如 SPI_HandleTypeDef*),不能加 const +--suppress=constParameterCallback # 回调函数指针类型限制,不能改签名 +--suppress=unusedFunction # 第三方库回调/弱符号函数被误报为未使用 +--suppress=unusedStructMember # HAL 寄存器位域成员未显式使用 +--suppress=noExplicitConstructor # C++ 兼容,不影响纯 C +--suppress=useStlAlgorithm # C++ STL 相关,不影响纯 C + +# --- 暂时抑制(噪音多,后续可移除) --- +--suppress=constVariablePointer # 局部指针加 const 是好习惯,但当前改动量大 + +# --- 头文件路径 --- +-I Inc -I App -I Drivers/BSP +-I Drivers/STM32F4xx_HAL_Driver/Inc -I Drivers/CMSIS/Include +-I Middlewares/Third_Party/FreeRTOS/Source/include +``` + +> 注意:以上为逻辑参数列表。在 `.pre-commit-config.yaml` 中书写时必须遵守第 4.2 节的 YAML 引号规则。 + +### 4.2 ⚠️ YAML 参数引号陷阱(曾导致钩子完全失效) + +`.pre-commit-config.yaml` 的 `args:` 使用 YAML **流序列** `[a, b, c]` 语法,其中**逗号是元素分隔符、空格不产生新元素**。曾出现两个致命错误,导致 cppcheck 钩子**空转**("Passed" 但从未真正检查): + +| 错误写法 | 实际被解析成 | 后果 | +|---|---|---| +| `[--enable=warning,performance,portability,style]` | `--enable=warning` + 3 个假文件参数 `performance`/`portability`/`style` | **只启用 warning**,style/performance/portability 全部失效 | +| `[-I Inc -I App -I Drivers/BSP]` | 单个参数 `"-I Inc -I App -I Drivers/BSP"` | 所有 include 路径失效(指向一个不存在的目录名)| + +**正确写法**:含逗号或空格的参数必须加引号,`-I` 与路径必须分成两个元素: + +```yaml +args: ['--enable=warning,performance,portability,style', '--error-exitcode=1', + '-I', 'Inc', '-I', 'App'] +``` + +**排查方法**:怀疑参数被拆坏时,在 PATH 前置一个同名 shim(如 `cppcheck.cmd` 把 `%*` 写日志),跑 `pre-commit run cppcheck --files <某文件>` 查看实际执行的完整命令行。 + +### 4.3 抑制规则说明 + +| 抑制规则 | 原因 | 是否永久 | +|---|---|---| +| `constParameterPointer` | HAL 回调签名由驱动框架定义,用户不能改 | 永久 | +| `constParameterCallback` | 同上,回调函数指针类型受限 | 永久 | +| `constVariablePointer` | 局部指针加 const 是好习惯,但当前代码量大、改动多 | 暂时 | +| `unusedFunction` | CubeMX 生成的弱符号、第三方库回调被误报 | 永久 | +| `unusedStructMember` | HAL 寄存器位域按位操作,cppcheck 不理解 | 永久 | +| `redundantAssignment` | HAL 清标志宏(如 `__HAL_SPI_CLEAR_OVRFLAG`)读寄存器覆盖旧值,cppcheck 不理解"读即目的" | 永久 | + +### 4.4 扫描范围 + +通过 `types: [c]` + `exclude` 控制: + +**检查**:`App/`、`Src/`、`Inc/`、`Drivers/BSP/` 下的 .c 文件 + +**不检查**(第三方库,问题不是我们的): + +``` +Drivers/CMSIS/ # ARM CMSIS +Drivers/STM32F4xx_HAL_Driver/ # ST HAL +Drivers/BSP/UI/ # LVGL UI 层 +Drivers/BSP/NET/lftpd/ # LwIP FTP +Drivers/BSP/dhara/ # NAND FTL +Lib/ # LVGL 库 +Middlewares/ # FreeRTOS +MDK-ARM/ # Keil 工程文件 +test/ # 测试代码 +``` + +--- + +## 5. 误报抑制方法 + +### 5.1 行内抑制(`// cppcheck-suppress`) + +确属误报、又不想全局 `--suppress` 掉整条规则时,在**报错行的上一行**加注释: + +```c +// cppcheck-suppress knownConditionTrueFalse ; 说明原因(分号后文字可选) +if (day_count < diy) break; +``` + +要点: +- 需要 `--inline-suppr` 开关(已在配置中启用),否则注释无效 +- 注释必须紧贴**报错报告行**的上一行(cppcheck 报告行可能不是你以为的那行,以输出为准) +- 检查 ID 后跟的 `(...)` 括号文字会被当作 suppression attribute 解析而报错,说明文字用 `;` 后跟 + +### 5.2 全局抑制(`--suppress`) + +某条规则在整个工程中都是噪音/不适用时,在 `.pre-commit-config.yaml` 的 args 中添加 `--suppress=规则名`(同样遵守 4.2 节引号规则),现有清单见 4.1 节。 + +--- + +## 6. 问题与误报处理记录 + +### style 检查发现的问题(已处理完毕) + +开启 style 后发现的有价值问题已全部处理: + +| 规则 | 问题 | 涉及文件 | 处理结果 | +|---|---|---|---| +| `compareValueOutOfTypeRangeError` | `uint16_t > 65535` 恒假 | net_socket.c | ✅ 已修复(改为 `== 0` 检测溢出绕回) | +| `redundantAssignment` | 重复赋值覆盖旧值 | ch395f.c | ✅ 已抑制(HAL 清标志宏读寄存器即目的,误报) | +| `unreadVariable` | 变量赋值后未读取 | gd5f2gq5ue.c, time_sync.c, ringbuf.c | ✅ 已修复(移除冗余初始化) | +| `variableScope` | 变量作用域可缩小 | sys_clock.c, net_socket.c 等 18 处 | ✅ 已修复(声明移入实际使用块) | +| `knownConditionTrueFalse` | `day_count < diy` 被判恒假(闰年 `diy=366` 分支未跟踪,误报) | sys_clock.c:108 | ✅ 已行内抑制(`// cppcheck-suppress`) | + +> 注:`knownConditionTrueFalse` 属真误报——闰年分支 `diy=366` 时 `day_count==365` 的 break 有意义,cppcheck 数据流分析未覆盖该分支。已用行内抑制处理,不影响该规则在其他文件上的正常检查。 diff --git a/docs/pre-commit使用说明.md b/docs/pre-commit使用说明.md index e4e4b5e2..c6a01ac2 100644 --- a/docs/pre-commit使用说明.md +++ b/docs/pre-commit使用说明.md @@ -86,209 +86,17 @@ git commit ### 2.6 cppcheck — C 静态分析 -cppcheck 是开源 C/C++ 静态分析工具,**不运行代码**,通过语法分析和数据流推理发现潜在缺陷。 +cppcheck 是开源 C/C++ 静态分析工具,**不运行代码**,通过语法分析和数据流推理发现潜在缺陷。本项目开启 `warning` / `performance` / `portability` / `style` 四个类别,任一类别发现问题即**阻止提交**(`--error-exitcode=1`)。 -**工作原理**:逐个读取 C 文件 → 分析语法/数据流/控制流 → 发现问题则报错。 - -#### 本项目开启的检查类别 - -| 类别 | 含义 | 典型问题 | -|---|---|---| -| `warning` | 通用警告 | 变量未使用、除零风险、空指针解引用、unreachable code | -| `performance` | 性能问题 | 不必要的值拷贝、字符串重复查找、低效的函数调用 | -| `portability` | 可移植性问题 | 不同平台行为不一致的代码、隐式类型转换 | -| `style` | 代码风格 | 变量作用域过大、冗余赋值、恒假条件、自赋值等可疑写法(详见下文详解)| - -#### style 类别具体检查什么(cppcheck 2.21.0) - -`style` 是"风格/质量"级别——报告的问题**不一定是 bug**,但绝大多数是**冗余代码、死代码、可疑写法、可以写得更安全/更清晰的代码**。下面按主题列出本项目(纯 C)实际会命中的规则(`--enable=style` 已开启): - -**① 冗余与死代码(redundant & dead code)— 最值得修** - -| 规则 | 检查什么 | 反例 | -|---|---|---| -| `redundantAssignment` | 变量赋值后被新值覆盖、旧值从未被读取 | `ret = OK; ret = f();`(首个赋值无意义)| -| `unreadVariable` | 变量赋值后从未被读取 | `s = -1;` 随后循环里被覆盖、从未用到 | -| `redundantCondition` | 短路求值使某条件恒真 | `if (a && a > 5)`(a 非零时前半已恒真)| -| `duplicateCondition` | if/else 分支条件重复 | `if (x){} else if (x){}` | -| `duplicateBreak` | 连续重复 break | `break; break;` | -| `duplicateExpression` / `duplicateExpressionTernary` | 运算符两侧表达式相同 | `a = a && b`、`x = x ? 1 : 2` | -| `unreachableCode` | return 之后不会执行的代码 | `return 0; foo();` | -| `unassignedVariable` | 变量声明后从未赋值 | `int x; use(x);` | -| `unusedLabel` | 从未被 goto 的标签 | `label:` 无对应 goto | - -**② 恒真/恒假条件(always-true / always-false)— 常藏逻辑 Bug** - -| 规则 | 检查什么 | 反例 | -|---|---|---| -| `knownConditionTrueFalse` | 依据数据流推断条件恒真/恒假 | `while (n>=365){ ... if (n<360) break; }` | -| `knownArgument` | 参数固定时函数结果可推断 | `strlen("")` 恒为 0 | -| `knownPointerToBool` | 已知指针当布尔用 | p 恒非空时 `if (p)` | -| `compareValueOutOfTypeRangeError` | 与类型范围外的值比较 | `uint16_t x; if (x > 65535)` 恒假 | -| `unsignedLessThanZero` | 无符号 < 0 恒假 | 无符号 `n` 的 `if (n < 0)` | -| `unsignedPositive` | 无符号 >= 0 / > -1 恒真 | `if (n >= 0)` | -| `pointerLessThanZero` / `pointerPositive` | 指针 < 0 / > 0 | `if (p < 0)` | - -**③ const 化与作用域(const & scope)** - -| 规则 | 检查什么 | 备注 | -|---|---|---| -| `constVariable` / `constVariablePointer` | 变量/指针可加 const 防误改 | `constVariablePointer` 当前**已抑制**(改动量大)| -| `constParameter` / `constParameterPointer` / `constParameterCallback` | 参数可加 const | 回调相关两条**已抑制**(HAL 签名不可改)| -| `variableScope` | 变量作用域可缩小(声明移入实际使用的最内层块)| 已全库修复 | -| `functionStatic` / `staticFunction` | 函数可加 static 限制可见性 | 本库函数多被外部引用,命中少 | - -**④ 可疑写法(suspicious patterns)— 疑似笔误** - -| 规则 | 检查什么 | +| 项 | 说明 | |---|---| -| `clarifyCalculation` | 位运算与逻辑运算易混(`|` vs `‖`、`&` vs `&&`)| -| `clarifyCondition` / `assignmentInCondition` | 条件里出现赋值(疑似 `=` 误写 `==`)、链式比较 | -| `bitwiseOnBoolean` | 对布尔量做位运算 | -| `selfAssignment` | 自赋值 `x = x` | -| `oppositeExpression` | 同一条件内互相矛盾的表达式 | -| `moduloofone` | `x % 1`(恒为 0)| -| `mismatchingBitAnd` | 位运算不匹配 | -| `suspiciousFloatingPointCast` | 浮点强转整型(可能丢精度)| -| `incrementboolean` | 对布尔量自增 | -| `shadowVariable` / `shadowArgument` / `shadowFunction` | 局部变量遮蔽外层变量/参数/函数 | -| `arrayIndexThenCheck` | 数组下标先使用、后越界检查 | -| `truncLongCastAssignment` / `truncLongCastReturn` | long 强转导致截断 | -| `funcArgNamesDifferent` | 函数参数名与声明不一致 | -| `nanInArithmeticExpression` | 算术表达式中出现 NaN | -| `ignoredReturnErrorCode` | 忽略带错误码的返回值 | -| `unpreciseMathCall` | 调用不精确的数学函数 | -| `returnNonBoolInBooleanFunction` | 布尔函数返回非布尔值 | -| `multiCondition` | 可疑的多条件组合 | -| `duplicateAssignExpression` | 同一表达式赋给多个变量 | -| `redundantBitwiseOperationInSwitch` / `redundantAssignInSwitch` | switch 中冗余位运算/赋值 | +| **触发条件** | 自有代码 .c 文件(App/、Src/、Inc/、Drivers/BSP/,第三方库除外)含潜在缺陷、冗余代码或可疑写法 | +| **处理方式** | **阻止提交**(必须修复) | +| **修复方式** | 按报告修复代码;确属误报用行内抑制 `// cppcheck-suppress` 或全局 `--suppress` | +| **典型报错** | `[path/to/file.c:123]: (style) variableScope The scope of the variable ... can be reduced` | +| **增量速度** | 单文件约 1~2 秒;全库约 5 秒 | -**⑤ 不适用本 C 工程的规则(C++ 专属,自动跳过)** - -`catchExceptionByValue`、`cstyleCast`、`missingOverride`、`noConstructor`、`noExplicitConstructor`、`operatorEqRetRefThis`、`unusedScopedObject`、`virtualCallInConstructor`、`useStlAlgorithm`、`constParameterReference` 等(异常/类/STL 相关,纯 C 代码不会命中)。 - -> 说明: -> - 上表规则均出自 cppcheck 2.21.0 `--errorlist` 中 `severity=style` 的真实清单。 -> - 部分规则(如 `duplicateBranch`、`suspiciousSemicolon`、`checkConst`)需要额外 `--inconclusive` 才会激活,为减少误报**未开启**。 -> - `unusedFunction`、`unusedStructMember`、`useStlAlgorithm`、`noExplicitConstructor` 等已在 `--suppress` 中抑制,原因见下文「抑制规则说明」。 -> - `--error-exitcode=1` 对**所有已启用级别**(warning/performance/portability/style)生效:任何一级发现问题都会**阻止提交**。style 级别的问题若确属误报,用行内抑制(见下文「行内抑制误报」)。 - -#### 不开启的常见类别 - -| 类别 | 为什么不开 | -|---|---| -| `error` | 严重错误(数组越界、内存泄漏),cppcheck 默认已包含,不需要显式加 | -| `information` | 信息类(未解析的头文件等),无实际价值 | - -#### 配置参数说明 - -``` ---std=c99 # 按 C99 标准检查(Keil 工程常用) ---error-exitcode=1 # 发现问题返回非零退出码(pre-commit 用来拦截提交) ---inline-suppr # 启用行内抑制注释(// cppcheck-suppress,默认关闭) ---quiet # 只报问题,不显示扫描进度 - -# --- 必须抑制(误报/不可修改) --- ---suppress=missingInclude # HAL/CMSIS 路径在 Keil 工程配置里,cppcheck 不知道 ---suppress=invalidPrintfArgType_sint # printf 的 %d 传 uint32_t —— 实际安全 ---suppress=invalidPrintfArgType_uint # printf 的 %u 传 int —— 实际安全 ---suppress=constParameterPointer # HAL 回调签名固定(如 SPI_HandleTypeDef*),不能加 const ---suppress=constParameterCallback # 回调函数指针类型限制,不能改签名 ---suppress=unusedFunction # 第三方库回调/弱符号函数被误报为未使用 ---suppress=unusedStructMember # HAL 寄存器位域成员未显式使用 ---suppress=noExplicitConstructor # C++ 兼容,不影响纯 C ---suppress=useStlAlgorithm # C++ STL 相关,不影响纯 C - -# --- 暂时抑制(噪音多,后续可移除) --- ---suppress=constVariablePointer # 局部指针加 const 是好习惯,但当前改动量大 - -# --- 头文件路径 --- --I Inc -I App -I Drivers/BSP --I Drivers/STM32F4xx_HAL_Driver/Inc -I Drivers/CMSIS/Include --I Middlewares/Third_Party/FreeRTOS/Source/include -``` - -#### ⚠️ YAML 参数引号陷阱(曾导致钩子完全失效) - -`.pre-commit-config.yaml` 的 `args:` 使用 YAML **流序列** `[a, b, c]` 语法,其中**逗号是元素分隔符、空格不产生新元素**。曾出现两个致命错误,导致 cppcheck 钩子**空转**("Passed" 但从未真正检查): - -| 错误写法 | 实际被解析成 | 后果 | -|---|---|---| -| `[--enable=warning,performance,portability,style]` | `--enable=warning` + 3 个假文件参数 `performance`/`portability`/`style` | **只启用 warning**,style/performance/portability 全部失效 | -| `[-I Inc -I App -I Drivers/BSP]` | 单个参数 `"-I Inc -I App -I Drivers/BSP"` | 所有 include 路径失效(指向一个不存在的目录名)| - -**正确写法**:含逗号或空格的参数必须加引号,`-I` 与路径必须分成两个元素: - -```yaml -args: ['--enable=warning,performance,portability,style', '--error-exitcode=1', - '-I', 'Inc', '-I', 'App'] -``` - -**排查方法**:怀疑参数被拆坏时,在 PATH 前置一个同名 shim(如 `cppcheck.cmd` 把 `%*` 写日志),跑 `pre-commit run cppcheck --files <某文件>` 查看实际执行的完整命令行。 - -#### 行内抑制误报(`// cppcheck-suppress`) - -确属误报、又不想全局 `--suppress` 掉整条规则时,在**报错行的上一行**加注释: - -```c -// cppcheck-suppress knownConditionTrueFalse ; 说明原因(分号后文字可选) -if (day_count < diy) break; -``` - -要点: -- 需要 `--inline-suppr` 开关(已在配置中启用),否则注释无效 -- 注释必须紧贴**报错报告行**的上一行(cppcheck 报告行可能不是你以为的那行,以输出为准) -- 检查 ID 后跟的 `(...)` 括号文字会被当作 suppression attribute 解析而报错,说明文字用 `;` 后跟 - -#### 抑制规则说明 - -| 抑制规则 | 原因 | 是否永久 | -|---|---|---| -| `constParameterPointer` | HAL 回调签名由驱动框架定义,用户不能改 | 永久 | -| `constParameterCallback` | 同上,回调函数指针类型受限 | 永久 | -| `constVariablePointer` | 局部指针加 const 是好习惯,但当前代码量大、改动多 | 暂时 | -| `unusedFunction` | CubeMX 生成的弱符号、第三方库回调被误报 | 永久 | -| `unusedStructMember` | HAL 寄存器位域按位操作,cppcheck 不理解 | 永久 | -| `redundantAssignment` | HAL 清标志宏(如 `__HAL_SPI_CLEAR_OVRFLAG`)读寄存器覆盖旧值,cppcheck 不理解"读即目的" | 永久 | - -#### 扫描范围 - -通过 `types: [c]` + `exclude` 控制: - -**检查**:`App/`、`Src/`、`Inc/`、`Drivers/BSP/` 下的 .c 文件 - -**不检查**(第三方库,问题不是我们的): - -``` -Drivers/CMSIS/ # ARM CMSIS -Drivers/STM32F4xx_HAL_Driver/ # ST HAL -Drivers/BSP/UI/ # LVGL UI 层 -Drivers/BSP/NET/lftpd/ # LwIP FTP -Drivers/BSP/dhara/ # NAND FTL -Lib/ # LVGL 库 -Middlewares/ # FreeRTOS -MDK-ARM/ # Keil 工程文件 -test/ # 测试代码 -``` - -#### style 检查发现的问题(已处理完毕) - -开启 style 后发现的有价值问题已全部处理: - -| 规则 | 问题 | 涉及文件 | 处理结果 | -|---|---|---|---| -| `compareValueOutOfTypeRangeError` | `uint16_t > 65535` 恒假 | net_socket.c | ✅ 已修复(改为 `== 0` 检测溢出绕回) | -| `redundantAssignment` | 重复赋值覆盖旧值 | ch395f.c | ✅ 已抑制(HAL 清标志宏读寄存器即目的,误报) | -| `unreadVariable` | 变量赋值后未读取 | gd5f2gq5ue.c, time_sync.c, ringbuf.c | ✅ 已修复(移除冗余初始化) | -| `variableScope` | 变量作用域可缩小 | sys_clock.c, net_socket.c 等 18 处 | ✅ 已修复(声明移入实际使用块) | -| `knownConditionTrueFalse` | `day_count < diy` 被判恒假(闰年 `diy=366` 分支未跟踪,误报) | sys_clock.c:108 | ✅ 已行内抑制(`// cppcheck-suppress`) | - -> 注:`knownConditionTrueFalse` 属真误报——闰年分支 `diy=366` 时 `day_count==365` 的 break 有意义,cppcheck 数据流分析未覆盖该分支。已用行内抑制处理,不影响该规则在其他文件上的正常检查。 - -#### 日常速度 - -- **增量扫描**(`git commit` 时):只检查 staged 文件,单文件约 1~2 秒 -- **全库扫描**(`pre-commit run --all-files`):约 5 秒,用于配置变更后验证 +> **完整规则清单(style 类别 95 项详解)、配置参数、YAML 引号陷阱、误报抑制方法、历史问题处理记录见 `docs/cppcheck使用说明.md`。** ### 2.7 no-mojibake — U+FFFD 乱码拦截 @@ -340,7 +148,7 @@ git commit -m "修复网络超时重连逻辑" |---|---|---| | trailing-whitespace | `Fixing xxx.c` → 需要重新 `git add` | 钩子已自动修复文件,`git add` 后重新提交 | | end-of-file-fixer | 同上 | 同上 | -| cppcheck | 报 warning/performance/portability | 根据报告修复代码,或在 args 中添加 `--suppress=规则名` | +| cppcheck | 报 warning/performance/portability/style | 根据报告修复代码;误报用行内抑制或 `--suppress`(详见 `docs/cppcheck使用说明.md`)| | no-mojibake | `U+FFFD replacement chars found` | 从 git 历史恢复干净版本(不可逆!) | | gitlint | `Title exceeds max length` | 精简 commit message 标题 | @@ -428,6 +236,7 @@ $env:PATH = [Environment]::GetEnvironmentVariable("Path","Machine") + ";" + [Env | `.gitlint` | 仓库根 | gitlint 规则(宽松,兼容中文) | | `scripts/check_mojibake.py` | scripts/ | U+FFFD 检测脚本 | | `docs/自动化代码审查实施.md` | docs/ | 实施规划文档(Route A/B 设计) | +| `docs/cppcheck使用说明.md` | docs/ | cppcheck 钩子详解(规则/配置/抑制) | | `docs/pre-commit使用说明.md` | 本文档 | 部署与使用说明 | ---