“风格检查器”的关键问题是风格就像艺术:每个人对什么是好的风格和什么不是好的风格都有不同的看法。这意味着风格检查器将始终需要根据当地的艺术品味进行定制。
要做到这一点,需要一个完整的 C++ 解析器,它可以访问符号定义、范围规则和理想的各种流分析。AFAIK,CppCheck 不提供准确的解析或符号表定义,因此它的错误检查不能既深入又正确。我认为 Coverity 和 Fortify 使用 EDG 前端提供了类似的东西。我不知道他们的工具是否提供对符号表或数据流分析的访问。铿锵而来。
您还需要一种编写样式检查的方法。我认为所有工具都提供对 AST 和符号表的访问,并且您可以自己编写检查代码,但要以熟悉 AST 为代价,这对于像 C++ 这样的大型语言来说很难。我认为 Coverity 和 Fortify 有一些类似 DSL 的方案来指定一些检查。
如果要修复样式不正确的代码,则需要可以修改代码表示的东西。Coverity 和 Fortify 不提供此 AFAIK。我相信 Clang 确实提供了修改 AST 和重新生成代码的能力。您仍然必须对 AST 结构有相当深入的了解才能编写树黑客逻辑并使其正确。
我们的DMS 软件再造工具包及其C++ 前端提供了这些功能中的大部分。使用其 C++ 前端,DMS 可以解析 ANSI C++11、GCC4(带有 C++11 扩展)和 MSVS 2010(带有 C++11 扩展)[2021 年 5 月更新:现在是完整的 C++17 和大部分C++20] 构建具有完整类型信息的 AST 和符号表。也可以询问任意表达式 AST 节点的类型。目前,DMS 计算 C++ 的控制流而不是数据流。
一个 AST API 允许您在程序上编写任意检查;或更改 AST 以解决问题,然后 DMS 的漂亮打印机可以重新生成完整的、可编译的源文本,其中包含注释和保留的文字格式信息(例如,数字的基数等)。你必须知道 AST 结构才能做到这一点,就像其他工具一样,但它更容易知道,因为它与 DMS C++ 语法规则同构。C++ 前端带有我们的 C++ 语法。[DMS 使用 GLR 解析器使这成为可能]。
此外,可以使用 DMS 的规则规范语言,使用 C++ 本身的表面语法来编写模式和转换。可以将 OP 的“不抛出非 STL 异常”编码为
pattern nonSTLexception(i: IDENTIFIER):statement
= " throw \i; " if ~derived_from_STD_exception(i);
(元)引号内的内容是带有一些模式匹配转义的 C++ 源代码,例如,“\i”指的是占位符变量“i”,根据规则它必须是 C++ IDENTIFIER;整个“扔 \i;” 子句必须是 C++“语句”(C++ 语法中的非终结符)。规则本身主要表达要匹配的语法,但可以调用应用于匹配子树的语义检查(例如“~is_derived_from_STD_exception”)(在这种情况下,无论“\i”匹配什么)。
在编写这样的模式时,您不必知道 AST 的形状;模式知道它,它会自动匹配。如果您曾经编写过 AST walkers,您将体会到这是多么方便。
匹配知道 AST 节点,因此知道精确位置(文件/行/列),这使得生成具有精确位置信息的报告变得容易。
您需要向 DMS 添加一个自定义例程“inherits_from_STD_exception”,以验证传递给该例程的标识符树节点是(根据 OP 需要)从 std::exception 派生的类。这需要在符号表中找到“std::exception”,并验证标识符树节点的符号表条目是一个类声明,并从其他类声明传递继承(通过以下符号表链接),直到出现 std::exception找到符号表条目。
DMS 转换规则本质上是一对模式,“如果你看到这个,就用那个替换它”。
我们已经为 COBOL 和 C++ 构建了几个带有 DMS 的自定义样式检查器。它仍然是相当多的工作,主要是因为 C++ 是一种非常复杂的语言,您必须仔细考虑检查的确切含义。
更棘手的检查和那些开始陷入深度静态分析的测试需要访问控制和数据流信息。DMS 现在为 C++ 计算控制流,我们正在研究数据流分析(我们已经为 Java、IBM Enterprise COBOL 和各种 C 方言完成了这项工作)。分析结果被绑定回 AST 节点,这样人们就可以使用模式来查找样式检查的元素,然后在需要时按照数据流将元素绑定在一起。
当使用 DMS (或者实际上使用任何其他以任何半准确方式处理 C++ 的工具)说完所有的事情时,编码额外或复杂的样式检查不太可能是“方便的”。您应该希望“可能具有良好的技术背景”。