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. 回顾复习之背包DP

    \(\small{(本文统一将c[i]视作cost,w[i]视作worth,下面的代码用这两个变量表示费用和价值)}\) \(\Large\textbf{1. 01背包}\) \(\large\tex ...

  2. 从零开始的 dbt 入门教程 (dbt core 开发进阶篇)

    引 在上一篇文章中,我们花了专门的篇幅介绍了 dbt 更多实用的命令,那么我们继续按照之前的约定来聊 dbt 中你可能会遇到的疑惑以及有用的概念,如果你是 dbt 初学者,我相信如下知识点一定会对你有 ...

  3. 从零开始的react入门教程(四),了解常用的条件渲染、列表渲染与独一无二的key

    壹 ❀ 引 在从零开始的react入门教程(三),了解react事件与使用注意项一文中,我们了解了react中事件命名规则,绑定事件时对于this的处理,以及事件中可使用的e对象.那么这篇文章中我们来 ...

  4. SSL证书类型价格和购买

    SSL证书 SSL和HTTPS的工作机制就不多说了, 密钥交换加通道依然是非常靠谱的安全访问方式, 除非你的浏览器连证书和DNS都被劫持, 否则中间节点要解密/篡改HTTPS访问的可能性微乎其微. 现 ...

  5. 【OpenGL ES】MVP矩阵变换

    1 前言 ​ 本文主要介绍 MVP 矩阵变换,其本质是线性变换,应用见→绘制立方体. Model:模型变换,施加在模型上的空间变换,包含平移变换(translateM).旋转变换(rotateM).对 ...

  6. python基础语法知识

    1.多组输入没有结束标志的两种表示形式 #method1: try: while True: #代码 except EOFError: pass #method2: while True: try: ...

  7. sentry-native 在 windows 平台上搭建

    官方文档:https://github.com/getsentry/sentry-native 虽说官方文档有搭建步骤,奈何我用 git bash 试了 n 遍,最终都会报一大堆错误,白白浪费了我快一 ...

  8. 深入理解Go语言(08):sync.WaitGroup源码分析

    一.sync.WaitGroup简介 1.1 sync.WaitGroup 解决了什么问题 在编程的时候,有时遇到一个大的任务,为了提高计算速度,会用到并发程序,把一个大的任务拆分成几个小的独立的任务 ...

  9. Windows如何快速修改hosts文件

    作为开发人员,修改hosts文件可能是一个经常会执行的操作(使用自定义域名映射),但是如果每次都需要在Windows资源管理中进入到目录:C:\Windows\System32\drivers\etc ...

  10. 案例分享:Qt激光加工焊接设备信息化软件研发(西门子PLC,mysql数据库,用户权限控制,界面设计,参数定制,播放器,二维图,rgv小车,期限控制,参数调试等)

    需求   1.键鼠控制,承担ui界面设计,布局兼容分辨率1024x768 ~ 1920x1080.  2.权限控制:三种权限,分为管理员(可以定制模块界面,修改产品名称等定制化软件和其他权限,同时具备 ...