Clang-tidy

前导信息

LLVM

LLVM(Low Level Virtual Machine)是一种工具链技术,是模块化和可复用编译器的集合,用它可以实现不同语言,并支持不同硬件平台。

alt text

上图中:

Clang

Clang是LLVM项目的一个子项目,是基于LLVM架构的C/C++/Objective-C编译器前端。

Clang-tidy

Clang-Tidy 是一款基于 LLVM/Clang 框架的强大静态代码分析工具,专门用于检测 C++ 代码中的潜在问题和改进建议。它通过生成和解析抽象语法树(AST)深入理解代码结构和语义,从而提供高精度的检测结果,帮助开发者提升代码质量。

因为它基于AST,所以要比基于正则表达式的静态检查工具更为精准,但是带来的缺点就是要比基于正则表达式的静态检查工具慢一点。也是因为它基于AST,所以clang-tidy运行的时候需要知道编译命令。

说白了,就是可以检查编码规范,比如:

与cpplint等工具不同的是,clang-tidy不仅仅可以做静态检查,还可以做一些修复工作。

clang-tidy实现有100+个check,详见^[1]。根据check不同种类,分为如下几大类:

Clang-tidy检测一个文件的过程大致如下:

1 Clang-tidy配置

1.1 Visual Studio

Visual Studio 2019或更新的版本已经自带了Clang-tidy^[2],如果没有,打开Visual Studio Installer,选择“使用C++的桌面开发”,在右侧工具集中找到“适用于Windows的C++ Clang工具”并勾选,点击修改按钮确认:

alt text

安装完成以后,打开项目,右键项目选择“属性”,在Code Analysis中找到Clang-Tidy并启用:

alt text

1.2 Windows

Clang-tidy包含在LLVM中,要在Windows上使用clang-tidy,需要从llvm-project下载并安装LLVM。

安装完成后,clang-tidy可执行文件位于${INSTALL_DIR}\LLVM\bin\clang-tidy.exe.

然后,从这里下载run-clang-tidy.py,用于快速调用检查工具。

1.3 Ubuntu

ubuntu 16.04默认安装的是3.8版本:

sudo apt install clang-tidy clang

ubuntu 16.04 apt最高可安装6.0版本:

sudo apt install clang-tidy-6.0 clang-6.0 clang-tools-6.0

这里以6.0为例,安装完成后,可执行文件位于/usr/bin/clang-tidy-6,且自带run-clang-tidy.py,位于/usr/bin/run-clang-tidy-6.0.py。

更多信息请参见[3]。

1.4 CMake

自3.7.2起,CMake已经集成了Clang-tidy,在CMakeLists.txt中进行如下设定即可启用:

set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=*")

更多信息请参见[4]。

2 Clang-tidy使用

2.1 规则文件 .clang-tidy

自行创建一个名为.clang-tidy的文件,填入规则

这里给出了规则列表命名规范

2.2 编译命令 Compile Commands

由于clang-tidy基于AST进行分析检查,需要预先知道被检查代码的编译命令compile_commands.json。

2.2.1 通过Visual Studio(不推荐)

右键项目选择“分析和代码清理”,选择“运行代码分析”,即可完成生成${PROJECT_ROOT}/build/${PROJECT_NAME}.dir/Debug/${PROJECT_NAME}.ClangTidy/compile_commands.json

但实测下来发现这种方法生成的compile_commands.json不完整,具体原因有待进一步研究。

2.2.2 通过CMake

编译时加上-DCMAKE_EXPORT_COMPILE_COMMANDS=ON命令,但需要注意,该命令仅适用于Makefile和Ninja,不支持MSVC^[2]。

错误示例:

cmake -S . -B build -G "Visual Studio 16 2019" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

不会生成compile_commands.json。

正确示例:

cmake -S . -B build -G "Unix Makefiles" -DCMAKE_EXPORT_COMPILE_COMMANDS=ON

可以生成compile_commands.json,一般位于${PROJECT_ROOT}/build/compile_commands.json

2.3 运行代码检查

一般地,当clang-tidy安装完成后,可通过命令行调用检查,这里以ubuntu为例:

clang-tidy-6.0 --list-checks -checks='*'

但每次都要输一大堆参数,这里更推荐使用run-clang-tidy.py进行更灵活的配置与调度。

新建一个shell script:

#!/bin/sh

clang_bin_dir="/usr/bin"
clang_tidy_binary="${clang_bin_dir}/clang-tidy-6.0"
clang_apply_replacements="${clang_bin_dir}/clang-apply-replacements-6.0"
clang_tidy_python_script="./run-clang-tidy-6.0.py"
compile_commands="./compile_commands.json"
tmp_compile_commands="./tmp/compile_commands.json"

cat $compile_commands | jq -c '[.[] | select(.file | contains("extern") | not)]' > $tmp_compile_commands

python "$clang_tidy_python_script" \
    -clang-tidy-binary "$clang_tidy_binary" \
    -clang-apply-replacements "$clang_apply_replacements" \
    -p "./tmp" \
    -header-filter='^((?!/external/|/src/external/).)*$' \
    -checks='-*,modernize-use-nullptr' \
    -export-fixes ./summary.yml \
    -fix

变量含义如下:

这段script的作用是对compile_commands进行预处理,然后利用预处理的compile_commands调用clang-tidy检查。

clang-tidy基于compile_commands.json进行检查,但这个json中可能会包含项目之外的文件,例如第三方依赖或系统库,我们不能,也不希望去修改这些文件,因此需要对compile_commands.json进行修改,以排除这些文件:

cat $compile_commands | jq -c '[.[] | select(.file | contains("extern") | not)]' > $tmp_compile_commands

上面命令的作用是读取compile_commands,并filter掉extern文件夹,然后将新的编译命令保存至tmp_compile_commands。

同理,也可以指定想要分析的文件夹:

cat $compile_commands | jq -c '[.[] | select(.file | contains("my_proj") | not)]' > $tmp_compile_commands

这里就只会保留my_proj下代码的compile commands。

下面这段python调用是真正clang-tidy检查的部分:

python "$clang_tidy_python_script" \
    -clang-tidy-binary "$clang_tidy_binary" \
    -clang-apply-replacements "$clang_apply_replacements" \
    -p "./tmp" \
    -header-filter='^((?!/src/external/|/include/external/).)*$' \
    -checks='-*,modernize-use-nullptr' \
    -export-fixes ./summary.yml \
    -fix

重要参数:

-p:compile_commands.json所在的目录

-header-filter:需要跳过检查文件的目录,按照正则表达式的形式填写

-checks:需要检查的规则,这里只检查了modernize-use-nullptr。或者不使用“-checks=”选项,而在项目主目录之下添加.clang-tidy文件,在里面编写项目的检查规则,这种方式更加适合对整个项目进行定制化的规则编写。.clang-tidy文件并不是必须放在主目录之下,只是通常放在主目录之下方便对整个项目进行检查。

-export-fixes:将检查到的问题保存到本地文件

-fix:尝试修复问题

References

  1. https://clang.llvm.org/extra/clang-tidy/checks/list.html
  2. https://learn.microsoft.com/zh-cn/cpp/code-quality/clang-tidy?view=msvc-160
  3. https://fekir.info/post/clang-tidy-windows/
  4. https://ortogonal.github.io/cmake-clang-tidy/
  5. https://cmake.org/cmake/help/latest/variable/CMAKE_EXPORT_COMPILE_COMMANDS.html#cmake-export-compile-commands
  6. https://stackoverflow.com/questions/61001314/what-is-the-correct-way-of-providing-header-filter-for-clang-tidy-in-cmake