Clang AST 简介

本文档简要介绍了 Clang AST 的奥秘。它面向想要为 Clang 做贡献或使用基于 Clang AST 的工具(例如 AST 匹配器)的开发人员。

幻灯片

介绍

Clang 的 AST 与其他一些编译器生成的 AST 不同,因为它与书面的 C++ 代码和 C++ 标准非常相似。例如,括号表达式和编译时常量在 AST 中以未简化的形式提供。这使得 Clang 的 AST 非常适合重构工具。

所有 Clang AST 节点的文档可通过生成的 Doxygen 获得。Doxygen 在线文档也被您最喜欢的搜索引擎索引,这将使您搜索 clang 和 AST 节点的类名通常会找到您正在寻找的类的 Doxygen(例如,搜索:clang ParenExpr)。

检查 AST

熟悉 Clang AST 的一种好方法是实际查看一些简单示例代码中的 AST。Clang 具有内置的 AST 转储模式,可以使用标志 -ast-dump 启用。

让我们看一个简单的示例 AST

$ cat test.cc
int f(int x) {
  int result = (x / 42);
  return result;
}

# Clang by default is a frontend for many tools; -Xclang is used to pass
# options directly to the C++ frontend.
$ clang -Xclang -ast-dump -fsyntax-only test.cc
TranslationUnitDecl 0x5aea0d0 <<invalid sloc>>
... cutting out internal declarations of clang ...
`-FunctionDecl 0x5aeab50 <test.cc:1:1, line:4:1> f 'int (int)'
  |-ParmVarDecl 0x5aeaa90 <line:1:7, col:11> x 'int'
  `-CompoundStmt 0x5aead88 <col:14, line:4:1>
    |-DeclStmt 0x5aead10 <line:2:3, col:24>
    | `-VarDecl 0x5aeac10 <col:3, col:23> result 'int'
    |   `-ParenExpr 0x5aeacf0 <col:16, col:23> 'int'
    |     `-BinaryOperator 0x5aeacc8 <col:17, col:21> 'int' '/'
    |       |-ImplicitCastExpr 0x5aeacb0 <col:17> 'int' <LValueToRValue>
    |       | `-DeclRefExpr 0x5aeac68 <col:17> 'int' lvalue ParmVar 0x5aeaa90 'x' 'int'
    |       `-IntegerLiteral 0x5aeac90 <col:21> 'int' 42
    `-ReturnStmt 0x5aead68 <line:3:3, col:10>
      `-ImplicitCastExpr 0x5aead50 <col:10> 'int' <LValueToRValue>
        `-DeclRefExpr 0x5aead28 <col:10> 'int' lvalue Var 0x5aeac10 'result' 'int'

翻译单元中的顶层声明始终是 翻译单元声明。在这个例子中,我们第一个用户编写的声明是 “f” 的 函数声明。“f” 的主体是一个 复合语句,其子节点是声明我们的结果变量的 声明语句,以及 返回语句

AST 上下文

翻译单元的 AST 的所有信息都捆绑在 ASTContext 类中。它允许从 getTranslationUnitDecl 开始遍历整个翻译单元,或访问 Clang 的 标识符表 用于解析的翻译单元。

AST 节点

Clang 的 AST 节点基于一个类层次结构,该层次结构没有共同的祖先。相反,存在多个较大的层次结构,用于基本节点类型,例如 DeclStmt。许多重要的 AST 节点从 TypeDeclDeclContextStmt 派生,某些类从 Decl 和 DeclContext 同时派生。

AST 中还有许多节点不属于更大的层次结构,并且只能从特定其他节点访问,例如 CXXBaseSpecifier

因此,要遍历整个 AST,需要从 TranslationUnitDecl 开始,然后递归地遍历可以从该节点访问的所有内容——此信息必须为每个特定的节点类型进行编码。该算法在 RecursiveASTVisitor 中编码。请参阅 RecursiveASTVisitor 教程

Clang AST 中的两个最基本节点是语句 (Stmt) 和声明 (Decl)。请注意,表达式 (Expr) 在 Clang 的 AST 中也是语句。