Clang 新手教程
长话短说
这个新手教程会让你弄清楚什么是clang,clang AST,clang plugins 和clang tools 等等。让你大概知道clang 可以解决什么问题,而且小白也是可以用clang libraries 来开发工具的 :)
Clang 是什么?
Clang 是一个以LLVM为后端的编译前端。
编译前端主要负责parse 源码,检查错误,并生成Abstract Syntax Tree (AST)。
Ref: Understanding the Clang AST
什么时候会用到Clang?
- 需要基于编译器的AST 对源码做精确的编辑
- 需要引入自定义的编译错误和警告
- 基于C/C++ 源码的代码生成(code generation),比如生成数据结构的序列化,语言交互接(Foreign function interface)
Clang AST 是什么?
- Clang AST 的节点是由几种没有共同基类的类来组成(建模)的
Clang’s AST nodes are modeled on a class hierarchy that does not have a common ancestor.
- 其中比较常用的四种class 是
Type
,Decl
,DeclContext
,Stmt
- 每种节点都有专门的遍历函数来获取子节点
- ASTContext 里可以获取AST 的额外信息,比如源码的地址
- 例子
// test.cc
int foo(int num) {
int result = (num / 2);
return result;
}
void test_f() {
int i = 10;
int b = foo(i);
}
clang -Xclang -ast-dump -fsyntax-only test.cc
Ref: Introduction to the Clang AST - Clang 15.0.0git documentation
Clang APIs
Clang 提供C 和C++ API
- C API (libClang) 稳定但是不完整
- C++ API(LibTooling)完整但是不稳定
- LibTooling 主要用于开发standalone 工具,并且plugins 只能使用LibTooling
Ref: Choosing the Right Interface for Your Application - Clang 15.0.0git documentation
Clang plugins v.s. Clang tools
Clang plugin 在编译时对AST 进行一些额外的操作。plugin是动态库(.so),在运行时由编译器加载,可以很容易集成到编译环境中。
Clang tools 是由LibTooling 开发的可执行程序,比如clang-check,clang-format,clang-tidy 这些llvm project 提供的。
有时候两种方式都能解决问题:
# 1. As a loadable Clang plugin:
clang -cc1 -load <BUILD_DIR>/lib/libLACommenter.so '\'
-plugin LACPlugin test/LACInt.cpp
# 2. As a standalone tool:
<BUILD_DIR>/bin/ct-la-commenter test/LACInt.cpp
考虑点:
- 能不能控制用哪个编译器?clang / gcc?
- 需不需要make 或者break build
- Clang 6.0 写的plugin Clang 7.0 不一定能用(API 不稳定!)
- CI 环境使用哪种工具用户接入成本更低?
RecursiveASTVisitor v.s. ASTMatcher
LibTooling 有两种framework: RecursiveASTVisitor 和ASTMatcher
- The RecursiveASTVisitor provides hooks of the form bool VisitNodeType(NodeType *) for most AST nodes
- ASTMatcher 是一套用来匹配和遍历AST 的DSL
- 两种framework 可以一起使用来解决比较复杂的问题!https://github.com/banach-space/clang-tutor/blob/main/README.md#unusedforloopvar
- 优先考虑用ASTMatcher,因为可以用clang-query 来验证DSL 是否正确
clang-query test.cc
# some useful commands to setup clang-query
set output dump
set bind-root false
set traversal IgnoreUnlessSpelledInSource
m functionDecl()
m functionDecl(hasName("foo"))
Ref:
How to write RecursiveASTVisitor based ASTFrontendActions. - Clang 15.0.0git documentation
Tutorial for building tools using LibTooling and LibASTMatchers - Clang 15.0.0git documentation
如何写一个Clang 工具?
- 想清楚目的
- 写一些最小的测试用例源码并用
clang -Xclang -ast-dump
来观察生成的AST - 找到想要匹配的AST 节点
- 使用clang-query 来在测试用例上检验ASTMatcher
- 将ASTMatcher 的DSL集成到clang LibTooling 的脚手架里
怎么创建Clang 工具项目?
In tree
- 编译llvmClang - Getting Started
注意:llvm 编译linking 占用内存非常高!所以要用单线程make,而且16G 内存的话,要将swap file 增加到16G 才能编译。ref: How to increase swap space?
- 在
llvm-project/clang-tools-extra
下创建目录和写MakefileHow to write RecursiveASTVisitor based ASTFrontendActions. - Clang 15.0.0git documentation Out of tree
有用的资源
- Step by step 教程 clang_tool_tutorial.pdf
- clang-tutor https://github.com/banach-space/clang-tutor/blob/main/README.md#clang-tutor