
llvm-project 中编写 clang-tidy 自定义检查的完整开发指南从 add_new_check.py 骨架到源码级原理【免费下载链接】llvm-projectThe LLVM Project is a collection of modular and reusable compiler and toolchain technologies.项目地址: https://gitcode.com/GitHub_Trending/ll/llvm-project导读本文以 llvm-project 仓库LLVM 官方 monorepo中 clang-tools-extra/docs/clang-tidy/Contributing.rst《Getting Involved》为核心脉络系统讲解如何为 :program:clang-tidy开发、注册、配置、测试并提交一个全新的自定义 lint 检查。读完本文你将掌握选择检查实现形态Clang diagnostic / 静态分析器 / clang-tidy check的决策依据、借助add_new_check.py生成检查骨架与测试的完整流程、AST Matcher 与PPCallbacks两种插件式分析机制、check 配置项与storeOptions的工作方式以及check_clang_tidy.py测试规范与提交 Pull Request、加载 out-of-tree 插件、在 LLVM 全量源码上运行与性能分析等实战方法。1. 为什么选择 clang-tidy扩展点的定位与取舍clang-tidy 本身内置了大量检查同时可以运行 Clang 静态分析器clang-analyzer-*的检查但它真正的威力在于能够以极低成本编写自定义检查检查被组织成模块module通过极少的代码改动甚至零改动就能链接进 clang-tidy 主程序。检查可以在两个层面接入分析管道预处理器层PPCallbacks通过PPCallbacks钩子观察宏展开、include 指令等预处理事件AST 层AST Matchers通过 AST Matchers 声明式地描述要匹配的语法模式。当发现问题时检查以类似 Clang 诊断的方式上报告警并可附带fix-it 提示自动修复建议。clang-tidy 提供的这套接口让用几行代码写出有用且精确的检查成为可能——这正是本指南要展开的内容。在动手之前需要先判断你的想法应该落在哪一层文档给出了三条清晰的分流标准实现形态适用场景Clang diagnostic检查足够通用、针对大概率是 bug的代码模式、可以有效实现且误报率极低时优先放进 Clang 前端核心诊断而非风格/可读性问题。Clang static analyzer check检查需要控制流分析如路径敏感分析、跨语句数据流应实现为静态分析器检查。clang-tidy check面向 linter 风格的检查、与特定编码风格强相关Google、LLVM、CERT 等、处理可读性问题等的检查最适合作为 clang-tidy 检查。仓库佐证在 clang-tools-extra/clang-tidy/ClangTidyCheck.h 与 ClangTidy.h 中可以看到面向检查作者与使用者的核心接口定义模块系统在 ClangTidyModule.h 中声明。2. 准备工作工作区与构建配置如果你是 LLVM 开发新手应先阅读 LLVM 官方的《Getting Started with the LLVM System》《Using Clang Tools》与《How To Setup Clang Tooling For LLVM》用 CMake 完成 LLVM、Clang 与 Clang Extra Tools 的检出和构建。使用 CMake 配置构建时务必同时启用clang与clang-tools-extra两个 projectclang-tidy 才会被构建。需要注意的相关配置项还包括因为新检查会附带文档建议安装 Sphinx 并在 CMake 配置中启用文档生成为了节省核心 Clang 库的编译时间可以只在 CMake 配置中启用X86target若设置CLANG_TIDY_ENABLE_STATIC_ANALYZERNO构建出的 clang-tidy 将不支持clang-analyzer-*与mpi-*检查若设置CLANG_TIDY_ENABLE_QUERY_BASED_CUSTOM_CHECKSNO构建出的 clang-tidy 将不支持基于 query 的自定义检查。在仓库当前的源码树中clang-tidy 的核心实现位于clang-tools-extra/clang-tidy其构建开关在 clang-tidy/CMakeLists.txt 与 clang-tidy-config.h.cmake 中体现。3. 仓库目录结构一份 clang-tidy 的地图理解 clang-tidy 的源码布局是编写检查的第一步。以仓库根目录为参照其结构如下相对于原文档给出的llvm/clang-tools-extra前缀本文统一从仓库根换算为clang-tools-extra/clang-tools-extra/clang-tidy/ # clang-tidy 核心 |-- ClangTidy.h # 面向使用者的接口 |-- ClangTidyCheck.h # 面向检查check的接口 |-- ClangTidyModule.h # clang-tidy 模块module接口 |-- add_new_check.py # 新建检查的自动化脚本 |-- rename_check.py # 重命名既有检查的脚本 |-- google/ # Google 风格模块 | |-- GoogleTidyModule.cpp / .h # 模块注册 | |-- 各检查Check.cpp / Check.h |-- llvm/ # LLVM 风格模块 |-- objc/ # Objective-C 模块 |-- ... |-- tool/ # clang-tidy 可执行程序入口源码 | |-- run-clang-tidy.py | |-- clang-tidy-diff.py |-- utils/ # 检查共用工具库如 TransformerClangTidyCheck.h clang-tools-extra/test/clang-tidy/ # 集成测试lit clang-tools-extra/unittests/clang-tidy/ # 单元测试在仓库中可以看到大量与模块同名的子目录abseil/、altera/、android/、boost/、bugprone/、cert/、cppcoreguidelines/、google/、llvm/、llvmlibc/、misc/、modernize/、objc/、performance/、readability/等。这些目录名称与用户面向的检查组check group名称一致每个目录中都包含对应的*TidyModule.cpp/.h与成对的*Check.cpp/.h文件例如 google/GoogleTidyModule.cpp。可执行程序相关辅助脚本run-clang-tidy.py、clang-tidy-diff.py位于 clang-tidy/tool 下。4. 编写一个 clang-tidy Check从骨架到完整实现4.1 用 add_new_check.py 一键生成骨架决定好模块与检查名后官方推荐直接运行clang-tidy/add_new_check.py脚本仓库内完整实现位于 clang-tools-extra/clang-tidy/add_new_check.py它会自动完成检查的创建、CMake 文件更新与测试生成。其命令行参数在脚本的argparse部分main 函数入口定义位置参数module新检查所属模块目录如readability位置参数check新检查名称如awesome-function-names--language LANG检查适用的语言可选c/c/objc/objc默认 C--standard限定语言标准版本如c17、c11会据此生成isLanguageVersionSupported的对应判断--description/-d检查的一句话描述默认FIXME: Write a short description会同步写入头文件注释与 Release Notes--update-docs只重建文档清单列表后退出。脚本内部从 write_header、write_implementation、adapt_module、write_test、write_docs 等函数可以看到会依次完成在指定模块目录内创建检查类对应的.h与.cpp并把.cpp加入该模块的 CMakeLists.txt按字典序插入见adapt_cmake在模块的*TidyModule.cpp中登记检查的注册语句见adapt_module插入registerCheck...(...)在test/clang-tidy/checkers/module/下创建 lit 测试文件该脚本将测试写到相对于模块的../../test/clang-tidy/checkers/module/check.ext即仓库的 clang-tools-extra/test/clang-tidy/checkers 目录创建检查文档文件并加入 docs/clang-tidy/checks/list.md 的清单与 Release Notes。一个值得注意的细节在脚本的 main 流程中若模块名为llvm其 C 命名空间会被特意映射为llvm_check以避免与全局广泛使用的llvm命名空间冲突其他模块直接以模块名作为命名空间。默认生成的新检查只作用于 C 代码如需不同语言选项使用脚本的--language参数。例如创建名为readability-awesome-function-names的检查$ clang-tidy/add_new_check.py readability awesome-function-names4.2 解读自动生成的检查类骨架脚本生成的检查类头文件模板如下注意#include的是本模块外一层的公共基类头文件#include ../ClangTidyCheck.h namespace clang::tidy::readability { class AwesomeFunctionNamesCheck : public ClangTidyCheck { public: AwesomeFunctionNamesCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context) {} void registerMatchers(ast_matchers::MatchFinder *Finder) override; void check(const ast_matchers::MatchFinder::MatchResult Result) override; bool isLanguageVersionSupported(const LangOptions LangOpts) const override { return LangOpts.CPlusPlus; } }; } // namespace clang::tidy::readability要点如下构造函数接收Name与Context必须原样转发给ClangTidyCheck构造函数其中Context是访问选项Options、诊断报告等全局能力的总入口。isLanguageVersionSupported可选覆写用于限定检查生效的语言与标准版本add_new_check.py的--language/--standard参数正是生成该函数返回表达式如 C 语言为!LangOpts.CPlusPlus并可按C11、C17、CPlusPlus11…… 追加条件可对照脚本中cpp_language_to_requirements与c_language_to_requirements两张映射表。若需在AST 层分析覆写registerMatchers与check若需分析预处理器层则应覆写registerPPCallbacks方法——注意add_new_check.py生成的起点骨架不会生成registerPPCallbacks覆写需要自行添加。在registerMatchers中创建 AST Matcher语法细节参考 Clang 官方《AST Matchers》与《AST Matcher Reference》描述你想要在 AST 中发现的模式匹配结果被交给check方法在那里做进一步检查并上报诊断。以生成的.cpp为模板完整的示例实现using namespace clang::ast_matchers; void AwesomeFunctionNamesCheck::registerMatchers(MatchFinder *Finder) { Finder-addMatcher(functionDecl().bind(x), this); } void AwesomeFunctionNamesCheck::check(const MatchFinder::MatchResult Result) { const auto *MatchedDecl Result.Nodes.getNodeAsFunctionDecl(x); if (!MatchedDecl-getIdentifier() || MatchedDecl-getName().startswith(awesome_)) return; diag(MatchedDecl-getLocation(), function %0 is insufficiently awesome) MatchedDecl FixItHint::CreateInsertion(MatchedDecl-getLocation(), awesome_); }Finder-addMatcher(..., this)把 matcher 注册进 MatchFinderbind(x)给匹配节点命名在check中通过Result.Nodes.getNodeAsFunctionDecl(x)取出匹配节点%0是诊断信息中参数的占位符由后续 MatchedDecl传入Clang 会把NamedDecl格式化输出为函数名FixItHint::CreateInsertion(loc, awesome_)声明一个在loc处插入awesome_前缀的自动修复建议同时上报的警告会被 clang-tidy 记录并可用-fix一键应用。需要说明的是clang-tidy 的官方文档示例曾引用google/ExplicitConstructorCheck作为参考实现当前仓库的 google 目录已不包含该检查想研读真实模块内样例时可参考同目录下现存的检查对例如 google/UsingNamespaceDirectiveCheck.cpp它们遵循同样的「registerMatchers声明匹配 check上报诊断」模式。4.3 诊断报告与 fix-it 的挂载机制文档强调clang-tidy 检查通过diag(...)上报问题行为与 Clang 自身诊断一致——诊断消息关联到源码位置可携带参数与多个 fix-it。需要与宏或预处理指令交互时则覆写registerPPCallbacks利用PPCallbacks观察预处理阶段的事件例如记录宏定义位置供后续 AST 匹配时判断节点是否来自宏展开。clang-tidy 在报告阶段使用ClangTidyDiagnosticConsumer见 ClangTidyDiagnosticConsumer.cpp收集并去重诊断。4.4 开发者常用辅助工具开发 clang-tidy 检查时以下工具尤为有用add_new_check.py——自动化新增检查全流程见上rename_check.py——如脚本名所示重命名既有检查pp-trace——记录某个源文件上的PPCallbacks方法调用是理解预处理器机制不可或缺的工具clang-query——交互式原型化 AST matcher、探索 Clang AST 的利器clang-check 的-ast-dump可搭配-ast-dump-filter——便捷地转储一段 C 程序的 AST。5. 检查开发技巧文档指引、Transformer 与增量式开发5.1 有用的背景文档速览写检查前建议先在 LLVM/Clang 代码库中定向阅读LLVM 层被 Clang 大量使用的支持类如StringRef、SmallVector等见《LLVM Programmers Manual》中「Important and useful LLVM APIs」「Picking the Right Data Structure for the Task」两节LLVM/ADT/STLExtras.h里提供了作用于 LLVM 容器的实用 STL 算法变体如llvm::all_of。这些类都可在 doxygen 中检索无需强记。Clang 层诊断、fix-it 与源码位置相关的机制由The Clang Basic Library描述token、词法与预处理器由The Lexer and Preprocessor Library描述C 源码语句如何在 AST 中表示由The AST Library描述——以上三个主题都收录在《Clang CFE Internals Manual》中。绝大多数检查经由AST与 C 源码交互源文件先被词法分析、预处理再解析成 ASTAST 完整构建后clang-tidy 把检查注册的 matcher 应用其上命中节点即回调check。对预处理器的监视独立于 AST 构建但检查可以在预处理阶段收集信息、供后续 AST 匹配时使用。C 源码的每个句法甚至语义元素在 AST 中都对应不同类通过组合AST matcher 函数筛选感兴趣的片段——建议仔细研读官方《AST Matcher Reference》理解不同 matcher 函数之间的关系。5.2 用 Transformer 库编写重写型检查Transformer 库允许把源码变换表达为一条RewriteRule并提供了组合源码编辑的函数。除非需要底层源码位置操作否则应考虑用 Transformer 库来写检查细节参见《Clang Transformer Tutorial》。对add_new_check.py生成的代码改为使用 Transformer 库需要做四处修改把#include ../ClangTidyCheck.h换成#include ../utils/TransformerClangTidyCheck.h把检查的基类从ClangTidyCheck改为TransformerClangTidyCheck删除类中对registerMatchers与check的覆写编写一个创建RewriteRule的函数并在构造函数中把它传给TransformerClangTidyCheck的构造函数。从仓库源码 TransformerClangTidyCheck.h 可以看到该基类同时覆写了registerPPCallbacks、registerMatchersfinal与checkfinal把 matcher/check 生命周期内部化它还识别一个名为IncludeStyle的 clang-tidy 选项取值llvm或google默认llvm影响规范头文件的区分方式。规则中每个 case 都必须带非空Explanation因为它同时充当诊断文案。5.3 增量式开发流程与 clang-query 原型验证推荐从简单用例出发、逐步增加复杂度add_new_check.py生成的测试文件正是起点。大致流程为检查编写一个测试用例用 clang-query 在测试文件上原型化 matcher把验证过的 matcher 固化进registerMatchers在check中上报所需诊断与 fix-it为用例补充CHECK-MESSAGES与CHECK-FIXES注释以验证诊断与修复构建check-clang-toolstarget 确认测试通过循环往复直到检查的所有方面都有测试覆盖。clang-query 支持把复杂匹配表达式拆解并命名clang-query let c1 cxxRecordDecl() clang-query match c1此外在一个 matcher 的左括号后按 Tab 会提示可与前一个 matcher 链式组合的候选 matcher部分可用 matcher 可能不在提示列表里注意 Tab 补全目前不支持 Windows。就像把巨型函数拆成带语义命名的小函数有助于理解算法一样把复杂 matcher 拆成带语义命名的小 matcher 也有助于理解与复用交互式验证成功后C API matcher 通常与交互版本一致或相近可以用局部变量保留这些命名。5.4 创建私有 matcher 与单元测试辅助代码当现有 AST matcher 无法表达所需的具体 AST 特征时可以用与公共 matcher 相同的基础设施创建自己的私有 matcher。它的好处是把复杂的手工 AST 遍历逻辑下沉到 matcher 里check中只需按绑定名取用所需节点。私有 matcher 等辅助支撑代码非常适合用单元测试覆盖——比FileCheck集成测试更易测。仓库中公共 AST matcher 类的单元测试位于ASTMatchersTeststarget是学习测试惯用法的好样板。clang-tidy 自身的单元测试通过构建ClangTidyTeststarget 运行需要提醒的是LLVM/Clang 中测试类 target 会被排除在 IDE CMake 生成器的 build all 之外必须显式指定 target 才会真正构建。5.5 让检查足够健壮覆盖基本「happy path」后建议用尽可能多的边界场景折磨检查。在大型代码库如 Clang/LLVM 自身上试跑是发现 matcher 遗漏的好办法但 LLVM 代码库未必足够——它是按特定编码风格与质量标准演进的测试语料越大社区对检查有效性及误报率的信心越强。文档给出如下建议创建包含被匹配代码的头文件用例用 clang-tidy 手工验证 fix-it 在头文件上的应用是否正确在check_clang_tidy.py支持自动校验前需手工完成定义包含被匹配代码的宏、模板类、模板特化用例同时在Windows 与 Linux下测试例如 fix-it 插入换行时应使用源文件已有的换行风格而非硬编码\n可用SourceManager::getBufferData(FileID).detectEOL()探测警惕高误报率理想情况下检查零误报但 AST 匹配不敏感于控制流/数据流出现一定误报在所难免——误报率越高越难被采用应为用户管理误报提供机制。两种主要机制支持免打扰代码模式如允许显式(void)强转来静默未使用变量告警只要该模式能清晰表达程序员意图与检查配置选项允许用户选择更激进的检查行为同时不为常见的高置信场景增加负担。5.6 为检查撰写文档add_new_check.py会在 Release Notes、检查清单与docs/clang-tidy/checks/module/check.md处创建条目。建议用一句话写清检查作用这句话应同时出现在 Release Notes、头文件 doxygen 注释首句与检查文档首句中注意在总结中避免使用 this check 这类措辞。如果检查涉及已发布的编码指南C Core Guidelines、SEI CERT 等或风格规范应在文档中给出相应章节链接同时应提供足够的诊断与 fix-it 示例让用户能直观理解运行后代码会发生什么变化存在例外或局限时务必详述。需要注意一个法律/许可约束直接引用 MISRA 或 AUTOSAR 指南的检查不会被接受仅与之重叠、但不声称实现或链接 MISRA/AUTOSAR 规则的通用检查则是可以的。构建docs-clang-tools-htmltarget 会运行 Sphinx 生成 HTML 文档到构建树中请确认新检查正确出现在 Release Notes 与检查清单中且文档格式结构无误。6. 注册你的检查模块机制与链接锚点日常开发中add_new_check.py会替你完成在既有模块中的注册只有当你需要创建全新模块或了解注册细节时才需手动操作以下内容照抄自原文档并保留完整代码。检查需在对应模块中以独立名字注册class MyModule : public ClangTidyModule { public: void addCheckFactories(ClangTidyCheckFactories CheckFactories) override { CheckFactories.registerCheckExplicitConstructorCheck( my-explicit-constructor); } };随后用静态初始化变量把模块注册进ClangTidyModuleRegistrystatic ClangTidyModuleRegistry::AddMyModule X(my-module, Adds my lint checks.);由于 LLVM 构建系统中模块分散编译为独立目标文件需要用如下锚点 hack保证模块真正被链接进 clang-tidy 可执行程序。在该注册变量附近添加// This anchor is used to force the linker to link in the generated object file // and thus register the MyModule. volatile int MyModuleAnchorSource 0;并在 clang-tidy 主程序或链接了 clang-tidy 库的二进制的主翻译单元中即 ClangTidyForceLinker.h加入对侧锚点// This anchor is used to force the linker to link the MyModule. extern volatile int MyModuleAnchorSource; static int MyModuleAnchorDestination MyModuleAnchorSource;7. 配置检查Options 的读取与 storeOptions若检查需要配置选项可在构造函数里用Options.getType(SomeOption, DefaultValue)读取检查专属选项同时覆写ClangTidyCheck::storeOptions让这些选项可被发现。storeOptions向 clang-tidy 声明该检查实现了哪些选项及当前值例如-dump-config命令行选项就会用到它。class MyCheck : public ClangTidyCheck { const unsigned SomeOption1; const std::string SomeOption2; public: MyCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), SomeOption1(Options.get(SomeOption1, -1U)), SomeOption2(Options.get(SomeOption2, some default)) {} void storeOptions(ClangTidyOptions::OptionMap Opts) override { Options.store(Opts, SomeOption1, SomeOption1); Options.store(Opts, SomeOption2, SomeOption2); } ...假设检查注册名为 my-check则在.clang-tidy文件中按如下方式设置CheckOptions: my-check.SomeOption1: 123 my-check.SomeOption2: some other value命令行指定检查选项时使用内联 YAML 格式$ clang-tidy -config{CheckOptions: {a: b, x: y}} ...从源码实现看选项系统位于 ClangTidyOptions.cpp / ClangTidyOptions.h其中的OptionsView/ClangTidyOptions::OptionMap提供了getType、store等接口是上面这些用法的底层支撑。8. 测试检查从 check-clang-tools 到 check_clang_tidy.py8.1 运行测试 target运行 clang-tidy 的全部测试构建check-clang-toolstarget。例如用 Ninja 生成器配置的构建$ ninja check-clang-toolsclang-tidy 检查可以用单元测试或lit 测试两种方式测试。单元测试更适合严格校验复杂替换lit 测试支持部分文本匹配与正则更适合编写紧凑的诊断消息测试。8.2 check_clang_tidy.py 测试框架check_clang_tidy.py脚本提供了一种便捷方式同时测试诊断消息与 fix-it脚本位于 clang-tools-extra 的 test 基础设施中。它从测试文件中过滤掉CHECK行运行 clang-tidy然后用两次独立的FileCheck调用验证第一次以CHECK-MESSAGES前缀校验诊断消息第二次以CHECK-FIXES前缀对应用 fix-it 之后的代码做校验。特别地CHECK-FIXES:可用原样出现在修复后代码中来断言某些代码未被 fix-it 修改。FileCheck的完整指令集都可用如CHECK-MESSAGES-SAME:、CHECK-MESSAGES-NOT:尽管基本形态CHECK-MESSAGES/CHECK-FIXES通常已足够。注意 FileCheck 官方文档默认前缀是CHECK描述为CHECK:、CHECK-SAME:、CHECK-NOT:等用于 clang-tidy 测试时把CHECK替换成CHECK-FIXES或CHECK-MESSAGES即可。check_clang_tidy.py还附加一个约束检查若文件中使用了CHECK-MESSAGES:则每条 warning/error 都必须有对应的 CHECK也可改用CHECK-NOTES:若你还想额外保证所有 note 都被检查到。使用方式把带合适RUN行的.cpp文件放入test/clang-tidy目录用CHECK-MESSAGES:/CHECK-FIXES:编写校验。建议把检查写尽可能具体避免误匹配输入的其他部分在测试代码中使用[[LINEX]]/[[LINE-X]]行号替换与不重名的函数、变量名。下面是一个基本用例// RUN: %check_clang_tidy %s google-readability-casting %t void f(int a) { int b (int)a; // CHECK-MESSAGES: :[[LINE-1]]:11: warning: redundant cast to the same type [google-readability-casting] // CHECK-FIXES: int b a; }RUN 行各字段依次是被测文件%s、检查全名、临时文件%t。在同一个测试文件里校验多个场景时用-check-suffixSUFFIX-NAME或-check-suffixesSUFFIX-NAME-1,SUFFIX-NAME-2,...参数并把指令替换为CHECK-MESSAGES-SUFFIX-NAME与CHECK-FIXES-SUFFIX-NAME// RUN: %check_clang_tidy -check-suffixUSING-A %s misc-unused-using-decls %t -- -- -DUSING_A // RUN: %check_clang_tidy -check-suffixUSING-B %s misc-unused-using-decls %t -- -- -DUSING_B // RUN: %check_clang_tidy %s misc-unused-using-decls %t ... // CHECK-MESSAGES-USING-A: :[[LINE-8]]:10: warning: using decl A {{.*}} // CHECK-MESSAGES-USING-B: :[[LINE-7]]:10: warning: using decl B {{.*}} // CHECK-MESSAGES: :[[LINE-6]]:10: warning: using decl C {{.*}} // CHECK-FIXES-USING-A-NOT: using a::A;$ // CHECK-FIXES-USING-B-NOT: using a::B;$ // CHECK-FIXES-NOT: using a::C;$8.3 用 -std 控制被测语言标准-std标志控制测试在哪种 C/C 标准下编译接受逗号分隔的标准列表并支持-or-later/-or-earlier后缀-stdc17只用 C17 运行测试-stdc17-or-later从 C17 起对每个标准当前为 C17、C20、C23、C26分别运行一次。适用于应在所有现代标准下工作正常的检查-stdc17-or-earlier对到 C17 为止的每个标准当前为 C98、C11、C14、C17分别运行。适用于应兼容所有旧标准的检查-stdc14,c17分别用 C14 与 C17 各运行一次。未指定-std时check_clang_tidy.py对 C 文件默认c11-or-later对 C 文件默认c99-or-later。add_new_check.py生成的骨架默认使用-or-later形式。除非测试期望只在特定标准版本下出现的行为否则优先写成-std最低版本-or-later。8.4 高频陷阱宏与模板C 语言存在许多暗角要让检查尤其带 fix-it 的在所有情况下都完美并不容易最常见的两类陷阱是宏与模板写在宏体/模板定义中的代码其含义可能随宏展开/模板实例化而改变多次宏展开/模板实例化可能让同一段代码被检查多次含义还可能不同同一条警告可能被重复上报clang-tidy 会去重完全相同的警告但若警告稍有差异全部都会展示给用户并用于应用修复对宏体/模板定义做替换对某些宏展开/模板实例化也许没问题但很容易破坏另一些展开/实例化。若需要多个文件来覆盖检查的各个方面建议放进该模块Inputs目录下以检查命名的子目录避免污染测试目录。若要验证检查与系统头文件的交互仓库提供了一套模拟系统头文件位于checkers/Inputs/Headers目录lit 测试中可用变量%clang_tidy_headers引用其路径。9. 提交 Pull Request提交前自检提交 PR 前鼓励先在改动上运行 clang-tidy 与 clang-format以保障代码质量、提前暴露问题。虽然 clang-tidy 目前并未在 CI 中强制启用遵循这一实践有助于保持代码一致性、预防常见错误。一个有用的命令用于检查已暂存staged的改动$ git diff --staged -U0 | ./clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py \ -j $(nproc) -path build/ -p1 -only-check-in-db $ git clang-formatclang-tidy-diff.py位于 clang-tidy/tool与run-clang-tidy.py同目录其参数-j指定并行任务数、-path指向构建目录、-p1处理标准 diff 前缀、-only-check-in-db只检查编译数据库中的文件。请注意部分警告可能是误报或需慎重权衡使用自己的判断对个别告警拿不准时欢迎在 PR 中讨论。10. Out-of-tree 检查插件把检查作为插件在源码树外开发大体遵循前述步骤包括新建模块、做模块注册所需的各种 hack。插件是共享库其代码在 clang-tidy 构建系统之外按其他 Clang 插件的做法与 LLVM 一起构建、链接即可。若用 CMake调用add_library或llvm_add_library时使用关键字MODULE。插件通过-load传给 clang-tidy同时还需给出要启用的检查名$ clang-tidy --checks-*,my-explicit-constructor -list-checks -load myplugin.so没有 ABI/API 稳定性承诺插件必须用将加载它的那份 clang-tidy 版本编译。插件可以使用线程、TLS 或其他任何可从外部头文件访问、树内代码可用的设施。测试 out-of-tree 检查可能需要从源码编译的 LLVM 安装中获取llvm-lit或者按 test-suite 指南获取lit、拿到FileCheck二进制再仿照check_clang_tidy.py写一份适配自己需求的版本。11. 在 LLVM 全量源码上运行 clang-tidyrun-clang-tidy.py在更大代码库上试跑是检验检查的最佳方式LLVM/Clang 正是天然目标源码已在手边。最便捷的方式是借助编译命令数据库compile command databaseCMake 可自动生成compile_commands.json。一旦就绪且 clang-tidy 在PATH中即可对整个代码库运行分析clang-tidy/tool/run-clang-tidy.py该脚本会以默认检查集对编译数据库中每个翻译单元执行 clang-tidy并展示产生的警告与错误还提供多个配置开关覆盖默认检查集-checks参数格式与 clang-tidy 完全一致。例如-checks-*,modernize-use-override只运行modernize-use-override。限定分析文件提供一个或多个文件名的正则参数。run-clang-tidy.py clang-tidy/.*Check\.cpp只分析 clang-tidy 检查文件。还可能需要用-header-filter与-exclude-header-filter限制展示警告的头文件范围二者行为与 clang-tidy 对应选项一致。应用修复-fix把所有修改收集到临时目录后统一应用再加-format会对改动行运行 clang-format。12. 检查性能剖析-enable-check-profile 与 JSON 输出clang-tidy 可以为每个检查收集性能剖析信息并对每个被处理的源文件翻译单元输出。启用剖析信息收集使用-enable-check-profile参数计时结果以表格形式输出到stderr。示例输出$ clang-tidy -enable-check-profile -checks-*,readability-function-size source.cpp ------------------------------------------------------------------------- clang-tidy checks profiling ------------------------------------------------------------------------- Total Execution Time: 1.0282 seconds (1.0258 wall clock) ---User Time--- --System Time-- --UserSystem-- ---Wall Time--- --- Name --- 0.9136 (100.0%) 0.1146 (100.0%) 1.0282 (100.0%) 1.0258 (100.0%) readability-function-size 0.9136 (100.0%) 0.1146 (100.0%) 1.0282 (100.0%) 1.0258 (100.0%) Total也可以把数据存为 JSON 文件以便后续处理$ clang-tidy -enable-check-profile -store-check-profile. -checks-*,readability-function-size source.cpp $ # Note that there wont be timings table printed to the console. $ ls /tmp/out/ 20180516161318717446360-source.cpp.json $ cat 20180516161318717446360-source.cpp.json { file: /path/to/source.cpp, timestamp: 2018-05-16 16:13:18.717446360, profile: { time.clang-tidy.readability-function-size.wall: 1.0421266555786133e00, time.clang-tidy.readability-function-size.user: 9.2088400000005421e-01, time.clang-tidy.readability-function-size.sys: 1.2418899999999974e-01 } }控制存储的参数只有一个-store-check-profileprefix默认情况下报告以表格形式输出到 stderr传入此选项后每个翻译单元的剖析数据改为存为 JSON。若 prefix 不是绝对路径则视为相对于运行 clang-tidy 所在目录路径中所有.与..会被折叠符号链接会被解析。示例假设源文件example.cpp位于/source目录。存储时只用输入文件名而非源文件完整路径并加当前时间戳前缀。指定-store-check-profile/tmp剖析文件保存到/tmp/ISO8601-like 时间戳-example.cpp.json在/foo目录内运行 clang-tidy 并指定-store-check-profile.剖析文件仍会保存到/foo/ISO8601-like 时间戳-example.cpp.json。这一能力对应仓库中的 ClangTidyProfiling.cpp它实现了计时表输出与 JSON 落盘逻辑供希望理解剖析实现细节的读者继续深入。结语从「会写」到「被社区接受」围绕 Contributing.rst 所描述的完整闭环一个高质量 clang-tidy 检查的诞生路径可以概括为正确定位检查形态 → 用add_new_check.py生成模块化骨架 → 用 AST Matcher /PPCallbacks/ Transformer 实现匹配与修复 → 通过storeOptions暴露可控选项 → 用check_clang_tidy.py-std矩阵覆盖宏、模板、头文件等边界 → 提交前用clang-tidy-diff.py自检并在 PR 中沟通 → 面向社区接受度持续控制误报率。仓库内 clang-tidy/add_new_check.py786 行含 CMake/模块/ReleaseNotes/文档/测试五处自动修改与 clang-tidy 目录下各模块的现成检查对为上述每一步都提供了可直接对照、可逐行研读的真实实现样例。【免费下载链接】llvm-projectThe LLVM Project is a collection of modular and reusable compiler and toolchain technologies.项目地址: https://gitcode.com/GitHub_Trending/ll/llvm-project创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考