Matching the Clang AST
本文为译文,点击 此处查看原文。
本文档解释了如何使用 Clang 的LibASTMatchers
来匹配AST
的有趣节点,并执行使用匹配节点的代码。与 LibTooling 相结合,LibASTMatchers
可以帮助编写代码到代码的转换工具或查询工具。
我们假设您已经了解了关于 Clang AST 的基本知识,如果您想了解更多关于 AST 结构的信息,请参阅 Clang AST的介绍。
1. 介绍
LibASTMatchers
提供了一个域特定语言(DSL,domain specific language)来创建 Clang AST 上的谓词(predicates)。这个 DSL 是用C++写的,可以从C++中使用,允许用户编写一个程序来匹配 AST 节点和访问这些节点的C++接口,这些接口提取属性(attributes)、源文件位置(source locations)、或在AST级别提供的其他任何信息。
AST matchers
是 AST 中节点上的谓词。Matchers
是通过调用creator
函数创建的,此函数允许构建一个matchers
树,其中使用inner matchers
使匹配更加具体。
例如,要创建一个匹配翻译单元的 AST 中的所有 class 或 union 声明的matcher
,您可以调用 recordDecl()。要缩小匹配范围,例如查找名称为“Foo”
的所有 class 或 union 声明,请插入一个 hasName matcher:调用 recordDecl(hasName(“Foo”))
返回一个matcher,它匹配任何 namespace 中名为“Foo”
的 class 或 union 。默认情况下,接受多个 inner matchers 的 matchers
使用一个隐式的 allOf()。这允许进一步缩小匹配范围,例如匹配所有派生自“Bar”
的类:recordDecl(hasName("Foo"), isDerivedFrom("Bar")
。
2. 如何创建匹配器
在 Clang AST 中有超过1000个类,当试图找出如何为特定模式创建一个 matcher 时,您可能会很快迷失方向。本节将教会您如何使用严格的分步模式来构建您感兴趣的 matcher。注意,AST 的某些部分总是缺少 matchers。请参阅本文档第4节编写自己的 AST matchers
。
使用 matchers 的先决条件是了解要匹配的对象的 AST 是什么样子的。Clang AST 的介绍向您介绍了如何将翻译单元的 AST 转储为人类可读的格式。
一般来说,创建正确 matchers 的策略是:
- 在您要匹配的 Clang AST 中找到的最外层 class。
- 查看 AST Matcher Reference 来查找 matchers,它们要么匹配您感兴趣的节点,要么缩小节点上的属性。
- 创建您的外部匹配表达式(outer match expression),验证它是否按预期工作。
- 检查 matchers,确定下一个要匹配的内部节点是什么。
- 重复这些操作,直到 matcher 完成。
3. 在匹配表达式中绑定节点
Matcher expressions
允许您指定 AST 的哪些部分对某个任务感兴趣。然后,您通常希望对匹配的节点做一些事情,比如构建源代码转换
。
为此,匹配特定 AST 节点的 matchers (所以称为node matchers
)是可绑定的;例如,recordDecl(hasName("MyClass")).bind("id")
将匹配的recordDecl
节点绑定到字符串"id"
,稍后在匹配回调函数中检索。
4. 编写自己的matchers
根据 matcher 的类型和灵活性,定义一个 matcher 有多种不同的方法。
4.1 VariadicDynCastAllOfMatcher
VariadicDynCastAllOfMatcher<Base, Derived>
如果Base
类型的节点可以被动态地转换为Derived
,则所有这些节点满足匹配。这些 matchers 的名称都是名词,它们的Derived
非常相似。VariadicDynCastAllOfMatchers
是 matcher 层次结构的主干。大多数情况下,您的匹配表达式将从其中一个开始,您可以将它们表示的节点绑定到ids
,以便稍后处理。
VariadicDynCastAllOfMatchers
是在 C++03 中建模可变参数模板函数的可调用类。它们取任意数量的Matcher<Derived>
,并返回一个Matcher<Base>
。
4.2 AST_MATCHER_P(Type, Name, ParamType, Param)
大多数 matcher 定义使用matcher creation macros
。它们定义了类型Matcher<Type>
本身的匹配器,以及一个名为Name
的matcher-creation
函数,该函数接受一个ParamType
类型的参数并返回相应的matcher
。
有多个matcher definition macros
处理多态返回值和不同的参数计数。详情参考 ASTMatchersMacros.h。
5. Matcher creation函数
Matchers是通过对matcher creation
函数的嵌套调用生成的。大多数时候,这些函数要么是使用VariadicDynCastAllOfMatcher
创建的,要么是使用matcher creation macros
创建的(参见下面)。独立(free-standing)函数表示此 matcher 只是其他 matchers 的组合,例如 callee。