在编译过程中,第三步语义分析(Semantic Analysis):验证语法是否正确,然后将所有节点组成抽象语法树 AST 。
抽象语法树(abstract syntax code,AST)是源代码的抽象语法结构的树状表示,树上的每个节点都表示源代码中的一种结构,之所以说是抽象的,是因为抽象语法树并不会表示出真实语法出现的每一个细节,比如说,嵌套括号被隐含在树的结构中,并没有以节点的形式呈现。抽象语法树并不依赖于源语言的语法,也就是说语法分析阶段所采用的上下文无关文法,因为在写文法时,经常会对文法进行等价的转换(消除左递归,回溯,二义性等),这样会给文法分析引入一些多余的成分,对后续阶段造成不利影响,甚至会使合个阶段变得混乱。因些,很多编译器经常要独立地构造语法分析树,为前端,后端建立一个清晰的接口。基于AST的不依赖具体文法和不依赖语言细节的特点,使得其在很多领域有广泛的应用,比如浏览器,智能编辑器,编译器。
上面的一个while循环,经过Clang分析所产生的AST如下图所示:
通过上面的语法树可以看到其描述代码的具体结构,而在Clang对代码编译时会进入一个语法树的解析阶段,则这个阶段中语法树的每个节点都会被遍历到,因此借助此阶段可以检测程序中所有代码的书写格式是否符合规范,甚至是对代码编写的质量作出分析。
创建一个简单的类HelloAST
可以通过以下命令查看它的语法树结构
我们可以看到自己的类定义、方法定义、方法调用在 AST 中所对应的节点。
其中第一个框为类定义,可以看到该节点名称为 ObjCInterfaceDecl
,该类型节点为 objc 类定义(声明)。 第二个框名称为 ObjCMethodDecl
,说明该节点定义了一个 objc 方法(包含类、实例方法,包含普通方法和协议方法)。 第三个框名称为 ObjCMessageExpr
,说明该节点是一个标准的 objc 消息发送表达式([obj foo])。
在clang-namespaceclang官网可以查询到这些
节点 | 描述 |
---|---|
ObjcCategoryDecl | 分类声明节点 |
ObjcCategoryImplDecl | 分类实现节点 |
ObjcImplementationDecl | 类实现节点 |
ObjcInterfaceDecl | 类声明节点 |
ObjcIvarDecl | 实例变量声明节点 |
ObjcMethodDecl | 实例方法声明 |
ObjcPropertyDecl | 属性声明节点 |
ObjcProtocolDecl | 协议声明节点 |
ParmVarDecl | 参数节点 |
… | … |
要实现自定义的clang插件(以C++ API为例),应按照以下步骤:
clang::PluginASTAction
(基于consumer的抽象语法树(Abstract Syntax Tree/AST)前端Action抽象基类) clang::ASTConsumer
(用于客户读取抽象语法树的抽象基类), clang::RecursiveASTVisitor
(前序或后续地深度优先搜索整个抽象语法树,并访问每一个节点的基类)等基类。PluginASTAction::CreateASTConsumer
PluginASTAction::ParseArgs
ASTConsumer::HandleTranslationUnit
RecursiveASTVisitor::VisitDecl
RecursiveASTVisitor::VisitStmt
等方法,实现自定义的分析逻辑。static FrontendPluginRegistry::Add<MyPlugin> X("my-plugin- name", "my-plugin-description");
添加一个简单的测试插件
clang::PluginASTAction
是一个基于consumer的AST前端Action抽象基类。
clang::ASTConsumer
则是用于客户读取AST的抽象基类。它们之间的关系是clang::PluginASTAction
作为一个关于AST的插件,同时也是访问clang::ASTConsumer
的入口;而clang::ASTConsumer
则是用于定义如何取得AST相关内容。
定义继承于clang::PluginASTAction
和clang::ASTConsumer
类的子类后,通过static clang::FrontendPluginRegistry::Add<MyPluginASTAction> X("VisitAST", "My plugin”);
就可以把插件注册到Clang中。
Build之后能够得到VisitAST插件,可以添加到我们的项目配置中。配置方式参考前面文章Pass配置
这个Plugin的作用是在编译过程中报一个Error。由此可见,我们可以在编译过程中插入一些我们的逻辑。
添加一个入口
添加自定义工具
在MatchFinder的run方法中,可以找到对应的节点进行处理
完整代码
其中
FixItHint可用于修复改动,将想要的格式替换原有的格式
Waring效果
Error效果
1、源码添加位置是在
2、CodeCheck文件夹平级的CMakeList.txt要添加
3、CodeCheck文件夹内CMakeList.txt要添加
4、检测项目的Other C Flags添加配置
-Xclang -load -Xclang $(SRCROOT)/CodeCheck.dylib -Xclang -add-plugin -Xclang CodeCheck