Clang Static Analyzer (4) Clang-Tidy 原创 精华
Clang Static Analyzer (4) Clang-Tidy
前文介绍CodeChecker时,使用到了Clang-Tidy,我们来看看这个工具是什么,如何使用。主要是为了了解下背后的知识点,使用CodeChecker已经很好用了。
1、Clang-Tidy介绍
Clang-Tidy是一个基于Clang的C++ “linter” 工具。绝大部分lint工具只能在出现问题的代码地方给出提示,之后需要人为修改,而clang-tidy则能够自动修复功能。当然这个如何修复,需要该check作者提供。clang-tidy 的目的是为诊断和修复典型编程错误提供一个可扩展的框架,如样式违规、接口滥用或可以通过静态分析推断的缺陷。clang-tidy 是模块化的,提供了便利的接口来增加新的check检查器。如果用户想往clang-tidy添加一个新的检测功能,只需要编写一个clang-tidy check实现。每一个check检测一种问题,例如检测某个违反Code style的模式,检测某些API不正确使用的方法等。
2、Clang-Tidy使用入门
clang-tidy是一个基于LibTooling的工具,如果为项目设置编译命令数据库,clang-tidy更容易工作。如何设置编译命令数据的例子,请参阅如何设置 LLVM 的工具。您还可以在命令行--
符号之后指定编译选项
clang-tidy test.cpp -- -Imy_project/include -DMY_DEFINES ...
clang-tidy有自己的checks检查器,也可以运行Clang Static Analyzer的checks检查器。每个check检查器都有一个名称,可以使用选项-checks=
选择要运行的检查,该选项指定了以逗号分隔的正和 负(前缀为-)的globs模式。正模式为要添加的检查器集合,负的模式会删除检查器集合。例如,下面的例子将禁用所有的检查(-*)
,并且启用除 clang-analyzer-cplusplus*
之外的所有匹配clang-analyzer-*
模式的检查器。
$ clang-tidy test.cpp -checks=-*,clang-analyzer-*,-clang-analyzer-cplusplus*
命令行选项-list-checks
会列出所有已启用的检查。当不带选项-checks=
时,它会显示默认启用的检查器。使用-checks=*
时,会查看所有可用的检查器;指定具体值-checks=XXX
时,会查看匹配该模式值的检查器。可用自己体验下。
clang-tidy -list-checks
clang-tidy -list-checks -checks=*
clang-tidy -list-checks -checks=-*,clang-analyzer-*,-clang-analyzer-cplusplus*
目前有以下检查组:
2.1 具体示例
可以使用之前的hello.c,看下怎么使用。如上文所说,一般不会直接使用clang-tidy,使用CodeChecker更好一些,需要了解下即可。
int main() {
int x = 7 / 0; // bug here
return 0;
}
执行如下命令:
clang-tidy hello.c
clang-tidy --checks=* hello.c
选择一条命令执行,输出类似下文的输出。可以看到输出了被各种检查器诊断出来的缺陷或者告警信息。
zhushangyuan@DESKTOP-RPE9R4O:~/CSA$ clang-tidy --checks=* hello.c
5 warnings generated.
/home/zhushangyuan/CSA/hello.c:2:7: warning: Value stored to 'x' during its initialization is never read [clang-analyzer-deadcode.DeadStores]
int x = 7 / 0; // bug here
^
/home/zhushangyuan/CSA/hello.c:2:7: note: Value stored to 'x' during its initialization is never read
/home/zhushangyuan/CSA/hello.c:2:11: warning: 7 is a magic number; consider replacing it with a named constant [cppcoreguidelines-avoid-magic-numbers,readability-magic-numbers]
int x = 7 / 0; // bug here
^
/home/zhushangyuan/CSA/hello.c:2:13: warning: Division by zero [clang-analyzer-core.DivideZero]
int x = 7 / 0; // bug here
^
/home/zhushangyuan/CSA/hello.c:2:13: note: Division by zero
/home/zhushangyuan/CSA/hello.c:2:13: warning: division by zero is undefined [clang-diagnostic-division-by-zero]
int x = 7 / 0; // bug here
^
3、检查器分组
目前有以下检查器分组。不同的分组针对不同的检查对象,不同的开源项目Android、Fuchsia等,不同的编码规范等,可以针对性启用。
Name prefix | Description |
---|---|
abseil- |
Checks related to Abseil library. |
altera- |
Checks related to OpenCL programming for FPGAs. |
android- |
Checks related to Android. |
boost- |
Checks related to Boost library. |
bugprone- |
Checks that target bug-prone code constructs. |
cert- |
Checks related to CERT Secure Coding Guidelines. |
clang-analyzer- |
Clang Static Analyzer checks. |
concurrency- |
Checks related to concurrent programming (including threads, fibers, coroutines, etc.). |
cppcoreguidelines- |
Checks related to C++ Core Guidelines. |
darwin- |
Checks related to Darwin coding conventions. |
fuchsia- |
Checks related to Fuchsia coding conventions. |
google- |
Checks related to Google coding conventions. |
hicpp- |
Checks related to High Integrity C++ Coding Standard. |
linuxkernel- |
Checks related to the Linux Kernel coding conventions. |
llvm- |
Checks related to the LLVM coding conventions. |
llvmlibc- |
Checks related to the LLVM-libc coding standards. |
misc- |
Checks that we didn’t have a better category for. |
modernize- |
Checks that advocate usage of modern (currently “modern” means “C++11”) language constructs. |
mpi- |
Checks related to MPI (Message Passing Interface). |
objc- |
Checks related to Objective-C coding conventions. |
openmp- |
Checks related to OpenMP API. |
performance- |
Checks that target performance-related issues. |
portability- |
Checks that target portability-related issues that don’t relate to any particular coding style. |
readability- |
Checks that target readability-related issues that don’t relate to any particular coding style. |
zircon- |
Checks related to Zircon kernel coding conventions. |
Clang语言的静态分析和clang-tidy检查器的静态分析类似。Clang的静态分析会被clang-tidy展示,也会通过选项-checks=
被过滤掉。然而,这些检查器的过滤不会影响编译参考,因此它不能打开已经在构建配置中关闭的Clang告警开关。-warnings-as-errors=
选项会把-checks=
选项的检查器检测出的告警信息升级为错误信息。
Clang静态分析诊断的检查器名称以clang-diagnostic-
开头。对应每一个告警选项的分析诊断,其名称格式为are named clang-diagnostic-<warning-option>
。例如,被编译选项-Wliteral-conversion
控制的Clang告警,会被名为clang-diagnostic-literal-conversion
的检查器来分析并报告。
The -fix flag instructs clang-tidy to fix found errors if supported by corresponding checks.
有个比较重要的选项,--fix
,开启这个选项clang-tidy会修复发现的错误,在对应的检查器支持的情况下。哪些检查器支持自动修复,可以参考下文中检查器列表中的Offers fixes
字段。使用clang-tidy --help
可以查看帮助信息,我们这里主要看下--fix
相关的帮助信息。开启这个选项--fix
,clang-tidy会修复发现的错误。没有指定--fix-errors
选项时,如果发现编译错误,clang-tidy会跳过修复。在指定--fix-errors
选项时,即使发现编译错误,也会继续修复。
--fix -
Apply suggested fixes. Without -fix-errors
clang-tidy will bail out if any compilation
errors were found.
--fix-errors -
Apply suggested fixes even if compilation
errors were found. If compiler errors have
attached fix-its, clang-tidy will apply them as
well.
没有亲自体验过,执行下面的命令,如果发现示例文件中的未使用的声明using declarations
的告警信息,就会自动修复删除掉。
// 找出simple.cc中所有没有用到的using declarations并自动fix(删除掉)
$ clang-tidy -checks="-*,misc-unused-using-decls" -fix path/to/simple.cc --
4、检查器列表
Clang-Tidy 现在支持<mark>四五百个</mark>Checks检查器,详细列表可以访问clang-tidy - Clang-Tidy Checks — Extra Clang Tools 16.0.0git documentation获取。可以看到对于这些检查器,是否支持自动修复错误。对于这些检查器,也是很好的学习资源,可以看看这些检查器的会修复什么类型的缺陷,以后写代码的时候,避免编写这些的缺陷,提升编程能力和素养。
- Clang-Tidy Checks列表片段
Name | Offers fixes |
---|---|
abseil-cleanup-ctad | Yes |
… | … |
android-cloexec-creat | Yes |
… | … |
android-comparison-in-temp-failure-retry | |
boost-use-to-string | Yes |
bugprone-argument-comment | Yes |
… | … |
cert-dcl21-cpp | Yes |
… | … |
clang-analyzer-core.DynamicTypePropagation | |
… | … |
5、学习些检查器
在使用clang-tidy检查报告,可以详细了解下检查器的知识,可以有助于我们理解这些检查规则,有助于如何修复。挑选几个学习下。
5.1 readability-duplicate-include
详细链接在https://clang.llvm.org/extra/clang-tidy/checks/readability/duplicate-include.html。
Looks for duplicate includes and removes them. The check maintains a list of included files and looks for duplicates. If a macro is defined or undefined then the list of included files is cleared.
查找重复的include语句并删除它们。该检查器维护一个include文件列表,然后查找重复项。如果已定义或取消定义宏,include文件列表会被清理。
示例如下:
#include <memory>
#include <vector>
#include <memory>
修复方法:
#include <memory>
#include <vector>
如下示例,因为中间出现宏定义,不会识别出重复include,代码不会被自动修复。
#undef NDEBUG
#include "assertion.h"
// ...code with assertions enabled
#define NDEBUG
#include "assertion.h"
// ...code with assertions disabled
5.2 readability-delete-null-pointer
详细链接在https://clang.llvm.org/extra/clang-tidy/checks/readability/delete-null-pointer.html。
在if语句里,如果检测指针是否存在,然后删除。这样的检查是没有必要的,因为删除空指针没有任何左右,属于可以删除的冗余代码。
int *p;
if (p)
delete p;
5.3 misc-unused-parameters
详细链接在https://clang.llvm.org/extra/clang-tidy/checks/misc/unused-parameters.html。
查找未使用的函数参数。未使用的参数可能意味着代码缺陷,例如,当使用不同的参数代替时。建议的修复要么是注释参数名称或完全删除参数,只要函数的调用方位于同一翻译单元中,并且可以修改。
该检查器类似于编译器诊断选项-Wunused-parameter
,可以用于准备代码库以启用该诊断。默认情况下,该检查器比较宽松。
示例1:
void a(int i) { /*some code that doesn't use `i`*/ }
// becomes
void a(int /*i*/) { /*some code that doesn't use `i`*/ }
示例2:
static void staticFunctionA(int i);
static void staticFunctionA(int i) { /*some code that doesn't use `i`*/ }
// becomes
static void staticFunctionA()
static void staticFunctionA() { /*some code that doesn't use `i`*/ }
直接能对代码自动修复,大佬们开发工具的效果总能超出我的想象。
LLVM Clang真是太优秀了