我正在考虑对 C++ 代码示例做一些静态分析项目,而不是整个程序。一般来说,静态分析需要一些更简单的中间表示,但如果没有整个程序代码,就无法准确地创建这样的表示。
不过,我知道Java有这样的工具——它基本上“猜测”丢失的信息,因此即使它不再健全或不完整,也允许进行静态分析。
是否有任何类似的东西可用于将部分 C++ 代码转换为某种中间形式(例如 LLVM 字节码)?
作为一般规则,如果你猜,你猜错了;基于此类猜测的静态分析器的任何投诉都是误报,并且往往会导致很高的拒绝率。
如果您坚持猜测,您将需要一个可以解析任意 C++ 片段的工具。(“猜猜这个方法的静态分析......”)。大多数 C++ 解析器只会解析完整的源文件,而不是片段。
您还需要一种建立部分符号表的方法。(“I 被列为 FOO 的参数,但没有类型信息,并且它与调用 FOO 之后的语句中声明的 I 不同”)。
我们的DMS Software Reengineering Toolkit及其C++ 前端可以提供片段解析,并可用作部分符号表的跳板。
DMS 提供对代码的一般解析/分析/转换,由提供给 DMS 的显式语言定义确定。C++ 前端提供了一个完整、强大的 C++ 前端,使 DMS 能够解析 C++、构建 AST,并使用属性语法 (AG) 为此类 AST 构建符号表,其中 C++ 查找规则被编码。AG 是一种在 AST 节点上编码的函数式计算;C++符号表生成器本质上是一个大函数程序,其部分附加了C++的BNF语法规则。
作为通用解析机制的一部分,给定语言定义(例如 C++ 前端),DMS 可以使用其内置模式语言解析该语言的任意(非)终端。因此 DMS 可以解析表达式、方法、声明等或任何其他格式良好的代码片段并构建 AST。如果提供了格式不正确的片段,则当前在片段解析时会出现语法错误;可以扩展 DMS 的错误恢复以生成合理的 AST 修复,从而解析任意元素。
部分符号表更难,因为大部分符号表构建机制依赖于正在构建的符号表的其他部分。然而,由于这都被编码为 AG,因此可以运行与所解析的片段相关的 AG 部分,例如方法的符号表构建逻辑。AG 可能需要进行广泛的修改,以使其能够在缺少符号定义的“假设”下运行;这些实际上将成为约束。当然,丢失的符号可能是多种事物中的任何一种,并且您最终可能会得到可能的符号表的配置。考虑:
{ int X;
T*X;
}
不知道 T 是什么,短语的类型(甚至它的句法类别)无法唯一确定。(DMS 将解析 T*X;并报告不明确的解析,因为有多种可能的匹配解释,请参阅Why can't C++ be parsed with a LR(1) parser?)
我们已经在部分解析和部分符号表方面做了一些工作,我们在其中实验性地使用 DMS 来捕获包含预处理器条件的代码,其中一些条件状态未定义。这导致我们构建条件符号表条目。考虑:
#if foo
int X;
#else
void X(int a) {...}
#endif
...
#if foo
X++;
#else
X(7);
#endif
使用条件符号,此代码可以进行类型检查。X 的符号表条目类似于“X ==> int if foo else ==> void(int)”。
我认为对带有约束的大型程序片段进行推理的想法很棒,但我怀疑这真的很难,而且您将永远尝试解析有关约束的足够信息以进行静态分析。
SciTools 的《Understanding 4 C++》是一款解析源代码并为各种事物提供指标的产品。作为一种工具,该产品有点像源代码浏览器,但我个人不使用它,因为 Visual Studio 的 Intellisense 也一样好。
它的真正强大之处在于它带有C 和 Perl API。因此,您可以使用它编写自己的静态分析工具。是的,它可以很好地处理丢失的代码文件。此外,了解 4 C++ 在 Windows 和许多其他操作系统上的工作。
至于您关于中间代码的最后一个问题,Understanding 4 C++ 并没有为您提供“中间”形式,但通过它的 API,它确实为您提供了抽象语法树之上的抽象层,为您提供了强大的功能分析源代码。我在工作中使用此 API 编写了很多工具,以及一个封装其原生 C API的托管 C++ API(我在 codeplex 上编写并公开共享)。