RecursiveASTVisitor Basics

类声明

template<typename Derived>
class clang::RecursiveASTVisitor<Derived>;

以前序或后序深度优先的方式遍历整个 Clang AST 并访问每个节点的一个类。其执行三种不同的操作:

  1. 遍历整个 AST(即访问每个节点)
  2. 对于给定的一个节点,沿着其类继承关系向前(Derived->Base方向)游历,直至到达一个顶层类(如 Stmt,Decl,Type)
  3. 对于一个给定的 (node, class) 组合,调用一个可由用户重写(user-overridable)的函数来访问该节点,其中 class 是 node 的动态类型的某个基类

这些操作由三组类方法来完成,分别是:

  1. 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) 执行的操作类似
  2. WalkUpFromFoo(Foo *x) 执行任务2。该函数首先调用 WalkUpFromBar(x),然后调用 VisitFoo(x)。其中 Bar 是 Foo 的直接父类(direct parent)
  3. 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的更多相关文章

  1. YOCVM

    一.热补丁的本质 对于线上紧急的bug,重新提审AppStore的时间过长.因此,能够下发一段补丁代码到线上运行,并结合Runtime,实时改变App原有的行为,就显得极为重要.补丁代码的形式可以有很 ...

  2. 微信团队分享:极致优化,iOS版微信编译速度3倍提升的实践总结

    1.引言 岁月真是个养猪场,这几年,人胖了,微信代码也翻了. 记得 14 年转岗来微信时,用自己笔记本编译微信工程才十来分钟.如今用公司配的 17 年款 27-inch iMac 编译要接近半小时:偶 ...

  3. 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 ...

  4. “Clang” CFE Internals Manual---中文版---"Clang"C语言前端内部手册

    原文地址:http://clang.llvm.org/docs/InternalsManual.html 译者:史宁宁(snsn1984) "Clang"C语言前端内部手册 简介 ...

  5. 基于Clang的Source to Source源代码转换(一)

    Clang中包含了非常多的关于抽象语法树(AST)的访问和操作的类和接口.我们程序开发人员可以直接通过继承其中的某些类,重写其中的关键成员方法,从而形成我们自己的对抽象语法树的操作. 那么,首先我们简 ...

  6. clang format 官方文档自定义参数介绍(中英文)

    官方文档:http://clang.llvm.org/docs/ClangFormatStyleOptions.html 中文 在代码中配置样式 当使用 clang::format::reformat ...

  7. 打造基于Clang LibTooling的iOS自动打点系统CLAS(二)

    1. 配置LLVM和Clang 在这篇文章里,我们会基于上一篇所述的方案进行展开,详细讲解如何从0开始创建一个基于Clang LibTooling的编译器前端工具.在开始之前,我们假设你已经基本了解何 ...

  8. Clang之语法抽象语法树AST

    语法分析器的任务是确定某个单词流是否能够与源语言的语法适配,即设定一个称之为上下文无关语言(context-free language)的语言集合,语法分析器建立一颗与(词法分析出的)输入单词流对应的 ...

  9. clang 编译 OC

    clang -fobjc-arc -framework Foundation helloworld.m -o helloworld.out OVERVIEW: clang LLVM compiler ...

  10. iOS编程 手动忽略clang编译器警告

    在iOS开发过程中, 我们可能会碰到一些系统方法弃用, weak.循环引用.不能运行之类的警告. 有代码洁癖的孩子们非常想消除他们, 今天就让我们来一次Fuck 警告.! 首先学会主要的语句 #pra ...

随机推荐

  1. NC25084 [USACO 2006 Nov S]Bad Hair Day

    题目 题目描述 Some of Farmer John's N cows (1 ≤ N ≤ 80,000) are having a bad hair day! Since each cow is s ...

  2. sensitive-word 敏感词/脏词开源工具-v.0.10.0-脏词分类标签支持

    sensitive-word sensitive-word 基于 DFA 算法实现的高性能敏感词工具. 创作目的 实现一款好用敏感词工具. 基于 DFA 算法实现,目前敏感词库内容收录 6W+(源文件 ...

  3. 从零开始手写 redis(三)内存数据重启后如何不丢失?

    前言 我们在 从零手写 cache 框架(一)实现固定大小的缓存 中已经初步实现了我们的 cache. 我们在 从零手写 cache 框架(一)实现过期特性 中实现了 key 的过期特性. 本节,让我 ...

  4. 【OpenGL ES】基于ValueAnimator的旋转、平移、缩放动效

    1 前言 ​ ValueAnimator 基于 Choreographer 的 frame callback 机制,周期性(约16.7ms,与屏幕帧率相关)执行其 doAnimationFrame() ...

  5. 微信小程序获取本日、本周、本月、本年时间段

    原文链接 https://cslaoxu.vip/110.html 说明 最近需要用到统计不同时间段内的记录数,所以找了一下现成的工具类.下面就演示一下如何引用到实际项目中. 详细用法请参考:http ...

  6. Vue+SpringBoot+ElementUI实战学生管理系统-5.用户管理模块

    1.章节介绍 前一篇介绍了项目的API接口设计,这一篇编写用户管理模块,需要的朋友可以拿去自己定制.:) 2.获取源码 源码是捐赠方式获取,详细请QQ联系我 :)! 3.项目截图 列表操作 动态图 4 ...

  7. Docker方式快速启动一个Redis实例

    安装Redis有多种方式,除了可以通过各个平台的软件包工具安装外,还可以直接从源码安装. 但是,安装Redis可能会遇到一些这样的问题,比如: 1.网络环境比较差,下载耗时比较长 2.从源码编译安装时 ...

  8. 符合ISO26262标准的建模规范检查模型静态分析静态测试工具

    Model Examiner - 功能安全解决方案(以下简称MXAM)测试套件是您进行全面静态模型分析的首选工具.MXAM提供了一种简单的方法来检查建模规范.分析模型结构和评估模型指标,所有这些功能都 ...

  9. 在Ubuntu搭建DHCP服务器

    一.提供DHCP的服务器,自己必须有固定的IP地址 不然局域网就乱了,服务器自身启动(比如搭建完DHCP服务后,重新启动了服务器)的时候,DHCP服务器没有IP地址,无法和自己的DHCP服务通信. 在 ...

  10. 【Azure Batch】在中国区批处理服务(Mooncake Batch Account)上实验自动池(Auto Pool)的创建/删除

    问题描述 在Azure Batch的介绍文档中,提出了自动池的概念, 它可以在任务完成后,自动删除Pool资源,详细介绍:https://docs.azure.cn/zh-cn/batch/nodes ...