clang-tidy插件必须实现clangtidycheck派生类并注册到clangtidymodule,缺一不可;需用ast匹配器定位模式,check()中须三重防护避免崩溃。

Clang-Tidy 插件需要什么基础组件
必须实现 ClangTidyCheck 派生类,并注册到 ClangTidyModule;不提供完整模块注册,插件加载时会静默失败,连 -list-checks 都看不到你的检查项。
关键结构缺一不可:
-
MyCustomCheck类继承自clang::tidy::ClangTidyCheck,重写registerMatchers()和check() - 一个继承
ClangTidyModule的模块类,重写createCheck()并在addCheckFactories()中注册 - CMakeLists.txt 里用
add_clang_library()构建动态库(不是可执行文件),且链接clangTooling、clangAST、clangLex等依赖 - 编译产物必须是
.so(Linux/macOS)或.dll(Windows),且文件名符合libMyModule.so格式(Clang-Tidy 通过前缀lib+ 模块名识别)
怎么写 registerMatchers 定位团队规范中的典型模式
不能靠字符串匹配,必须用 Clang AST 匹配器(ast_matchers)。比如团队禁止裸指针成员变量,就得匹配 fieldDecl(hasType(pointerType())),而不是搜 "*"。
常见误判点:
- 忽略模板上下文:用
fieldDecl()默认不匹配类模板中的成员,需加unless(isInstantiated())或显式处理isTemplateInstantiation() - 过度匹配:
varDecl()会抓到函数内局部变量,加hasParent(cxxRecordDecl())才能限定为类成员 - 类型穿透不足:
pointerType()不匹配const T*,要用pointsTo(anything())或组合qualType(hasCanonicalType(pointerType()))
示例:检查是否用了非 const 引用参数(团队要求输入参数尽量 const&)
void MyCustomCheck::registerMatchers(MatchFinder *Finder) {
Finder->addMatcher(
parmVarDecl(
hasType(references(qualType(unless(isConstQualified()),
hasCanonicalType(referenceType(pointee(qualType()))))))
).bind("nonConstRefParam"), this);
}
check() 里怎么安全生成诊断信息并避免崩溃
AST 节点可能为空、未完成解析或跨翻译单元失效,直接调 Node.getNodeAs<parmvardecl>("nonConstRefParam")->getType().getAsString()</parmvardecl> 很容易段错误。
必须做三重防护:
- 用
if (const auto *Param = Node.getNodeAs<parmvardecl>("nonConstRefParam"))</parmvardecl>判空 - 用
Param->getType().isNull()检查类型有效性(尤其模板推导失败时) - 诊断位置用
Param->getLocation(),但要先if (!Param->getLocation().isValid()) return; - 修复建议(FixItHint)只对源码存在的 token 生效;若想改模板特化里的参数,基本不可行——别硬塞
FixItHint::CreateReplacement()
示例中生成警告:
void MyCustomCheck::check(const MatchFinder::MatchResult &Result) {
const auto *Param = Result.Nodes.getNodeAs<parmvardecl>("nonConstRefParam");
if (!Param || !Param->getLocation().isValid() || Param->getType().isNull())
return;
diag(Param->getLocation(), "non-const lvalue reference parameter violates team guideline")
getLocation(), "const ");
}</parmvardecl>
怎么让插件被 clang-tidy 正确加载和调试
加载失败时不会报错,只会跳过——最常见原因是路径、命名或符号导出问题。
- 插件路径必须通过
-plugin-path=显式指定,且该路径下只能放.so文件,不能有子目录 - 模块名(即
ClangTidyModule子类名)必须与add_clang_library(libMyModule)中的库名一致,且首字母小写(Clang-Tidy 内部会把模块名转成小写再拼接lib前缀) - 确保符号可见:CMake 中添加
set_property(TARGET MyModule PROPERTY POSITION_INDEPENDENT_CODE ON),并用__attribute__((visibility("default")))导出模块类 - 调试时加
-debug参数,观察日志里是否有Loading module 'mymodule';没有就说明注册或路径错了
运行命令示例:
clang-tidy -plugin-path=/path/to/plugins -checks="-*,mymodule-non-const-ref-param" foo.cpp
最难调的是匹配逻辑和 AST 生命周期——节点在 check() 里看似可用,但一旦涉及 getDescribedClassTemplate() 或 getTemplateSpecializationKind(),极易因 Sema 未完成而返回空。这类问题没有通用解法,只能结合 -ast-dump 看实际 AST 结构,再调整 matcher 条件。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











