Clang为编写需要程序语法和语义信息的工具提供了基础设施。主要提供了三种方式来使用Clang库
LibClang
是你要使用的接口。只有当你有充分的理由不使用 LibClang 时才考虑其他接口。
使用 LibClang 的典型例子:
- Xcode
- Clang Python 绑定
请使用 LibClang 的场景:
- 想从 C++ 以外的其他语言与 clang 对接
- 需要一个稳定的接口,并注意向后兼容
- 想要强大的高级抽象,比如用游标迭代AST,而不想学习Clang的AST的所有细节。
请不要使用LibClang的场景:
想完全控制Clang的AST
LibClang使用示例
使用LibClang的python绑定,pip install libclang
# test.py
import os
import sys
import clang.cindex
from clang.cindex import Config, TypeKind, CursorKind, Index
def traverse_argument(node, arg):
pass
def traverse_node(node, depth):
if node.location.file:
(path, filename) = os.path.split(node.location.file.name)
if sys.argv[1] == filename:
try:
# CursorKind.CONSTRUCTOR excluded: references there are often stored, so not o_ or io_
if node.kind in [CursorKind.FUNCTION_DECL, CursorKind.CXX_METHOD]:
for arg in node.get_arguments():
traverse_argument(node, arg)
kind = str(node.kind)
print("%s%s %s (%s)" % ("| " * depth, kind, node.spelling, node.location.line))
except:
return
for n in node.get_children():
traverse_node(n, depth + 1)
def main():
index = clang.cindex.Index.create()
# 指定编译参数
compile_args = []
file = sys.argv[1]
parser = index.parse(sys.argv[1], args=compile_args)
cursor = parser.cursor
# 实现同上
traverse_node(cursor, 0)
pass
if __name__ == "__main__":
main()
// test.cpp
#include <stdio.h>
#include <iostream>
#include <string>
#include <vector>
using namespace std;
namespace Print{
class Print{
public:
int print(std::string text){
std::cout << text << std::endl;
return 0;
}
};
int print(std::vector<string> texts){
for(auto text : texts){
std::cout << text << std::endl;
}
return 0;
}
}
int main() {
std::vector<std::string> vts = {"Hello World", "nihao"};
Print::print(vts);
Print::Print prt;
std::string text = {"Hello World"};
prt.print(text);
return 0;
}
执行python test.py test.cpp
| CursorKind.USING_DIRECTIVE (7)
| | CursorKind.NAMESPACE_REF std (7)
| CursorKind.NAMESPACE Print (8)
| | CursorKind.CLASS_DECL Print (9)
| | | CursorKind.CXX_ACCESS_SPEC_DECL (10)
| | | CursorKind.CXX_METHOD print (11)
| | | | CursorKind.PARM_DECL text (11)
| | | | | CursorKind.NAMESPACE_REF std (11)
| | | | | CursorKind.TYPE_REF std::string (11)
| | | | CursorKind.COMPOUND_STMT (11)
| | | | | CursorKind.CALL_EXPR operator<< (12)
| | | | | | CursorKind.CALL_EXPR operator<< (12)
| | | | | | | CursorKind.DECL_REF_EXPR cout (12)
| | | | | | | | CursorKind.NAMESPACE_REF std (12)
| | | | | | | CursorKind.UNEXPOSED_EXPR operator<< (12)
| | | | | | | | CursorKind.DECL_REF_EXPR operator<< (12)
| | | | | | | CursorKind.UNEXPOSED_EXPR text (12)
| | | | | | | | CursorKind.DECL_REF_EXPR text (12)
| | | | | | CursorKind.UNEXPOSED_EXPR operator<< (12)
| | | | | | | CursorKind.DECL_REF_EXPR operator<< (12)
| | | | | | CursorKind.UNEXPOSED_EXPR endl (12)
| | | | | | | CursorKind.DECL_REF_EXPR endl (12)
| | | | | | | | CursorKind.NAMESPACE_REF std (12)
| | | | | CursorKind.RETURN_STMT (13)
| | | | | | CursorKind.INTEGER_LITERAL (13)
| | CursorKind.FUNCTION_DECL print (18)
| | | CursorKind.PARM_DECL texts (18)
| | | | CursorKind.NAMESPACE_REF std (18)
| | | | CursorKind.TEMPLATE_REF vector (18)
| | | | CursorKind.TYPE_REF std::string (18)
| | | CursorKind.COMPOUND_STMT (18)
| | | | CursorKind.CXX_FOR_RANGE_STMT (19)
| | | | | CursorKind.VAR_DECL text (19)
| | | | | | CursorKind.CALL_EXPR basic_string (19)
| | | | | | | CursorKind.UNEXPOSED_EXPR operator* (19)
| | | | | | | | CursorKind.CALL_EXPR operator* (19)
| | | | | | | | | CursorKind.UNEXPOSED_EXPR __begin1 (19)
| | | | | | | | | | CursorKind.DECL_REF_EXPR __begin1 (19)
| | | | | | | | | CursorKind.UNEXPOSED_EXPR operator* (19)
| | | | | | | | | | CursorKind.DECL_REF_EXPR operator* (19)
| | | | | CursorKind.DECL_REF_EXPR texts (19)
| | | | | CursorKind.COMPOUND_STMT (19)
| | | | | | CursorKind.CALL_EXPR operator<< (20)
| | | | | | | CursorKind.CALL_EXPR operator<< (20)
| | | | | | | | CursorKind.DECL_REF_EXPR cout (20)
| | | | | | | | | CursorKind.NAMESPACE_REF std (20)
| | | | | | | | CursorKind.UNEXPOSED_EXPR operator<< (20)
| | | | | | | | | CursorKind.DECL_REF_EXPR operator<< (20)
| | | | | | | | CursorKind.UNEXPOSED_EXPR text (20)
| | | | | | | | | CursorKind.DECL_REF_EXPR text (20)
| | | | | | | CursorKind.UNEXPOSED_EXPR operator<< (20)
| | | | | | | | CursorKind.DECL_REF_EXPR operator<< (20)
| | | | | | | CursorKind.UNEXPOSED_EXPR endl (20)
| | | | | | | | CursorKind.DECL_REF_EXPR endl (20)
| | | | | | | | | CursorKind.NAMESPACE_REF std (20)
| | | | CursorKind.RETURN_STMT (22)
| | | | | CursorKind.INTEGER_LITERAL (22)
| CursorKind.FUNCTION_DECL main (27)
| | CursorKind.COMPOUND_STMT (27)
| | | CursorKind.DECL_STMT (29)
| | | | CursorKind.VAR_DECL vts (29)
| | | | | CursorKind.NAMESPACE_REF std (29)
| | | | | CursorKind.TEMPLATE_REF vector (29)
| | | | | CursorKind.NAMESPACE_REF std (29)
| | | | | CursorKind.TYPE_REF std::string (29)
| | | | | CursorKind.UNEXPOSED_EXPR (29)
| | | | | | CursorKind.CALL_EXPR vector (29)
| | | | | | | CursorKind.UNEXPOSED_EXPR (29)
| | | | | | | | CursorKind.UNEXPOSED_EXPR (29)
| | | | | | | | | CursorKind.UNEXPOSED_EXPR (29)
| | | | | | | | | | CursorKind.INIT_LIST_EXPR (29)
| | | | | | | | | | | CursorKind.STRING_LITERAL "Hello World" (29)
| | | | | | | | | | | CursorKind.STRING_LITERAL "nihao" (29)
| | | CursorKind.UNEXPOSED_EXPR (30)
| | | | CursorKind.CALL_EXPR print (30)
| | | | | CursorKind.UNEXPOSED_EXPR print (30)
| | | | | | CursorKind.DECL_REF_EXPR print (30)
| | | | | | | CursorKind.NAMESPACE_REF Print (30)
| | | | | CursorKind.UNEXPOSED_EXPR (30)
| | | | | | CursorKind.CALL_EXPR vector (30)
| | | | | | | CursorKind.UNEXPOSED_EXPR vts (30)
| | | | | | | | CursorKind.DECL_REF_EXPR vts (30)
| | | CursorKind.DECL_STMT (31)
| | | | CursorKind.VAR_DECL prt (31)
| | | | | CursorKind.NAMESPACE_REF Print (31)
| | | | | CursorKind.TYPE_REF class Print::Print (31)
| | | | | CursorKind.CALL_EXPR Print (31)
| | | CursorKind.DECL_STMT (32)
| | | | CursorKind.VAR_DECL text (32)
| | | | | CursorKind.NAMESPACE_REF std (32)
| | | | | CursorKind.TYPE_REF std::string (32)
| | | | | CursorKind.CALL_EXPR basic_string (32)
| | | | | | CursorKind.UNEXPOSED_EXPR (32)
| | | | | | | CursorKind.STRING_LITERAL "Hello World" (32)
| | | CursorKind.UNEXPOSED_EXPR (33)
| | | | CursorKind.CALL_EXPR print (33)
| | | | | CursorKind.MEMBER_REF_EXPR print (33)
| | | | | | CursorKind.DECL_REF_EXPR prt (33)
| | | | | CursorKind.UNEXPOSED_EXPR (33)
| | | | | | CursorKind.CALL_EXPR basic_string (33)
| | | | | | | CursorKind.UNEXPOSED_EXPR text (33)
| | | | | | | | CursorKind.DECL_REF_EXPR text (33)
| | | CursorKind.RETURN_STMT (34)
| | | | CursorKind.INTEGER_LITERAL (34)
Clang Plugins
Clang插件允许你在AST上运行额外的动作作为编译的一部分。插件是动态库,在运行时由编译器加载,它们很容易集成到你的编译环境中。
使用Clang插件的典型例子:
- 为你的项目提供特殊的lint-style警告或错误
- 从单一的编译步骤中创建额外的构建工件
使用Clang Plugins的场景:
- 需要你的工具在任何依赖性改变时重新运行
- 希望你的工具能够生成或破坏一个构建
- 需要对Clang AST的完全控制
请不要使用Clang插件的场景:
- 想在你的构建环境之外运行工具
- 希望完全控制Clang的设置,包括内存中虚拟文件的映射
- 需要在你的项目中运行一个特定的文件子集,这个子集不一定与任何会触发重建的变化有关
LibTooling
LibTooling是一个C++接口,旨在编写独立的工具,以及集成到运行clang工具的服务中。
使用LibTooling的典型例子:
- 一个简单的语法检查器
- 重构工具
使用LibTooling的场景:
- 想在单个文件或特定的文件子集上运行工具,独立于构建系统之外
- 希望完全控制 Clang AST
- 想与 Clang Plugins 共享代码
请不要使用 LibTooling的场景:
- 想作为依赖关系变化所引发的构建的一部分来运行
- 希望有一个稳定的接口,这样你就不需要在 AST API 发生变化时修改你的代码了
- 希望有高水平的抽象,如游标和代码自动完成。
- 不想用C++编写你的工具
LibTooling 使用示例
下载源码,解压
https://github.com/llvm/llvm-project/releases
进入源码目录,创建build目录并进入,执行cmake命令
cmake -DLLVM_ENABLE_PROJECTS=clang -G "Visual Studio 16 2019" -A x64 -Thost=x64 ..\llvm
找到clang-check工具,基于此文件进行修改,并编译此工具