iOS学习笔记 利用Clang 为App提升质量
基于Clang可以开发出在线网页代码导航工具,比如DXR
用于代码增量分析、代码可视化、代码质量报告的保障App质量的系统平台CodeChecker
什么是Clang
Clang是C、C++、OC的编译前端 ,Swift有自己的编译前端SIL optimizer
简单说,Clang是把C、OC、C++源码编译成为AST的编译前端。
AST:抽象语法树,代码更精简,更适合静态检查
Clang的优势:
-
对于使用者来说,Clang编译的速度非常快,对内存的使用率非常低,并且兼容GCC(XCode5之前的编译器)
-
对于代码诊断来说,Clang也非常强大。使用Clang编译前端,可以精确的显示出问题所在的行和具体位置,并可以准确说明出现问题的原因,指出错误类型是什么,使我们快速掌握问题的细节。
-
Clang对typedef的保留和展开也处理的非常好。typedef可以缩写很长的类型,保留typedef对于粗粒度诊断分析很有帮助。当需要了解细节时,展开typedef即可。
-
Fix-it提示也是Clang提供的一种快捷修复源码问题的方式。在宏处理上,很多宏都是深度嵌套的,Clang会自动打印实例化信息和嵌套范围信息来帮助宏的诊断和分析。
-
Clang的架构是模块化的。除了代码静态分析外,利用其输出的接口还可以开发用于代码转义、代码生成、代码重构的工具,方便与IDE进行集成。
与Clang相比,GCC对于OC的支持比较差,效率和性能不达标,难以推动GCC团队发展。于是,苹果开发了LLVM工具套件,将GCC全面替换成了LLVM。
Clang提供了一个易用性很高的黑盒 Driver,用于封装前端命令和工具链的命令,使其易用性得到了提升。
Clang做了什么事?
首先,Clang会对代码进行词法分析,将代码切分成Token。
如查看main.m函数的所有Token:
clang -fmodules -E -Xclang -dump-tokens main.m
这个命令可以显示每个Token的类型、值,以及位置。
查看所有的Token类型
Clang定义的Token类型,大致可以分四类:
- 关键字:语法中的关键字,如if、while、for等。
- 标识符: 变量名
- 字面量: 值、数字、字符串
- 特殊符号: 加减乘除等符号
词法分析完后会进行语法分析,将输出的Token按照语法组合成语义,生成类似VarDecl这样的节点,然后将节点按照层级关系构成AST。
查看main.m的语法树
clang -fmodules -fsyntax-only -Xclang -ast-dump main.m
TranslationUnitDecl是根节点,表示一个编译单元。
Decl表示一个声明
Expr表示这是一个表达式
Literal表示字面量,一个特殊的Expr
Stmt表示陈述
Clang还有众多的节点类型。Clang里,节点主要分成Type类型、Decl声明、Stmt陈述这三种,其余都是这三种的派生。
总结:先做词法分析,将代码切分成Token。在进行语法分析,将输出的Token按照语法组合成语义,生成类似VarDecl这样的节点,然后将节点按照层级关系构成AST。
示列如下:
TranslationUnitDecl 0xc75b450 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0xc75b740 <<invalid sloc>> <invalid sloc> implicit __builtin_va_list ‘char *’
`-FunctionDecl 0xc75b7b0 <test.cpp:1:1, line:7:1> line:1:5 main ‘int (void)’
`-CompoundStmt 0xc75b978 <line:2:1, line:7:1>
|-DeclStmt 0xc75b870 <line:3:2, col:7>
| `-VarDecl 0xc75b840 <col:2, col:6> col:6 used a ‘int’
|-DeclStmt 0xc75b8d8 <line:4:2, col:12>
| `-VarDecl 0xc75b890 <col:2, col:10> col:6 used b ‘int’ cinit
| `-IntegerLiteral 0xc75b8c0 <col:10> ‘int’ 10
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< a = b <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
|-BinaryOperator 0xc75b928 <line:5:2, col:6> ‘int’ lvalue ‘=‘
| |-DeclRefExpr 0xc75b8e8 <col:2> ‘int’ lvalue Var 0xc75b840 ‘a’ ‘int’
| `-ImplicitCastExpr 0xc75b918 <col:6> ‘int’ <LValueToRValue>
| `-DeclRefExpr 0xc75b900 <col:6> ‘int’ lvalue Var 0xc75b890 ‘b’ ‘int’
<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<
`-ReturnStmt 0xc75b968 <line:6:2, col:9>
`-ImplicitCastExpr 0xc75b958 <col:9> ‘int’ <LValueToRValue>
`-DeclRefExpr 0xc75b940 <col:9> ‘int’ lvalue Var 0xc75b840 ‘a’ ‘int
Clang提供了什么能力?
Clang为一些需要分析代码语法、语义信息的工具提供了基础设施。这些基础设施就是LibClang、ClangPlugin、LibTooling。
LibClang
LibClang提供了一个稳定的高级C接口,XCode使用的就是LibClang。LibClang可以访问Clang的上层高级抽象的能力,比如获取所有Token、遍历语法树、代码补全等。由于API稳定,Clang版本更新对其影响不大。但是LibClang并不能完全访问到ClangAST信息。
使用LibClang可以直接用它的C API,或者官方脚本Python binding。还有开源的node-js/ruby binding,OC写的ClangKit库。
Clang Plugins
Clang Plugins可以在AST上做些操作,这些操作能够集成到编译中,成为编译的一部分。插件是在运行时由编译器加载的动态库,方便集成到构建系统中。
使用Clang Plugins 一般希望完全控制Clang AST,同时能够集成在编译流程中,可以影响编译的过程,进行中断或者提示。
LibTooling
LibTooling是一个C++接口,通过LibTooling能够编写独立运行的语法检查和代码重构工具。
LibTooling优势如下:
- 所写的工具不依赖于构建系统,可以作为一个命令单独使用,比如clang-check,clang-fixit,clang-format
- 可以完全控制Clang AST
- 能够和Clang Plungins共用一份代码
与Clang Plugins相比,LibTooling无法影响编译过程;
与LibClang相比,LibTooling的接口没有那么稳定,也没法开箱即用,当AST的API升级后需要更新接口的调用。
但是,LibTooling基于能够完全控制Clang AST 和可独立运行的特点,可以做到很多事情。
- 改变代码:可以改变Clang生成代码的方式。基于现有代码可以做出大量的修改。还可以进行语言的转换,比如OC转换Swift或JS。
- 做检查:检查命名规范,增加更强的类型检查,还可以按照自己的定义进行代码的检查分析。
- 做分析:对源码做任意类型分析,甚至重写程序。给Clang添加一些自定义的分析,创建自己的重构器,还可以基于工程生成相关图形或文档进行分析。
基于LibTooling有个开发人员工具合集:ClangTools。工具主要有:
- 语法检查工具 clang-check
- 自动修复编译错误工具 clang-fixit
- 自动代码格式工具 clang-format
- 新语言和新功能的迁移工具
- 重构工具
LibTooling的基本使用方法:LibASTMatchersTutorial