规则检查器-环境介绍及抽象语法树初步认识
本文主要简单介绍,llvm和clang环境搭建,抽象语法树及checker的简单认识
llvm/clang背景
- llvm介绍
llvm是一个模块化和可重用的编译器和工具链技术的集合。
LLVM最初是在2000年由伊利诺伊大学香槟分校(UUIC)的学生Chris Lattner及其硕士顾问Vikram Adve创建的研究项目,并在2003年发布第一个正式版本,目的是提供一种基于SSA的现代编译策略,这种策略能够支持任何编程语言的静态和动态编译。
LLVM是当今最流行的开源编译器框架项目,你可以使用它编写自己的编译器。
LLVM的命名最早源自于底层虚拟机(Low Level Virtual Machine)的首字母缩写,由于这个项目的范围并不局限于创建一个虚拟机,这个缩写导致了广泛的疑惑。LLVM开始成长之后,成为众多编译工具及低端工具技术的统称,使得这个名字变得更不贴切,开发者因而决定放弃这个缩写的意涵,现今LLVM已单纯成为一个品牌,适用于LLVM下的所有项目
- Clang介绍
Clang:是一个C、C++、Objective-C和Objective-C++编程语言的编译器前端。它采用了底层虚拟机(LLVM)作为其后端。它的目标是提供一个GNU编译器套装(GCC)的替代品。作者是克里斯·拉特纳(Chris Lattner),在苹果公司的赞助支持下进行开发,而源代码授权是使用类BSD的伊利诺伊大学厄巴纳-香槟分校开源码许可。Clang主要由C++编写。
Clang项目包括Clang前端和Clang静态分析器等。这个软件项目在2005年由苹果电脑发起,是LLVM(Low Level Virtual Machine)编译器工具集的前端(front-end),目的是输出代码对应的抽象语法树(Abstract Syntax Tree, AST),并将代码编译成LLVM Bitcode。接着在后端(back-end)使用LLVM编译成平台相关的机器语言。
Clang本身性能优异,其生成的AST所耗用掉的内存仅仅是GCC的20%左右。2014年1月发行的FreeBSD10.0版将Clang/LLVM作为默认编译器。
- Clang-Tidy介绍
clang-tidy是一个源码分析工具,用于检查 C、 C++和 Objective-C 程序的bugs.
该工具是完全开源的,并且是Clang项目的一部分. 和Clang其他部分一样,该工具作为C++库的方式实现,可以方便的集成于其他工具和程序.
什么是LLVM IR
- LLVM IR 是一门低级语言,语法类似于汇编,是编译中程序的间表示
- 任何高级编程语言(如C++)都可以用LLVM IR表示
- 基于LLVM IR可以很方便地进行代码优化
参考链接:https://www.cnblogs.com/Tu9oh0st/p/16358531.html

LLVM/CLANG 关系介绍
Clang为前端llvm架构前端
1.源代码通过clang的词法分析生成tokens
2.通过clang的语法分析生成了AST抽象语法树
3.通过clang的语义分析生成中间表示IR
Clang-tidy
LLVM后端
1.负责优化IR代码
2.负责生成目标程序

LLVM/CLANG 环境准备
1.查看环境依赖,升级低版本检查工具 Getting Started with the LLVM System - Requirements
2. cmake版本过低:
去https://cmake.org/files/下载所需版本的源码。也可以使用wget下载,例如:
wget https://cmake.org/files/v3.22/cmake-3.22.1.tar.gz
- 解压:tar -xvzf cmake-3.22.1.tar.gz
- 进入解压目录,配置成功之后显示:CMake has bootstrapped. Now run make.
chmod 777 ./configure
./configure
- 配置完成后,编译:make
- 编译完成后,安装: sudo make install
- 最后使用新安装的cmake替换旧版本,其中/usr/local/bin/cmake为新安装的cmake目录。
sudo update-alternatives --install /usr/bin/cmake cmake /usr/local/bin/cmake 1 --force

3.下载ninja和gcc
sudo apt install ninja-build
sudo apt install gcc g++
4.下载 LLVM工程:
- 创建文件夹并下载llvm工程:git clone https://github.com/llvm/llvm-project.git

- 编译 LLVM 和 Clang:
cd llvm-project
mkdir build (in-tree build is not supported)
cd build
- 下方命令会同时以release模式编译 LLVM、Clang以及Clang-tidy:
方式一:cmake -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_BUILD_TYPE=Release -G "Unix Makefiles" ../llvm
make (等的花都谢了,编的好慢啊!)
方式二:以ninja方式编译:
cmake -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DCMAKE_BUILD_TYPE=Release -G "Ninja" ../llvm
ninja(小内存ninja -j2)
sudo ninja install 安装到系统
5.LLVM安装完成状态验证
通过执行clang-tidy -list-checks -checks=*命令,我们可以查看当前可用的所有check。
而如果想要执行某个检查,那就执行 clang-tidy --checks='check名字' 被测文件
- 添加 llvm/build/bin 到path:
export PATH=/XXXl/llvm-project/build/bin/:$PATH
clang --help
clang file.c -fsyntax-only (正确性检查)
clang file.c -S -emit-llvm -o - (打印未优化的llvm代码)
clang file.c -S -emit-llvm -o - -O3
clang file.c -S -O3 -o - (输出本地机器码)
运行测试套件:
make check-clang
抽象语法树
抽象语法树(Abstract Syntax Tree,AST)
Clang的AST非常类似于编写的C++代码和C++标准。AST中每一个节点都是一个Decl或者Stmt类的一个实例。
1. 声明类Decl
Decl用于表示在一个声明或者定义,包括变量、typedef定义、函数和结构体等。它是一个基类,不同的声明表达式都继承自它。如VarDecl类、 FunctionDecl类。
2.语句类Stmt
- Stmt用于表示在源代码中的语句,包括简单语句和复杂语句,程序中的函数都是由语句组成的。
3.Stmt也是一个基类,不同的语句都继承自它。
- 在C语言中最简单的语句形式是表达式。
- 大多数C语言语句是这类形式的,其他简单语句是跳转语句包括return、continue、goto.
- 复杂语句由简答语句组成,可分为两类:跳转语句和循环语句,跳转语句包括if/else、switch,循环语句包括while、do-while和for循环结构。
查看抽象语法树
系统提供一个内置的ast-dump方法,可以把整段代码按照AST结构翻译成人类可以读懂的格式。这种格式基于遍历整段代码,然后按照代码结构对AST节点进行相应层次结构的组合。
Demo:test.cc
int f(int x) {
int result=(x/42);
return result;
默认情况下,Clang 是许多工具的前端;-Xclang 用于将选项直接传递给 C++的前端。
-fsyntax-only 只进行语法检查,不进行编译。
查看一下上面代码对应生成的抽象语法树AST:
clang -Xclang -ast-dump -fsyntax-only test.cc

- 在一个翻译单元中的顶层声明始终是translation unitdeclaration
- 声明是一个“f”的函数,则f的主体是一个符合语句类型的节点(CompoundStmt),它的子节点则是声明语句(DeclStmt)节点,和返回语句(ReturnStmt)节点。
- Clang的AST节点是基于层级关系组合在一起的。
自定义检查器注册
注册接口介绍
1. 基于抽象语法树匹配的缺陷检测主要分为两个部分:
- 一部分是匹配机制的实现,由基于抽象语法树匹配器的注册组成。
- 一部分是匹配对象的实现,主要包括声明类Decl、语句类Stmt。
void registerMatchers(ast matchers::MatchFinder*Finder)override
void check(const ast matchers::MatchFinder::MatchResult &Result)override;

2.通过add_new_check.py脚本添加自定义check
此处为check创建到misc类别,自定义为CcTestCheckCheck,用于测试
./add_new_check.py misc cc-test-check(会自动将cc-test-check生成对应的基础demo,主要修改CcTestCheckCheck.cpp实现功能)

使能自定义checker
- 编写checker后重新编译项目:
cd <path of llvm>/build/
ninja开始编译
sudo ninja install安装到系统
- 安装后查看编写的 checker
clang-tidy --list-checks --checks=misc*

- 运用编写的checker
clang-tidy --checks=-*,misc-cc-test-check test.cc --

- Note:如果要dump内容包含引用头文件,建议加上-nostdinc++
clang -Xclang -ast-dump -nostdinc++ -fsyntax-only CcTestCheckCheck.cpp