最近尝试将 Clang-tidy 加入到项目中,以便加强代码规范。本文纪录在这一过程中积累的关于 Clang-tidy 的知识,涵盖安装、使用、cmake 和 CI/CD 实践等内容。
Clang-tidy是一个强大的开源工具,用于自动执行C++源代码的静态分析。它是Clang项目的一部分,可以检查代码中的错误、风格问题、性能问题等,并且可以自动修复一些常见问题。
Clang-tidy的一个重要特性是它的可扩展性。你可以编写自己的检查器来检查特定的编程错误或风格问题。这使得Clang-tidy成为一个非常强大的工具,可以定制以适应你的编程风格和需求。
Clang-tidy还可以与其他工具集成,如编辑器和持续集成系统,以自动执行代码检查和修复。这使得它成为提高代码质量和减少编程错误的重要工具。
Clang-tidy检测一个文件的过程大致如下:
Macos
推荐使用 pip 进行安装:
pip install clang-tidy
Linux,使用 apt 或者 pip
apt-get install clang-tidy
pip install clang-tidy
使用 clang-tidy 检测某个文件时需要知道头文件路径等编译信息,这是因为它需要了解代码的上下文环境,以便正确地解析代码并检测潜在的问题。例如,如果代码中使用了某个库的函数,Clang-tidy需要知道该库的头文件路径才能正确地解析函数的声明和定义。如果缺少这些编译信息,Clang-tidy可能会产生误报或漏报的问题。
因此一个 clang-tidy 检查的命令大概是这样的:
clang-tidy --checks='-*' test.cpp -- -I ./src/ -x c++
这个命令的含义如下:
在实际使用 clang-tidy 中,搭配上 cmake 将事半功倍。clang-tidy 中有个 -p 参数,-p build-path
参数是用于指定Clang-tidy读取编译命令数据库的路径。编译命令数据库通常是一个名为compile_commands.json的文件,它包含了编译每个源文件所需的完整命令行,包括编译器选项、头文件路径等信息。
例如,如果你使用CMake进行构建,可以通过设置-DCMAKE_EXPORT_COMPILE_COMMANDS=ON
选项来生成compile_commands.json文件。然后,你可以使用-p参数告诉Clang-tidy在哪里找到这个文件,如clang-tidy -p /path/to/build/directory。
如果没有指定-p参数,Clang-tidy会尝试在第一个输入文件的所有父路径中搜compile_commands.json文件。
这个参数的主要目的是让Clang-tidy能够正确解析源代码。因为C++的语法和语义很大程度上取决于编译上下文,如果没有正确的编译信息,Clang-tidy可能无法正确解析源代码,也就无法进行准确的静态分析。
clang 官方提供了一些脚本,在 clang-tidy 命令行的基础上提供了额外的能力,通过 clang-tidy-diff.py 来检查 git diff 中实际发生修改的代码。你可以这样使用:
git diff branchA master | python clang-tidy.py -path /path/to/compile_commands.json
这个命令的含义如下:
clang-tidy 的检查项非常丰富,这部分可以参考:
如果你不知道检查哪些,那偷个懒直接使用 CLion 的默认配置即可,通过 how-to-dump-clions-default-clang-tidy-configuration 提到的方法生成 .clang-tidy
文件。命令行中使用 --config-file
指定 .clang-tidy
文件即可
clang-tidy ----config-file=/path/to/.clang-tidy ...
在检查项中,可以指定 warning-as-error
,例如
---
Checks: '-*,
bugprone-argument-comment,
bugprone-assert-side-effect,
bugprone-bad-signal-to-kill-thread,
bugprone-copy-constructor-init'
WarningsAsErrors: 'bugprone-*'
这样检测到相关问题后直接报错,方便 ci/cd 进行检查
在CMake中,我们可以通过设置CMAKE_CXX_CLANG_TIDY属性来为整个项目开启Clang-tidy检查。例如:
set(CLANG_TIDY_COMMAND clang-tidy;
-header-filter=^${PROJECT_ROOT_DIR}/src/.*;
-config-file=${PROJECT_ROOT_DIR}/.clang-tidy;)
set(CMAKE_CXX_CLANG_TIDY ${CLANG_TIDY_COMMAND})
在这个例子中,CMAKE_CXX_CLANG_TIDY被设置为一个包含Clang-tidy命令和相关选项的变量。这将导致所有编译的文件都会被Clang-tidy检查。
然而,有时我们可能希望跳过某些文件的检查,比如第三方库的代码。在这种情况下,使用CMAKE_CXX_CLANG_TIDY就不再适用。一个更好的选择是使用set_target_properties命令,只为特定的目标开启Clang-tidy检查。例如:
set_target_properties(myTarget
PROPERTIES
CXX_CLANG_TIDY ${CLANG_TIDY_COMMAND})
在这个例子中,只有myTarget目标的源文件会被Clang-tidy检查,其他没有设置CXX_CLANG_TIDY属性的目标则不会受到影响。
有两种方式将 clang-tidy 加入 CI/CD 中:
对比两种方式各有优劣:
最终选择了方法 2,庆幸的是目前大部分 C/C++ 项目都使用基本使用 cmake 来管理,包括 Android NDK,因此使用起来并无太大麻烦。例如你想在 CI/CD 中检查某个 Android NDK 项目,你可以这么做:
./gradlew assembleFullRelease
编译你的项目compile_commands.json
文件所在git diff A B | python clang-tidy-diff.py -path=/path/compile_commands.json -config-file=/path/.clang-tidy