Clang Static Analyzer (4) Clang-Tidy 原创 精华

zhushangyuan_
发布于 2022-12-8 20:02
浏览
1收藏

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`*/ }

6、参考站点

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
标签
已于2023-6-1 09:49:44修改
3
收藏 1
回复
举报
2条回复
按时间正序
/
按时间倒序
红叶亦知秋
红叶亦知秋

直接能对代码自动修复,大佬们开发工具的效果总能超出我的想象。


回复
2022-12-9 10:16:00
zhushangyuan_
zhushangyuan_ 回复了 红叶亦知秋
直接能对代码自动修复,大佬们开发工具的效果总能超出我的想象。

LLVM Clang真是太优秀了

1
回复
2022-12-9 10:43:11
回复
    相关推荐