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. 吉特日化MES & 日化制药工厂信息化系统架构图

    作者:情缘   出处:http://www.cnblogs.com/qingyuan/ 关于作者:从事仓库,生产软件方面的开发,在项目管理以及企业经营方面寻求发展之路 版权声明:本文版权归作者和博客园 ...

  2. Python实现二叉查找

    搜索 搜索是在一个项目集合中找到一个特定项目的算法过程.搜索通常的答案是真的或假的,因为该项目是否存在. 搜索的几种常见方法:顺序查找.二分法查找.二叉树查找.哈希查找 二分法查找 二分查找又称折半查 ...

  3. NC25879 外挂

    题目链接 题目 题目描述 我的就是我的,你也是我的,记住了,狐狸! ​ --韩信-白龙吟 对于打赌输了的小T会遭受到制裁,小s修改了数据库使他可以派出许多军队来围攻小T. 很不幸,小T与小s打赌打输了 ...

  4. Centos中报错apt Command not Found

    先说结论: 在centos下用yum install xxxyum和apt-get的区别: 一般来说著名的linux系统基本上分两大类: RedHat系列:Redhat.Centos.Fedora等 ...

  5. Atom N2600, N2800 安装 Ubuntu22.04 卡住的问题处理

    问题描述 Atom N2600, N2800 的某些旧型号机器, 安装 Ubuntu 时在安装界面选择安装后, 启动过程中会卡住, 或者数秒即黑屏, 再无反应. 这个问题对于Debian系的其他发行版 ...

  6. Java设计模式-观察者模式Observer

    介绍 观察者模式是行为设计模式之一.当您对对象的状态感兴趣并希望在任何更改时得到通知时,观察者设计模式非常有用.在观察者模式中,观察另一个对象状态的对象被称为观察者,而被观察的对象则被称为主体. 优点 ...

  7. 函数探秘:深入理解C语言函数,实现高效模块化编程

    欢迎大家来到贝蒂大讲堂 养成好习惯,先赞后看哦~ 所属专栏:C语言学习 贝蒂的主页:Betty's blog 1. 函数的概念 在数学中我们就知道了函数这个概念,而C语言同样引入了函数这个概念,那C语 ...

  8. 3D环饼图

    // <div class="AnalysisAccCom">         <first-title title="分析对象统计"> ...

  9. PrettyTable模块

    # 用来生成美观的ASCII格式的表格 pip install prettytable # 导入 from prettytable import PrettyTable # 使用 tb = pt.Pr ...

  10. Java Reactive Programming

    Java Reactive Programming 响应式编程 在 Spring Boot 中,支持了响应式编程,带来了性能和内存使用方面的优化. 详见: Spring: Blocking vs no ...