ClangQuery
在LLVM3.5
中引入ClangQuery
工具,它可读入一个源文件
,交互
查询它所关联的ClangAST
节点.是帮助
查看并学习前端
如何表达每行代码
的很好工具.
然而,它的主要目标
是,你不但可查看程序的AST
,而且可测试AST
匹配器.
编写重构工具
时,你会对使用,包含匹配感兴趣的ClangAST
片段的断定(predicate)
的AST
匹配器库感兴趣
.
ClangQuery
工具可在开发时帮助你,因为它让你可查看哪个AST
节点匹配具体
的AST
匹配器.
你可在ASTMatchers.h
中,查看可用的AST
匹配器的列表,但是也可根据驼峰大小写
的名字,猜测
感兴趣的AST
节点的类.
如,functionDecl
会匹配所有表示函数声明
的FunctionDecl
节点.
试验了哪个
匹配器确切返回感兴趣的节点
后,可在重构工具
中用它们实现自动转换
方法.
如下查看AST
,会对上次PPTrace
中用到的"helloworld"
代码运行clang-query
.ClangQuery
期望有编译命令数据库
.
如果查看没有编译命令数据库
的文件,就在双短划线
后给出编译命令
,或空着它,如果不需要特殊编译器选项
,如下行所示:
$ clang-query hello.c --
发出该命令
后,clang-query
会显示一个等待输入命令的交互提示
.可输入match
命令和任意AST
匹配器的名字.
如,下面命令中,让clang-query
显示所有CallExpr
节点:
clang-query> match callExpr()
Match #1:
hello.c:12:5: note: "root" node binds here
write(1, "Hello, ", 7);
^~~~~~~~~~~~~~~~~~~~~~
...
该工具会高亮
程序中与关联CallExprAST
节点对应的第一个令牌
的确切
位置.ClangQuery
可接受的命令列表如下:
1,help
:打印命令列表.
2,match<matchername>
或m<matchername>
:该命令用请求的匹配器
遍历AST
.
3,set output<(diag|print|dump)>
:该命令在成功匹配后,修改
如何打印节点信息
.第一个选项会打印一个Clang
诊断消息,并高亮
节点,这是默认
选项.
第二个
选项会简单地打印匹配
到的对应源码的摘要
,而最后选项会调用带复杂调试功能
并显示所有子节点
的dump()
类成员函数.
了解程序ClangAST
的结构的一个重要方法
,是修改dump
输出,并匹配高级节点
.试试:
lang-query> set output dump
clang-query> match functionDecl()
它会显示在C源码
中,构成所有函数体
的语句和式
的类
的所有实例.另一方面,记住,用ClangCheck
可更容易得到的该完整AST
转存.
ClangQuery
更适造AST
匹配器式并检查
它们的结果.
ClangCheck
ClangCheck
是个更易学习的只有几百行代码
的非常基础
工具.然而,因为链接了LibTooling
,它具有整个Clang
的解析能力
.
ClangCheck
可解析C/C++
源码文件,转存ClangAST
,及基础
检查.还可应用Clang
给出的"fixit"
修改建议,利用为ClangModernizer
建造的重写器设施.
如,假设想要打印program.c
的AST
,就如下输入:
$ clang-check program.c -ast-dump --
注意,ClangCheck
按LibTooling
读取源文件的方式,可用命令数据库
文件,或在双短划线(--)
后输入适当的参数
.
ClangCheck
是个小工具,编写自己工具
时,可学习它.
去除c_str()
调用
remove-cstr-calls
工具是个简单的源到源
转换(即重构)工具.工作时会识别冗余
的调用std::string
对象的c_str()
,并重写代码来避免它.
有时会冗余调用
,首先,用另一个string
对象或c_str()
的结果来建造新的string
对象时,如,std::string(myString.c_str())
.
这可简化
为直接使用string
拷贝构造器,如,str::string(myString)
.
其次,根据string
对象来建造LLVM
的具体的StringRef
和Twine
类的实例
时.
这时,最好用string
对象自身,而不是其c_str()
的结果,即用StringRef(myString)
,而不是StringRef(myString.c_str())
.
可在单个C++
文件里完整写出该工具
,这是另一个优秀的易学习的演示如何使用LibTooling
建造重构工具
的示例.
编写自己的工具
Clang
项目为用户
提供了包括语法和语义
分析的三个接口
,以利用Clang
的特性和它的解析能力.
首先,libclang
是和Clang
交互的主要方式,它提供了稳定的CAPI
,允许外部项目
嵌入它,并可高级访问整个框架
.
该稳定
接口保持旧版本
兼容,避免因为发布新版的libclang
而破坏你的软件.从其它语言使用libclang
也是可能的,如,使用Clang
的Python
绑定.
Apple的Xcode
,如,它通过libclang
和Clang
交互.
其次,允许编译过程
中添加自己的趟
的Clang
插件,而不是由如Clang
静态分析器等工具执行离线分析
.
每次编译翻译单元
都要执行
它时,这是有用
的.因此,要考虑期望执行该分析
的时间,来决定是否适合频繁
运行.
另一方面,与给编译器增加命令
选项一样,整合分析
到构建
系统,也很容易.
最后还可通过LibTooling
利用Clang
.以重构代码
或检查语义
为目标,它可轻松建造
独立工具.
和LibClang
相比,LibTooling
不与兼容
妥协,可完全访问ClangAST
结构.
问题:编写C++
重构工具
假设创立新的叫IzzyC++
的C++IDE
.
利用LibTooling
制作简单而好用
的C++
代码重构工具;它按C++
成员函数,全名,和替换名
接受参数
.
任务是找到该成员函数
定义,用替代名
修改它,且相应修改所有对该函数
的调用.
配置源码位置
第一步
是决定在哪存放
工具代码.在LLVM
的源码目录中,将新建一个叫izzyrefactor
的目录,在tools/clang/tools/extra
中,保存项目的所有文件
.
之后,扩展extra
目录中的Makefile
,以包含你的项目.简单,找到DIRS
变量,并在其它Clang
工具项目的旁边添加izzyrefactor
名字.
或许还想编辑CMakeLists.txt
文件,假如你使用CMake
,添加新的一行:
add_subdirectory(izzyrefactor)
来到izzyrefactor
目录,创建新的Makefile
,以通知LLVM-build
系统,要建造独立于其它二进制文件
而存在的独立工具
.如下:
CLANG_LEVEL := ../../..
TOOLNAME = izzyrefactor
TOOL_NO_EXPORTS = 1
include $(CLANG_LEVEL)/../../Makefile.config
LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support\mc option
USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a \clangDriver.a clangRewriteFrontend.a clangRewriteCore.a \clangParse.a clangSema.a clangAnalysis.a clangAST.a \clangASTMatchers.a clangEdit.a clangLex.a clangBasic.a
include $(CLANG_LEVEL)/Makefile
这是指定所有需要
和你的代码
链接到一起的库的重要文件
,这样才能建造该工具
.
可选地,如果不想运行make install
时,和其它LLVM
工具一样安装新工具
,在设置TOOL_NO_EXPORTS
这行之后,可添加一行NO_INSTALL=1
.
设置TOOL_NO_EXPORTS=1
,因为工具
不会使用插件
,因此,不需要导出
符号,减小了最终程序
的动态符号表
的大小,也减少了动态链接
并加载
程序的时间.
注意包含
了定义了期望的所有规则
的Clang
总的Makefile
来编译
该项目.
如果使用CMake
而不是自动工具
配置脚本,用如下内容,创建新CMakeLists.txt
文件:
add_clang_executable(izzyrefactorIzzyRefactor.cpp)
target_link_libraries(izzyrefactorclangEdit clangTooling clangBasic clangAST clangASTMatchers)
此外,如果不想在Clang
源码树中构建
该工具,你也可按独立工具构建
它.
注意,前面Makefile
中用的库,在USEDLIBS
变量中,及在CLANGLIBS
变量中用的库.
它们引用了相同的库,除了USEDLIBS
有包含LibTooling
的clangTooling
.因此,在Makefile
中,在-lclang
这行之后,添加一行-lclangTooling
,就大功告成了.
剖析工具样板代码
所有代码在IzzyRefactor.cpp
中.新建该文件
并开始添加初始
样板代码,如下:
int main(int argc, char **argv) {cl::ParseCommandLineOptions(argc, argv);string ErrorMessage;OwningPtr<CompilationDatabase> Compilations (CompilationDatabase::loadFromDirectory(BuildPath, ErrorMessage));if (!Compilations)report_fatal_error(ErrorMessage);//...
}
主要代码从llvm::cl
名字空间的解析argv
中的每个选项的ParseCommandLineOptions
函数开始.
注意,基于LibTooling
的工具,典型会使用为所有重构工具
共享的CommonOptionsParser
对象来轻松
解析通用选项.这里
本例中,用低级的ParseCommandLineOptions()
函数来确切说明要解析的参数
.
所有的LLVM
工具都会使用cl
名字空间提供的功能,在命令行
中,用工具识别
定义的参数,实在是很简单.为此,声明新的opt
模板类型和list
的全局变量
:
cl::opt<string> BuildPath(cl::Positional,cl::desc("<build-path>"));
cl::list<string> SourcePaths(cl::Positional,cl::desc("<source0> [... <sourceN>]"),cl::OneOrMore);
cl::opt<string> OriginalMethodName("method",cl::desc("Method name to replace"),cl::ValueRequired);
cl::opt<string> ClassName("class",cl::desc("Name of the class that has this method"),cl::ValueRequired);
cl::opt<string> NewMethodName("newname",cl::desc("New method name"),cl::ValueRequired);
定义main
函数前声明这五个
全局变量.根据期望按参数
读取的数据
,指定了opt
类型.如,如果需要读取数字
,你会声明一个新的cl::opt<int>
全局变量.
为了读取
这些参数的数值
,先要调用ParseCommandLineOptions
.之后,只需要在期望关联数据类型
的代码位置,引用
与参数关联
的全局变量名
.
如,如果代码像std::out<<NewMethodName
期望串,NewMethodName
会为该参数
求值用户提供的串
.
因为opt<>
的opt_storage<>
父类模板,这工作,它定义了从所管理的数据类型
(此处为string
)继承的类
.
通过继承,opt<string>
变量也是个串
.
如果opt<>
类模板不能从被包装的数据类型
(如,不存在的int
类)继承,它会定义个类型转换符号
,如为int
数据类型,定义operator int()
.
代码中,效果是一样的;引用cl::opt<int>
变量时,它会自动
转换为一个整数
,并返回它所存储的数字
,即用户在命令行
中提供的数字
.
还可为参数
指定不同特征
.示例中,通过指定cl::Positional
使用了位置参数
,即用户不会显示按名字
指定参数
,而是会根据在命令行
中的相对位置
推导出来.
还给opt
构造器传递了一个desc
对象,它定义了一段,用户在命令行
中输入-help
参数时,展示
给用户的描述信息
.
还有个使用cl::list
类型的参数
,不同于opt
,它允许传递多个参数
,这样可处理一堆源文件
.要求包含下面头文件
:
#include "llvm/Support/CommandLine.h"
注意,作为LLVM
编码标准的一部分
,include
语句要,先包含本地头文件
,随后包含Clang
和LLVMAPI
头文件.
当两个
头文件属于相同分类
时,按字母顺序
排序.写独立工具,则自动为你整理头文件顺序
.
最后三个
全局变量设置用本重构工具的要求选项
.第一个是-method
名字的参数.第一个串参数
指定没有短线
的参数名字
,而cl::RequiredValues
会通知命令行解析器
,该值
是运行
该程序所必需的.
该参数
会给出方法
名字,然后工具
会去找该方法
,然后用-newname
给出的名字更改
它的名字.-class
参数给出有此方法
的类名
.
下一段代码管理新的CompilationDatabase
对象.首先,要包含定义OwningPtr
类的头文件
.
#include "llvm/ADT/OwningPtr.h"
注意Clang
版本,从Clang/LLVM
版本3.5
开始,人们弃用了OwningPtr<>
模板,而是转向C++
标准的std::unique_ptr<>
模板.
其次,要包含第一次用到的正式属于LibTooling
的CompilationDatabase
类的头文件:
#include "clang/Tooling/CompilationDatabase.h"
该类负责管理
编译数据库
.
为了初化该对象
,用到叫loadFromDirectory
的工厂方法,它会从指定的构建
目录加载编译数据库
文件.
这就是按输入工具
参数,声明构建
路径的目的;用户要指定源文件
及编译数据库
文件的路径
.
注意,给该工厂成员函数
输入两个参数
:代表命令行的cl::opt
对象的BuildPath
对象,及最近声明的ErrorMessage
串.
假如引擎
加载编译数据库
失败了,会用一个消息
填充ErrorMessage
串,即工厂成员函数
没有返回CompilationDatabase
对象,这时会马上显示该消息
.
llvm::report_fatal_error()
函数会触发已配置
的任意LLVM
错误处理例程,并以1错误码
退出工具.它要求包含下面头文件:
#include "llvm/Support/ErrorHandling.h"
示例中,缩写了很多类
的全名
,因此还要在全局域
中添加若干个using
声明,但是只要你喜欢,也可用全名
:
using namespace clang;
using namespace std;
using namespace llvm;
using clang::tooling::RefactoringTool;
using clang::tooling::Replacement;
using clang::tooling::CompilationDatabase;
using clang::tooling::newFrontendActionFactory;
使用AST
匹配器
对编写基于Clang
的代码重构工具,AST
匹配器非常重要
.
AST
匹配器库让用户可轻松匹配
符合特定条件的ClangAST
的子树,如,表示所命名为calloc
,且有两个参数
的函数调用的AST
节点.
查找指定的ClangAST
节点并修改
它们,这是每个代码重构
工具共同的基本任务
,利用
该库,极大地方便了编写
此类工具.
为了帮助找到正确的匹配器
,依靠ClangQuery
和AST
匹配器文档,文档在此.
先为工具编写叫wildlifesim.cpp
的测试案例.这是一个复杂
的可沿着直线
任意方向遍历的一维动物
模拟器:
class Animal {int position;
public:Animal(int pos) : position(pos) {}//返回新位置int walk(int quantity) {return position += quantity;}
};
class Cat : public Animal {
public:Cat(int pos) : Animal(pos) {}void meow() {}void destroySofa() {}bool wildMood() {return true;}
};
int main() {Cat c(50); c.meow();if (c.wildMood())c.destroySofa();c.walk(2);return 0;
}
要求工具可将成员函数比如walk
重命名为run
.运行ClangQuery
,研究此例中的AST
.这里用recordDecl
匹配器,转存所有表示C结构
和C++
类的RecordDeclAST
节点的内容
:
$ clang-query wildanimal-sim.cpp --
clang-query> set output dump
clang-query> match recordDecl()
(...)
|-CXXMethodDecl 0x(...) <line:6:3, line 8:3> line 6:7 walk 'int (int)'
(...)
在表示Animal
类的RecordDecl
对象的内部,观察到按CXXMethodDeclAST
节点表示walk
.查看AST
匹配器文档,发现它由methodDeclAST
匹配器匹配
.
组合匹配器
AST
匹配器的强大在于可组合性
.如果只想要声明了叫walk
成员函数的MethodDecl
节点,可先匹配所有叫walk
的命名声明,然后细化匹配
同时是方法声明
的节点.
hasName("input")
匹配器返回所有叫"input"
的命名声明.可在ClangQuery
中,测试methodDecl
和hasName
的组合:
clang-query> match methodDecl(hasName("walk"))
可见它只返回
了一个walk
的声明,而不是代码
中存在的所有八个
不同方法的声明.太好了!
尽管如此,发现仅修改Animal
类的walk
方法的定义
是不够的,因为继承类
可能重载
它.不想重构工具
重写了基类
的一个方法
而不重写
继承类中重载
方法.
要找到所有定义了walk
方法的类,即Animal
类或其继承类.为了找到所有Animal
类或其继承类,使用期望按NamedDecl
参数的isSameOrDerivedFrom()
匹配器.
通过带选择所有叫hasName()
的NamedDecl
的匹配器的组合来提供该参数
.因此,如下查询:
clang-query> match recordDecl(isSameOrDerivedFrom(hasName("Animal")))
还要选择那些重载
了walk
方法的继承类.hasMethod()
断定返回包含指定方法
的类声明
.和第一个查询
组合成如下查询
:
clang-query> match recordDecl(hasMethod(methodDecl(hasName("walk"))))
为了用and
符号语义(满足所有断定
)连结两个断定
,使用allOf()
匹配器.它要求所有传入匹配器
必须成立.
此时就可建造最终查询
,以找到要重写
的所有声明
:
clang-query> match recordDecl(allOf(hasMethod(methodDecl(hasName("walk"))), isSameOrDerivedFrom(hasName("Animal"))))
用该查询,可精确地找到Animal
类或其继承类的所有walk
方法的声明.
这样允许修改所有这些声明
的名字
,但是还需要修改
方法调用.为此,先观察CXXMemberCallExpr
节点和它的memberCallExpr
匹配器.试试:
clang-query> match memberCallExpr()
因为代码
确实有四个方法调用
,ClangQuery
返回四个匹配
:meow,wildMood,destroySofa
,和walk
.只对定位
最后一个感兴趣.
已知道如何利用hasName()
匹配器,来选择指定命名
声明,但是如何把命名声明
映射到成员函数
调用式呢?
答案是用member()
匹配器,来只选择命名
且和一个方法
名字链接
的声明
,然后用callee()
匹配器用调用式
链接它.
完整式如下:
clang-query> match memberCallExpr(callee(memberExpr(member(hasName("walk")))))
然而,这里,盲目
选择了所有调用walk()
的方法.这里只想选择那些Animal
类或其继承类
的walk
调用.
memberCallExpr()
匹配器按参数接受
第二个匹配器.会使用thisPointerType()
匹配器以仅选择调用对象
是指定类
的方法调用
.
利用该规则
,构建
完整表达式
:
clang-query>matchmemberCallExpr(callee(memberExpr(member(hasName("walk")))),thisPointerType(recordDecl(isSameOrDerivedFrom(hasName("Animal")))))
在代码中用AST
匹配器断定
已决定了用哪些断定
来抓正确的AST
节点,现在可在工具
代码中运用它们了.首先,为了使用AST
匹配器,需要添加新的include
指令:
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
还要添加新的using
指令,使得更易引用
这些类(在其它用
指令后面
):
using namespace clang::ast_matchers;
第二个
头文件,对实际查找
机制是必须的.继续编写main
函数,开始添加剩余代码:
RefactoringTool Tool(*Compilations, SourcePaths);
ast_matchers::MatchFinder Finder;
ChangeMemberDecl DeclCallback(&Tool.getReplacements());
ChangeMemberCall CallCallback(&Tool.getReplacements()));
Finder.addMatcher(recordDecl(allOf(hasMethod(id("methodDecl", methodDecl(hasName(OriginalMethodName)))), isSameOrDerivedFrom(hasName(ClassName)))), &DeclCallback);
Finder.addMatcher(memberCallExpr(callee(id("member", memberExpr(hasName(OriginalMethodName))))),thisPointerType(recordDecl(isSameOrDerivedFrom(hasName(ClassName)))), &CallCallback);
return Tool.runAndSave(newFrontendActionFactory(&Finder));
在3.5
版本中,要修改以上代码
的最后一行
为
return Tool.runAndSave(newFrontendActionFactory(&Finder).get());
这要就完成了main
函数.
第一行
代码实例化了个新的RefactoringTool
对象.这是用到的LibTooling
的需要额外包含
语句的第二个类
:
#include "clang/Tooling/Refactoring.h"
RefactoringTool
类为你的工具
实现了协调基本任务
的所有逻辑:
如打开
源文件,解析
它们,运行AST
匹配器,匹配
时调用回调函数
,按工具要求修改源码
等.
因此,在初化
要求对象后,要调用RefactoringTool::runAndSave()
结束main
函数.
转移控制
到该类
,让它执行所有基本任务
.
接着,声明一个已包含其头文件
的MatchFinder
对象.你用ClangQuery
练习过的,该类负责匹配ClangAST
.
MatchFinder
要求用AST
匹配器和回调函数
配置,提供的AST
匹配器匹配
一个AST
节点时,就会调用
回调函数.
在回调函数
中,可修改
源码.按MatchCallback
的子类实现回调函数
.
接着,声明回调函数对象
,并用MatchFinder::addFinder()
方法,用回调
关联具体的AST
匹配器.
声明两个单独回调函数
,一个重写方法声明
,另一个重写
方法调用.这两个回调函数
叫DeclCallback
和CallCallback
.
把前面设计的两个AST
匹配器组合
,但是用,用户按命令行参数
提供的要重构的类名
替换Animal
类名.
同样,用也是命令行参数
的OriginalMethodName
替换walk
.
还战略性
地引入了新的叫id()
匹配器,它不修改表达式
所匹配的节点
,只是用具体
节点绑定
一个名字.为让回调
函数可产生替换
内容,这很重要.
id()
匹配器接受两个参数,第一个是用它取节点
的节名
,第二个是用来抓命名AST
的匹配器
.
第一个AST
组合负责定位
成员方法声明,命名确定
方法的MethodDecl
节点.第二个AST
组合负责定位调用成员函数
,命名与调用
成员函数链接
的CXXMemberExpr
节点.
编写回调函数
要定义匹配AST
节点时,要执行的动作
.为此,创建两个从MatchCallback
继承的新类
,每个匹配
各一个类.
class ChangeMemberDecl : public ast_matchers::MatchFinder::MatchCallback {tooling::Replacements *Replace;
public:ChangeMemberDecl(tooling::Replacements *Replace) : Replace(Replace) {}virtual void run(const ast_matchers::MatchFinder::MatchResult &Result) {const CXXMethodDecl *method = Result.Nodes.getNodeAs<CXXMethodDecl>("methodDecl");Replace->insert(Replacement(*Result.SourceManager, CharSourceRange::getTokenRange(SourceRange(method->getLocation())), NewMethodName));}
};class ChangeMemberCall : public ast_matchers::MatchFinder::MatchCallback {tooling::Replacements *Replace;
public:ChangeMemberCall(tooling::Replacements *Replace) : Replace(Replace) {}virtual void run(const ast_matchers::MatchFinder::MatchResult &Result) {const MemberExpr *member = Result.Nodes.getNodeAs<MemberExpr>("member");Replace->insert(Replacement(*Result.SourceManager, CharSourceRange::getTokenRange(SourceRange(member->getMemberLoc())), NewMethodName));}
};
这两个类
都私下存储
了,只是std::set<Replacement>
的typedef
的Replacements
对象的引用.Replacement
类存储的信息
有,用什么文本
,在哪个
文件的哪些行
要打补丁
.
RafactoringTool
类内部管理Replacement
对象集合,因而在main
函数中用RefactoringTool::getReplacements()
方法取得该集合
,并初化回调函数
.
用存储它来以后使用的Replacements
对象指针定义
了个基本构造器
.通过重载run()
方法,来实现回调
的动作,又一次,代码相当简单
.
函数按参数
接受一个MatchResult
对象.对给定匹配,MatchResult
类存储了,如id()
匹配器请求的按名字
绑定的所有节点
.
在MatchResult
对象中可通过节点名
公开访问的BoundNodes
类管理
这些节点.因此,在run()
函数中的第一个动作
是调用特化的BoundNodes::getNodeAs<CXXMethodDecl>
方法取感兴趣的节点
.
结果,就得到了CXXMethodDeclAST
节点的只读版本
引用.
访问该节点
后,为了决定
如何给代码
打补丁,需要一个,告诉在源文件中,关联令牌
的确切行和列
的SourceLocation
对象.
CXXMethodDecl
从表示通用声明的Decl
基类(a
)继承.它(a
)提供了返回想要的SourceLocation
对象的Decl::getLocation()
方法.
有了它,就可创建第一个Replacement
对象,并把它插入
到工具
建议的源码修改列表
中.
用到的Replacement
构造器需要三个参数
:一个SourceManager
对象的引用,一个CharSourceRange
对象的引用,和包含在头两个
参数指定位置
要写的新文本串
.
SourceManager
类是个通用的管理加载
到内存
的源码的Clang
组件.CharSourceRange
类包含有用的解析器
,它分析令牌并推导
出组成该令牌的源码区间
(文件中的两个点
),从而决定要从源码文件
中删除的确切符
,并为新文本空出位置
.
用该信息
创建新的Replacement
对象,并在由RefactoringTool
管理的集合
中存储
它,就完成任务了.RefactoringTool
会应用
这些补丁,或去除
冲突.
记得在匿名
名字空间里,声明
所有本地包装
,这样避免,翻译单元
导出本地符号
.
测试你的新重构工具
用野生
模拟器代码示例,作为测试用例
,来测试新创建的工具.现在应该运行make
,然后等待LLVM
编译并链接你的新工具
.
生成工具
后,试用一番.看看在命令行接口
中,按cl::opt
对象声明
的参数:
$ izzyrefactor -help
为了使用该工具
,还需要编译命令数据库
.为了避免创建并运行一个CMake
配置文件,手动
创建一个.
命名为compile_commands.json
,并有如下内容.把<FULLPATHTOFILE>
标签替换为野生模拟器
源码目录的完整路径
:
[
{
"directory": "<FULLPATHTOFILE>",
"command": "/usr/bin/c++ -o wildlifesim.cpp.o -c <FULLPATHTOFILE>/ wildlifesim.cpp",
"file": "<FULLPATHTOFILE>/wildlifesim.cpp"
}
]
保存该编译命令数据库
后,就可测试
工具了:
$ izzyrefactor -class=Animal -method=walk -newname=run ./ wildfilesim.cpp
现在可检查野生
模拟器源码
,会看到该工具
重命名了所有方法
的定义和调用
.
更多
设置命令数据库
的指令.
C++模块
AST
匹配器和LibTooling
的教程.
ClangTidy
的用户手册
ClangFormat的用户手册.
llvm博客