# 静态代码分析工具Clang-Tidy

# 基本介绍

clang-tidy 是一个基于 clang 的 C++ “linter” 工具。其作用主要是用来检查和修正代码中的典型编程问题,像代码风格/接口误用/通过静态分析可定位到的bugclang-tidy模块化功能做的很好,有预留接口,非常便于实现新的代码检查功能。

# 安装clang-tidy

直接编译安装LLVM工程,包含子工程构建,编译安装的bin路径下有clang-tidy工具。

LLVM编译安装的详细步骤可以参考这里 (opens new window)

# 使用clang-tidy

clang-tidy 是一个基于 LibTooling 的工具,如果您为项目设置编译命令数据库,则使用起来会更容易(有关如何执行此操作的示例,请参阅如何为 LLVM 设置工具)。源码对应的编译命令数据库保存在compile-command.json文件中,这个文件中其实保存的是每个可执行文件的构建命令,现在很多工具可以自动生成这个文件,例如对于使用CMake管理的工程,只需要在编译的时候带上参数cmake -DCMAKE_EXPORT_COMPILE_COMMANDS=ON即可在build目录下生成compile-command.json文件,在执行.clang-tidy命令时设置-p={project}/build即可。

可以在命令行添加--符号,以不使用编译命令而时使用编译选项来定制检测规则,用来实现单个文件的格式检测:

clang-tidy test.cpp -- -Imy_project/include -DMY_DEFINES ...

-I参数指定要包含的头文件路径

clang-tidy有其自己的检测器,也可以使用Clang的静态分析器,每一项检测都有其自己的名字,使用-checks参数可以指定要进行哪些检测,它指定了逗号分隔的正向和反向规则,正向规则指定了要添加的检测子集,反向规则指定了要移除的规则。示例:

clang-tidy -checks='-*,modernize-*' test.cpp --
  • -checks指定要进行哪些检测,-*表示移除所有检测,modernize-*表示保留modernize开头的所有检测。-checks支持的参数有:
checks名称前缀 说明
abseil- Google Abseil相关的检测
altera- 针对FPGAOpenCL相关的检测
android- Android相关检测
boost- Boost库相关检测
clang-analyzer- Clang Static Analyzer检测

更多检测名称前缀参考:

1.https://clang.llvm.org/extra/clang-tidy/index.html#using-clang-tidy (opens new window)

# 配置文件和格式文件

# 配置文件

clang-tidy提供了一个配置文件,可以指定要使用的检测器、检测器的参数等。配置文件使用YAML语法编写,一般放在当前工程目录下,文件名为.clang-tidy

# 指定要使用的检测器
Checks: '-*,modernize-*,performance-*'

# 指定检测器的参数
CheckOptions:
  - key: 'modernize-use-nullptr.NullPtrBeforeZero'
    value: '0'

# 格式文件

clang-tidy命令的--format-style=参数,需要.clang-format文件作为参数来设置修复格式化代码的样式。

--format-style=可选的值有:

  • 'none',默认值,关闭格式化
  • 'file',设置此值clang-tidy会使用最近父目录下的.clang-format文件来设置格式化的样式
  • '{ }'指定格式化选项,例如:-format-style='{BasedOnStyle: llvm, IndentWidth: 8}'
  • 'llvm', 'google', 'webkit', 'mozilla'

一个简单的.clang-format文件的内容:

Language:        Cpp
AccessModifierOffset: -2
AlignAfterOpenBracket: Align
AlignConsecutiveAssignments: false
AlignConsecutiveDeclarations: false
AlignEscapedNewlinesLeft: false
AlignOperands: true

# 使用配置文件和格式化文件

对于单个文件,通过命令行可以直接来运行:

clang-tidy -format-style='file' -config-file=.clang-tidy -p ../build/debug test_clang.cpp

对于工程中有很多文件,写成shell脚本的形式为:

#!/bin/bash

function main() {
    local project_dir
    project_dir=$(git rev-parse --show-toplevel)
    cd "$project_dir" || exit 1

    local srcs
    srcs=$(git ls-files src crossdock |
           grep -E -v 'thrift-gen|Test\.cpp' |
           grep -E '\.cpp$')

    local cmd
    for src in $srcs; do
        cmd="clang-tidy -p=build"
        cmd+=" -checks=\"-clang-diagnostic-unused-command-line-argument\" "
        cmd+=" $src"
        echo "$cmd"
        eval "$cmd"
    done
}

main

# 在代码中设置排除clang-tidy检测

对于工程中的代码,有时候可以确认代码是正常的,但是不满足clang-tidy的规则,这个时候需要增加设置使目标代码免除检测。

通用抑制检测的机制:

  • 对于一行,使用NOLINT注释:

int main(int argc, char** argv) // NOLINT
{
    return 0;
}
  • 下一行免除检测
// NOLINTNEXTLINE

int main(int argc, char** argv)
{
    return 0;
}
  • 设置一段代码免除检测
int main(int argc, char** argv)
{
    // NOLINTBEGIN
    int x;
    int y;
    // NOLINTEND
    return 0;
}

上面的NOLINT注释后可以接参数,设置具体要避免哪些规则的检测:


int main(int argc, char** argv)
{
    // NOLINTBEGIN(google*)
    int x;
    int y;
    int array[10]; // NOLINT(*-avoid-c-arrays)
    // NOLINTEND(google*)
    return 0;
}

# reference

1.https://clang.llvm.org/extra/clang-tidy/index.html#using-clang-tidy (opens new window)