静态分析不仅仅是直接找出缺陷,还在于发现容易滋生缺陷、损害代码可读性和可维护性的情形。静态分析可以检查代码的许多其他属性:
- 代码度量:例如,含有过多循环、if、else、switch 和 case 语句的方法最终会变得难以理解,因而难以维护。通过圈复杂度(Cyclomatic Complexity)这一代码度量来统计它们,是评估方法何时变得过于复杂的好办法。
- 依赖关系:如果程序中的类纠缠在一起,代码中任何变更的影响都会变得不可预测。静态分析可以帮助评估类和组件何时纠缠在一起。
- 不可变性:被多个线程并发使用的类型应当是不可变的;否则,您将不得不用复杂的锁策略来保护状态的读写访问,而这些策略最终会难以维护。静态分析可以确保某些类保持不可变。
- 死代码:死代码是可以安全移除的代码,因为它在运行时不再被调用。它不仅可以被移除,而且必须被移除,因为这些多余的代码会给程序增加不必要的复杂性。静态分析可以找出程序中的大部分死代码(但不是全部)。
- API 破坏性变更:如果您向客户提供 API,很容易在不经意间移除某个公有成员,从而破坏客户的代码。静态分析可以比较程序的两个状态,并对这种隐患发出警告。
- API 使用:有些 API 需要谨慎使用。例如,持有可释放(disposable)字段的类,通常自身也必须是可释放的——除非该可释放字段的生命周期与类实例的生命周期不一致,而那本身就散发着设计问题的味道。
代码异味(code smell)也可以被视为一种容易滋生缺陷的情形。下面是维基百科给出的定义:
In computer programming, 代码异味, (or 坏味道) is any symptom in the source code of a program that possibly indicates a deeper problem. According to Martin Fowler, "a code smell is a surface indication that usually corresponds to a deeper problem in the system". Another way to look at smells is with respect to principles and quality: "smells are certain structures in the code that indicate violation of fundamental design principles and negatively impact design quality". Code smells are usually not bugs—they are not technically incorrect and do not currently prevent the program from functioning. Instead, they indicate weaknesses in design that may be slowing down development or increasing the risk of bugs or failures in the future. Bad code smells can be an indicator of factors that contribute to technical debt. Robert C. Martin calls a list of code smells a "value system" for software craftsmanship.许多实用的工具可以检测 C++ 代码库中的缺陷,包括 Cppcheck、Clang-Tidy 和 Visual Studio 分析器。但检测代码异味呢?
静态分析工具的开发者可以决定哪些情况算缺陷,而代码异味则更具主观性,取决于开发团队的选择。例如,一个团队可能认为超过 20 行的方法就算复杂,而另一个团队可能把上限设为 30。如果一个工具要检测代码异味,它也必须允许团队定制相关的规则和阈值。
把代码当作数据,是检测代码异味的最佳方式
静态分析的理念是分析源代码的各种属性并报告这些属性;但从哲学上讲,它也是“把代码当作数据”的理念。对应用程序开发者来说,这可能感觉有些反常,因为我们习惯于把源代码看作指令、过程和算法。但把代码当作数据同样威力巨大。
分析完一个源文件后,我们可以提取它的 AST,并生成一个包含丰富代码信息的模型。然后,我们可以用一种类似 SQL 的代码查询语言来查询这个模型。
CppDepend提供了一种名为CQLinq的代码查询语言,可以像查询数据库一样查询代码库。开发者、设计师和架构师可以定义自己的查询,轻松找到代码异味。
使用 CQLinq,我们可以把来自代码度量、依赖关系、API 使用以及其他模型信息的数据组合起来,定义识别特定代码异味的高级查询。
下面是一条匹配最复杂方法的 CQLinq 查询示例:

小结
结合多种 C++ 工具来检测代码库中的问题往往是更好的做法:一些工具专注于缺陷,另一些还能检测代码异味。CppDepend 把这些方法结合在一起:既提供了定义自定义查询的便捷方式,也能导入其他静态分析工具的结果,让这些结果同样可以用 CQLinq 查询。
