Clang-Tidy必须基于compile_commands.json运行,否则因缺失头文件路径、宏定义等编译上下文而大量误报或漏报;正确做法是CMake启用-DCMAKE_EXPORT_COMPILE_COMMANDS=ON生成该文件,并在CI中用-p指定路径执行。

Clang-Tidy 必须基于 compile_commands.json 运行,否则路径和宏定义全错
很多团队在 CI 里直接写 clang-tidy *.cpp,结果告警大量误报或漏报。根本原因是 Clang-Tidy 需要完整的编译上下文:头文件搜索路径、-D 宏、C++ 标准版本、模板实例化信息——这些只有通过 compile_commands.json 才能准确还原。
正确做法是让 CMake 生成该文件,并在 CI 中显式指定:
cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -B build/ -S .
然后运行:
run-clang-tidy -p build/ -checks='bugprone-*,-bugprone-lambda-function-name' src/
-
-p build/是必须的,指向包含compile_commands.json的目录 - 不加
-p时,Clang-Tidy 默认用系统头路径,#include "my_header.h"会找不到 - 如果项目用了
add_subdirectory(third_party),记得在run-clang-tidy命令里用-header-filter排除第三方路径,否则满屏误报
把 bugprone-dangling-handle 和 cppcoreguidelines-pro-bounds-array-to-pointer-decay 当成门禁硬规则
这两类检查直接对应线上高频崩溃根源:悬空引用和数组退化为裸指针后越界。它们不是“风格建议”,而是未定义行为(UB)的明确信号。
立即学习“C++免费学习笔记(深入)”;
在 CMakeLists.txt 中启用并设为错误:
set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=-*,bugprone-dangling-handle,bugprone-undefined-memory-manipulation,modernize-use-auto,readability-identifier-naming;--warnings-as-errors=bugprone-*")
-
bugprone-dangling-handle能抓到return std::string().c_str()或临时对象绑定到 const 引用后返回其地址 -
bugprone-undefined-memory-manipulation覆盖 memcpy/memset 越界、负数长度、空指针传参等典型内存误操作 - 别开
performance-*类规则当门禁——它们常因模板展开深度触发误报,适合本地开发提示,不适合阻断合入
Cppcheck 作为 Clang-Tidy 的补位工具,专扫跨函数未初始化变量
Clang-Tidy 对跨函数变量状态跟踪较弱,而 Cppcheck 的数据流分析引擎对这类问题更敏感。尤其当函数参数没被显式初始化、又在深层调用链中被读取时,Cppcheck 往往先报警。
CI 中并行执行,不替代但互补:
cppcheck --enable=warning,style,performance,portability --inconclusive --suppress=missingInclude --project=build/compile_commands.json --xml-version=2 2> cppcheck.xml
-
--inconclusive必加:否则像 “int x; if (cond) x = 1; return x;” 这种分支未覆盖初始化会被忽略 -
--suppress=missingInclude避免头文件路径问题干扰主线逻辑检查 - 它不依赖编译器前端,对宏定义多、条件编译复杂的模块(如嵌入式驱动层)更稳定
告警必须分级,未初始化变量和空指针解引用不能被 suppress
实践中发现,开发者最容易滥用 // NOLINT 或 // NOLINTNEXTLINE 来绕过烦人的警告。但两类问题绝不能放行:
-
uninitializedVariable(Cppcheck)和bugprone-unchecked-optional-access(Clang-Tidy)——未初始化就用,值完全不可控 -
clang-diagnostic-dereference-of-null-pointer和bugprone-undefined-memory-manipulation——解引用空指针或越界写,直接触发 SIGSEGV
CI 脚本里应做白名单过滤:
grep -q "uninitializedVariable\|dereference_of_null_pointer\|undefined_memory_manipulation" cppcheck.xml && exit 1 || true
真正难处理的是误报:比如某个指针在所有路径上都非空,但工具推演不出。这时优先重构代码(如提前 assert(ptr) 或改用 std::optional),而不是加 suppress。
最后提醒一句:静态检查不是万能的。它查不到 ASan 能捕获的运行时堆栈溢出,也推演不出多线程下无锁操作的竞争窗口。工具链要分层,Clang-Tidy 和 Cppcheck 只负责守住“语法合法但逻辑危险”这一道底线——这道底线一旦失守,后续所有测试和监控都可能失效。


















