Clang-Query 仅是交互式 AST 调试器,不支持自动拦截;真正用于 pre-commit/CI 拦截的是 Clang-Tidy,需配合自定义检查(如路径+函数调用匹配)和 compile_commands.json 实现架构约束。

Clang-Query 本身不支持自动拦截提交,得靠 Clang-Tidy + pre-commit 配合
很多人误以为 clang-query 是个“扫描+阻断”工具,其实它只是交互式查询 AST 的调试器,没有内置检查逻辑、不生成诊断、也不支持配置规则集。真正能落地到 CI 或本地 pre-commit 拦截的,是 clang-tidy ——它基于同一套 Clang AST,但封装了可启用/禁用的检查项(如 cppcoreguidelines-* 、cert-*),还能输出机器可读格式(-format-style=none -export-fixes)。
实际流程是:用 clang-tidy 定义架构约束(比如禁止某模块调用 std::system、禁止跨层 include),再把它塞进 Git hook 或 CI 脚本里;一旦发现违规,就非零退出,Git 提交会中断。
-
clang-query只在你写新检查逻辑时有用:比如先用它验证某个 AST 匹配模式是否真能抓到CallExpr调用特定函数,再把该逻辑写成clang-tidy自定义检查 - 直接拿
clang-query扫代码 = 手动翻 AST,没法集成、没法批量、更没法阻断 - 如果你硬要用
clang-query做自动化,只能自己写 shell 脚本解析它的输出,但稳定性差、无错误码、不推荐
怎么用 Clang-Tidy 表达“禁止 A 模块调用 B 库函数”这类架构约束
标准检查项覆盖不了模块级依赖规则,必须自定义检查。核心是继承 ClangTidyCheck,在 registerMatchers() 里写 ASTMatcher,例如禁止 network/ 目录下源文件调用 std::execve:
void MyArchRuleCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
callExpr(callee(functionDecl(hasName("::execve"))),
hasAncestor(translationUnitDecl(
hasDescendant(namedDecl(hasName("network/.*\.cc"))))))
.bind("execve_call"),
this);
}
关键点:
- 路径匹配不能靠字符串硬编码,得用
hasSourceLoc+getFilename()在check()回调里判断,否则 matcher 无法识别相对路径 - 模块边界建议用目录前缀(如
src/core/vssrc/legacy/),别依赖命名空间——命名空间可嵌套、可 alias,不可靠 - 匹配到后,用
diag(...)报错,并加[arch-rule]标签,方便 Git hook 过滤
pre-commit 阶段怎么避免 clang-tidy 慢到让人想绕过
全量跑 clang-tidy 会卡住提交,必须限范围。不要用 git diff --name-only HEAD 粗暴过滤,而要:
- 只检查本次修改的
.cc和.h文件(git diff -z --name-only --diff-filter=ACM HEAD | grep -z '.c|.h$' | xargs -0) - 跳过生成代码(如
*_pb.cc、gen/*),加--header-filter='^(?!.*_pb.h$|gen/).*.h$' - 用
-p而不是-extra-arg,否则宏定义和 include 路径错乱,误报率飙升 - 加
--quiet减少输出,但保留 error 级别信息——pre-commit 脚本靠grep -q 'error:'判断是否失败
如果项目没 compile_commands.json,别手动生成,改用 CMake 的 -DCMAKE_EXPORT_COMPILE_COMMANDS=ON,这是唯一可靠方式。
CI 中拦截失败后,开发者最常卡在哪几个点
报错信息里出现 use of undeclared identifier 'XXX' 或 no member named 'YYY' in 'ZZZ',基本不是架构问题,而是 clang-tidy 解析失败:
- 没传对
-p路径,或compile_commands.json里directory字段是相对路径(Clang-Tidy 要求绝对路径) - 用了 C++20 概念但 CI 的 Clang 版本太老(
clang-tidy-12不支持requires),得统一升级到clang-tidy-15+ - 头文件里有平台相关宏(如
#ifdef __linux__),但clang-tidy默认不定义这些,需补-D__linux__
真正难的是让团队接受“编译通过 ≠ 可提交”,这需要把检查项收敛到 3–5 条高危规则(如禁止裸指针跨模块传递、禁止异常穿透接口层),而不是堆 50 条低价值警告。否则大家第一反应永远是删 hook,不是改代码。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











