c++静态代码检查-TscanCode

前面两篇文章我们制定了编码规范,还有编码规范检查的方法。编码规范是为了让大家写代码的风格更统一,为了让人更好的阅读理解代码。相比编码规范,我觉得代码静态检查,提前发现潜在的错误更为重要。有很多开源的工具,或者商业的产品提供了静态检查的功能:
开源的工具:TscanCode, cppcheck, clang等
商业收费的工具:pclint, coverity 等

目前只用过 cppcheck, TscanCode, 个人感觉 TscanCode 更加专业一点。 在公司的代码仓库上使用 cppckeck 检查,有很多误报,还有很多编码风格的小问题,真正的严重错误基本上没有发现。用 TscanCode 发现了五十几个严重错误,基本上没有误报的。下面就详细介绍一下 TscanCode 的使用方法,其他工具的就做介绍了,感兴趣的同学请自行搜索。

1. 安装

TscanCode 是开源的工具,代码仓库为:https://github.com/Tencent/TscanCode, 我们可以自己下载下来编译,也可以使用腾讯预编译好的可执行文件(代码仓库的 release 目录)。简单起见,我们直接使用预编译好的文件就行。

1
2
3
4
5
6
7
qiushao@qiushao-pc:~/projects/opensources$ git clone https://github.com/Tencent/TscanCode.git
qiushao@qiushao-pc:~/projects/opensources$ cd TscanCode/release/linux/
qiushao@qiushao-pc:~/projects/opensources/TscanCode/release/linux$ unzip TscanCodeV2.14.24.linux.zip
qiushao@qiushao-pc:~/projects/opensources/TscanCode/release/linux$ cd TscanCodeV2.14.24.linux/TscanCodeV2.14.2395.linux
qiushao@qiushao-pc:~/projects/opensources/TscanCode/release/linux/TscanCodeV2.14.24.linux/TscanCodeV2.14.2395.linux$ chmod a+x tscancode
qiushao@qiushao-pc:~/projects/opensources/TscanCode/release/linux/TscanCodeV2.14.24.linux/TscanCodeV2.14.2395.linux$ echo "PATH=$PATH:$(pwd)" >> ~/.bashrc
qiushao@qiushao-pc:~/projects/opensources/TscanCode/release/linux/TscanCodeV2.14.24.linux/TscanCodeV2.14.2395.linux$ source ~/.bashrc

这样就安装完了。

2. 基本使用方法

我们先看看帮助

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
qiushao@qiushao-pc:~/projects/opensources/TscanCode$ tscancode -h
TscanCode - A tool for static C/C++ code analysis

Syntax:
tscancode [OPTIONS] [files or paths]

If a directory is given instead of a filename, *.cpp, *.cxx, *.cc, *.c++, *.c,
*.tpp, and *.txx files are checked recursively from the given directory.

Options:
-D<ID> Define preprocessor symbol. Unless --max-configs or
--force is used, TscanCode will only check the given
configuration when -D is used.
Example: '-DDEBUG=1 -D__cplusplus'.
-U<ID> Undefine preprocessor symbol. Use -U to explicitly
hide certain #ifdef <ID> code paths from checking.
Example: '-UDEBUG'
--enable=<id> Enable additional checks. The available ids are:
* all
Enable all checks. It is recommended to only
use --enable=all when the whole program is
scanned, because this enables unusedFunction.
* warning
Enable warning messages
* style
Enable all coding style checks. All messages
with the severities 'style', 'performance' and
'portability' are enabled.
* performance
Enable performance messages
* portability
Enable portability messages
* information
Enable information messages
* unusedFunction
Check for unused functions. It is recommend
to only enable this when the whole program is
scanned.
* missingInclude
Warn if there are missing includes. For
detailed information, use '--check-config'.
Several ids can be given if you separate them with
commas. See also --std
-h, --help Print this help.
-I <dir> Give path to search for include files. Give several -I
parameters to give several paths. First given path is
searched for contained header files first. If paths are
relative to source files, this is not needed.
-j <jobs> Start [jobs] threads to do the checking simultaneously.
-q, --quiet Do not show progress reports.
--xml Write results in xml format to error stream (stderr).

Example usage:
# Recursively check the current folder. Print the progress on the screen and
# write errors to a file:
tscancode . 2> err.txt

# Recursively check ../myproject/ and don't print progress:
tscancode --quiet ../myproject/

# Check test.cpp, enable all checks:
tscancode --enable=all test.cpp

# Check f.cpp and search include files from inc1/ and inc2/:
tscancode -I inc1/ -I inc2/ f.cpp

qiushao@qiushao-pc:~/projects/opensources/TscanCode$

语法格式如下:

1
tscancode [OPTIONS] [files or paths]

可以对文件和目录进行扫描,如果是目录的话,会递归扫描目录下的所有 *.cpp, *.cxx, *.cc, *.c++, *.c, *.tpp, and *.txx 文件。
提供以下选项:

  • -D: 定义宏定义。比如:-DDEBUG=1 -D__cplusplus
  • -U: 取消宏定义。比如:-UDEBUG
  • --enable=: 启用附加检查。取值范围:
    • all : 启用所有附加检查。
    • warning : 打开 warning 消息。
    • style : 启用代码风格检查。
    • performance : 启用性能检查。
    • portability : 启用可移植性检查。
    • information : 打开 information 消息。
    • unusedFunction : 启用未调用函数检查。
    • missingInclude : 启用找不到头文件消息。
  • -I: 指定头文件路径。比如:-Ifoobar/include
  • -j: 并发检查线程数。比如: -j8
  • -q: 不打印处理进度,只输出结果。
  • --xml: 指定输出格式为 xml。

使用例子1:
递归检查 samples 目录下所有源文件, 进度信息显示在终端,结果重定向到 result.txt 文件中

1
tscancode samples 2> result.txt

实际使用时发现有错误:

1
2
3
4
5
qiushao@qiushao-pc:~/projects/opensources/TscanCode$ tscancode samples 2> result.txt
Failed to load setting file 'cfg.xml'. File not found
Failed to load library configuration file 'std.cfg'. File not found
Failed to load library configuration file 'posix.cfg'. File not found
Failed to load library configuration file 'windows.cfg'. File not found

看信息是没有找到 cfg.xml 文件, cfg.xml 文件在 tscancode 文件同级目录下。查看代码,发现居然需要使用绝对路径或者相对路径调用 tscancode 才行(也就是调用路径一定要存在/),
改成以下调用方式后就可以了:

1
./release/linux/TscanCodeV2.14.24.linux/TscanCodeV2.14.2395.linux/tscancode ./samples 2> result.txt

呵呵,居然不知道获取执行文件的路径,然后从同级目录下读取 cfg。

使用例子2:
指定头文件目录

1
tscancode -I inc1/ -I inc2/ f.cpp

3. 启用关闭部分检查

配置文件在 tscancode 同级目录下的 cfg/cfg.xml 里面。

1
2
3
4
5
6
7
 <section name="Checks">
<id name="nullpointer" value="1">
<subid name="dereferenceAfterCheck" value="1" severity="Serious" rule_name="先判空后解引用" desc="指针先判空,然后在判空作用域外解引用,因为指针判空暗示当前上下文该指针可能为空,因此建议对没有在>
判空作用域的指针添加判空保护。"/>
<subid name="funcRetNull" value="0" severity="Warning" rule_name="函数返回值未判空解引用" desc="函数存在返回NULL的分支,所以返回值可能为空,应该先判空再使用"/>
<subid name="funcRetNullStatistic" value="1" severity="Serious" rule_name="基于统计推断函数返回值未判空" desc="通过分析代码调用模式,如果调用func函数,有10处对返回值进行了判空,而只有1处没有判空>
,那么很有可能是漏掉了判空。"/>

比如说我们不需要空指针检查,把 value 的值改为 0 即可。

4. 规则扩展

目前看起来好像只能通过修改代码来添加规则了。这里先不讨论了,后面有需求再研究一下。