Clang RecursiveASTVisitor & ASTFrontendActions based on it
RecursiveASTVisitor Basics
类声明
template<typename Derived>
class clang::RecursiveASTVisitor<Derived>;
以前序或后序深度优先的方式遍历整个 Clang AST 并访问每个节点的一个类。其执行三种不同的操作:
- 遍历整个 AST(即访问每个节点)
- 对于给定的一个节点,沿着其类继承关系向前(Derived->Base方向)游历,直至到达一个顶层类(如 Stmt,Decl,Type)
- 对于一个给定的 (node, class) 组合,调用一个可由用户重写(user-overridable)的函数来访问该节点,其中 class 是 node 的动态类型的某个基类
这些操作由三组类方法来完成,分别是:
- TraverseDecl(Decl *x) 执行任务1,是遍历以x为根的 AST 的入口函数。该函数只是简单地将任务分派(dispatchs, i.e. forwards)给 TraverseFoo(Foo *x),继而调用 WalkUpFromFoo(x),然后递归地访问x的子节点。TraverseFoo 中的 Foo 是 *x 的动态类型。TraverseDecl(Decl *x) 和 TraverseType(QualType x) 执行的操作类似
- WalkUpFromFoo(Foo *x) 执行任务2。该函数首先调用 WalkUpFromBar(x),然后调用 VisitFoo(x)。其中 Bar 是 Foo 的直接父类(direct parent)
- VisitFoo(Foo *x) 执行任务3
这三组方法具有下列层次关系:Traverse* > WalkUpFrom* > Visit*。某一层次的方法可以调用相同层次的另一个方法以及较低层次的方法,但不能调用层次比它高的方法。
由于 WalkUpFromFoo() 在调用 VisitFoo() 之前先调用 WalkUpFromBar(Bar是Foo的超类),因此最终结果是对于一个给定的节点将以自顶向下的顺序依次调用其 Visit*() 方法(如,对于 NamespaceDecl 类型的节点,调用顺序依次为 VisitDecl(),VisitNamedDecl(),VisitNamespaceDecl())。这种机制保证相同类型 AST 节点的 Visit*() 方法调用被组合在一起,而不会与不同类型节点的 Visit*() 方法搅和在一起。
要使用该 visitor,首先要进行子类化(将其自身作为模板参数,采用奇异递归模板模式(curiously recurring template pattern)),然后为声明、类型、语句、表达式以及其他所有需要自定义行为的 AST 节点重写(override)Traverse*、WalkUpFrom* 和 Visit* 方法。大多数用户只需要重写 Visit* 方法即可,也可以重写前两种方法以实现更加高级的操作。在遍历的过程中,如果这些重写函数中的任意一个返回了 false,则整个遍历过程将终止。
默认情况下该 visitor 尝试访问显式源代码(explicit source code)的每一部分而且只访问一次。针对模板的讨论详参原文档。
默认情况下该 visitor 以前序的形式遍历AST,如果需要后序遍历,需要重写 shouldTraversePostOrder 方法并返回 true。
源码简析
TraverseDecl 方法
1 template <typename Derived>
2 bool RecursiveASTVisitor<Derived>::TraverseDecl(Decl *D) {
3 if (!D)
4 return true;
5
6 // As a syntax visitor, by default we want to ignore declarations for
7 // implicit declarations (ones not typed explicitly by the user).
8 if (!getDerived().shouldVisitImplicitCode() && D->isImplicit())
9 return true;
10
11 switch (D->getKind()) {
12 #define ABSTRACT_DECL(DECL)
13 #define DECL(CLASS, BASE) \
14 case Decl::CLASS: \
15 if (!getDerived().Traverse##CLASS##Decl(static_cast<CLASS##Decl *>(D))) \
16 return false; \
17 break;
18 #include "clang/AST/DeclNodes.inc"
19 }
20
21 // Visit any attributes attached to this declaration.
22 for (auto *I : D->attrs()) {
23 if (!getDerived().TraverseAttr(I))
24 return false;
25 }
26 return true;
27 }
行11到行19便是整个分派过程。getDerived() 方法返回派生子类的引用,
基于 RecursiveASTVisitor 的 ASTFrontendAction
关于 FrontendAction 的知识见另一篇 blog:Clang FrontendActions。此处给出的例子只是简单地打印出程序中所有的 witch-case 语句的两部分信息:switch 条件表达式和 case 语句。
ClangTool 的 run 方法接受一个 FrontendAction (wrapped with newFrontendActionFactory),因此首先创建一个 FrontendAction 类:
1 class SwitchAction : public ASTFrontendAction {
2 public:
3 virtual std::unique_ptr<ASTConsumer> CreateASTConsumer(CompilerInstance &CI,
4 StringRef InFile) {
5 return std::unique_ptr<ASTConsumer>(
6 new SwitchConsumer(&CI.getASTContext()));
7 }
8 };
编译器在对源文件进行 parsing 时执行传进来的 FrontendAction(Act),在 Act 的准备阶段调用 CreateASTConsumer 方法,在生成完 AST 后,会调用 ASTConsumer 的 HandleTranslationUnit(ASTContext &Ctx) 方法。下面创建一个 ASTConsumer 类:
1 class SwitchConsumer : public ASTConsumer {
2 public:
3 explicit SwitchConsumer(ASTContext *Context) : Visitor(Context) {}
4 virtual void HandleTranslationUnit(ASTContext &Context) {
5 Visitor.TraverseDecl(Context.getTranslationUnitDecl());
6 }
7
8 private:
9 SwitchVisitor Visitor;
10 };
在 ASTConsumer 的 HandleTranslationUnit 中调用 Visitor 的 TraverseDecl 方法来遍历 AST。此时 AST 已经创建完毕,我们将其根 TranslationUnitDecl 传递给 TraverseDecl 方法。
Refenences:
Clang RecursiveASTVisitor & ASTFrontendActions based on it的更多相关文章
- YOCVM
一.热补丁的本质 对于线上紧急的bug,重新提审AppStore的时间过长.因此,能够下发一段补丁代码到线上运行,并结合Runtime,实时改变App原有的行为,就显得极为重要.补丁代码的形式可以有很 ...
- 微信团队分享:极致优化,iOS版微信编译速度3倍提升的实践总结
1.引言 岁月真是个养猪场,这几年,人胖了,微信代码也翻了. 记得 14 年转岗来微信时,用自己笔记本编译微信工程才十来分钟.如今用公司配的 17 年款 27-inch iMac 编译要接近半小时:偶 ...
- c++ binding code generator based on clang
google it http://www.swig.org/Doc3.0/CSharp.html http://samanbarghi.com/blog/2016/12/06/generate-c-i ...
- “Clang” CFE Internals Manual---中文版---"Clang"C语言前端内部手册
原文地址:http://clang.llvm.org/docs/InternalsManual.html 译者:史宁宁(snsn1984) "Clang"C语言前端内部手册 简介 ...
- 基于Clang的Source to Source源代码转换(一)
Clang中包含了非常多的关于抽象语法树(AST)的访问和操作的类和接口.我们程序开发人员可以直接通过继承其中的某些类,重写其中的关键成员方法,从而形成我们自己的对抽象语法树的操作. 那么,首先我们简 ...
- clang format 官方文档自定义参数介绍(中英文)
官方文档:http://clang.llvm.org/docs/ClangFormatStyleOptions.html 中文 在代码中配置样式 当使用 clang::format::reformat ...
- 打造基于Clang LibTooling的iOS自动打点系统CLAS(二)
1. 配置LLVM和Clang 在这篇文章里,我们会基于上一篇所述的方案进行展开,详细讲解如何从0开始创建一个基于Clang LibTooling的编译器前端工具.在开始之前,我们假设你已经基本了解何 ...
- Clang之语法抽象语法树AST
语法分析器的任务是确定某个单词流是否能够与源语言的语法适配,即设定一个称之为上下文无关语言(context-free language)的语言集合,语法分析器建立一颗与(词法分析出的)输入单词流对应的 ...
- clang 编译 OC
clang -fobjc-arc -framework Foundation helloworld.m -o helloworld.out OVERVIEW: clang LLVM compiler ...
- iOS编程 手动忽略clang编译器警告
在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak.循环引用.不能运行之类的警告. 有代码洁癖的孩子们非常想消除他们, 今天就让我们来一次Fuck 警告.! 首先学会主要的语句 #pra ...
随机推荐
- Python 爬虫方法总结
实现爬虫的套路 准备URL 准备start_url url地址规律不明显,总数不确定 通过代码提取下一页的url 通过xpath提取 寻找url地址,部分参数在当前的响应中(比如当前页码数和总页码数在 ...
- 【Unity3D】AudioSource组件
1 简介 1)AudioSource 与 AudioListener 简介 AudioSource(音频源)组件用于控制播放 AudioClip(音频片段),能够控制 2D 和 3D(距离越远 ...
- 我的小程序之旅七:微信公众号设置IP白名单
一.为什么要配置IP白名单 此处IP为服务器对公网IP: 在IP白名单内的IP地址作为来源,获取access_token接口才可调用成功. 而想要调用公众号相关API,就必须获取access_toke ...
- 【Android 抓包对抗】代理检查绕过
1. 安装apk,点进去发现一点就挂 2. apk 拖入到jadx中观察,发现多出检查,一旦满足条件就会退出 .... if (((ConnectivityManager) getSystemServ ...
- 如何拓展jwt返回的数据
默认的返回值仅有token,我们还需在返回值中增加username和id,方便在客户端页面中显示当前登陆用户 通过修改该视图的返回值可以完成我们的需求. 在user/utils.py中,创建 def ...
- Django关于StreamingHttpResponse与FileResponse响应文件或视频的下载请求
StreamingHttpResponse from django.http import StreamingHttpResponse StreamingHttpResponse(streaming_ ...
- java数组实现的超市管理系统(控制台)
说明:使用数组存储数据,针对用户功能1:增加用户2:删除用户3:修改用户:针对商品功能:1.显示所有商品2.修改商品信息3.添加商品信息4.删除商品信息5.查询商品信息 效果展示 ========== ...
- 问题:django.template.exceptions.TemplateSyntaxError: 'staticfiles' is not a registered tag library. Must be one of: admin_list admin_modify admin_urls cache i18n l10n log rest_framework static tz
django使用swagger自动生成API文档时,报错 解决方法 在settings.py里面配置一下以下代码 'libraries': { 'staticfiles': 'django.templ ...
- 运行python的几种方式
1. 通过cmd终端去运行 按住win + r 打开命令提示符,然后输入python,就可以进入python环境,输入你需要指定的python代码即可. # 注意:这种方法只是建议临时使用一下,因为午 ...
- 我的第一个项目(十三) :组件间传值的一些方案(vuex,eventbus,localStorage)
好家伙, 先说一下我的需求,我要组件间传值 1.eventBus 前端兄弟组件传值eventbus无法使用 不报错也不触发,就很奇怪 //eventBus.js import Vue from & ...