Klocwork(或其他工具)可以知道类型,typedef和#define指令吗?

时间:2011-06-22 16:21:08

标签: c++ static-analysis klocwork

我有looking forhelp detect工具来阻止程序作为64位代码正常运行的solution provided by ahmeddirie错误。最近,我一直在玩弄Klocwork及其自定义检查器功能,这让我可以使用XPath将源代码导航为树。这对于正则表达式的“更聪明”替代方法很有用,但我无法让它知道类型。

例如,假设我想找到使用forint计算的long循环的每个实例。以下代码很容易找到。

for (int i = 0; i < 10; i++)
    // ...

搜索此代码很简单,因为变量定义就在循环内部。但是,请考虑以下示例。

int i;
// ...
for (i = 0; i < 10; i++)
    // ...

这很难找到,因为变量定义与循环是分开的,并且必要的XPath表达式要么笨拙,要么容易出错。

那么,自定义Klocwork规则是否可以找到类似这样的表达式,其中包含类型感知,包括解析typedef#define语句?是否有其他工具可以做到这一点?

编辑1:请考虑以下示例。

typedef int myint;

void Foo() {
    int i;
    for (i = 0; i < 10; i++) {
        Bar();
    }

    myint j;
    for (j = 0; j < 10; j++) {
        Bar();
    }
}

{{3}}找到第一个循环,因为i的类型明确定义为int。但是,找不到第二个循环,因为typedef模糊了底层类型。什么工具以一种方式跟踪类型,以便将第二个循环变量j识别为int

3 个答案:

答案 0 :(得分:2)

您可以使用Clang(http://clang.llvm.org)甚至Elsa(https://github.com/dsw/oink-stack/)在类型传播和模板实例化后生成AST。两者都提供了一个不错的C ++ API和一些将AST转储成可读文本的方法。这两个选项都是免费的

答案 1 :(得分:1)

我工作的公司,Semantic Designs Inc.提供工具整合 用于分析和转换计划的一般基础设施 各种编程语言的特定分析组件。 这些被称为DMS。在C ++的情况下,DMS包括 集成的词法分析器,预处理器,解析器以及名称和类型 每个GCC3,GCC4,ISO14882c1998(ANSI)的分辨率组件, Visual C ++ 6.0和非托管Visual Studio 2005 C ++。对于各种 C的方言,也存在控制流分析,副作用 分析器和符号依赖性分析器,使用哪种工具 指针检查器,无效代码移除器,功能分析器和程序 切片机已经实施。

名称和类型解析组件提供完整的符号表 信息和查找功能,以便参考 标识符可以很容易地与其类型和其他类型相关联 声明性信息。信息就像被捕获的那样 由编译器使用,但与抽象语法树一起保留 以适合任何工具的自适应重复使用的形式 成分

Semantic Designs最近构建了一个自定义工具 与循环中的索引变量类型有关 声明,例如在您的示例中。在这种情况下,问题是 升级使用-fno-for-scope编译器开关的GCC2代码, 它为循环变量提供了范围解析规则 后来的GCC方言支持。该工具必须转换循环, 将循环变量的声明移动到外部上下文中 保留了-fno-for-scope范围规则。凡这样的变化 没有必要,没有改变。

因此,该工具必须识别与每个参考相关联的类型 到一个循环变量,在屏蔽范围的情况下区分,和 重构代码,以便GCC3和GCC4名称解析 导致与GCC2相同的语义解释 -fno换范围。这需要能够访问符号表 与每个变量引用相关的信息,在这种情况下 移动代码的地方,重建正确的语法 其声明被移动的任何变量的类型声明。 DMS提供的符号表和标识符引用表 C ++名称和类型解析组件包含所有必需的 信息,以及用于重建规定类型语法的模块 允许合成正确的新类型声明。

例如,请考虑以下示例:

// loop variable hides variable in global scope
// will change meaning without -fno-for-scope
// fix: move decl. of cnt before for-loop
//   optionally rename globcnt loop variable

float globcnt = 0.0;

int Foo::foo3() {
    for (int globcnt = 0; globcnt < 5; globcnt++) {
        globalInt += globcnt;
    }
    globalInt += 2*globcnt + 1;
    return 0;
}

GCC2 -fno-for-scope语义表明对globcnt的引用 循环外部是循环变量,即使GCC3会 考虑循环变量超出范围并解析引用 全局变量。该工具将此代码转换为:

float globcnt = 0.0;

int Foo::foo3() {
    int globcnt = 0;
    for (; globcnt < 5; globcnt++) {
        globalInt += globcnt;
    }
    globalInt += 2*globcnt + 1;
    return 0;
}

如果代码没有被转换,GCC4总会返回一个 来自Foo的值为1:foo3。但是,转变了价值 受到最初设计的循环迭代的影响 GCC2。该工具必须认识到对globcnt的最终引用 是int类型的局部变量,而不是全局变量 键入float,它可以通过符号表查找来执行,并执行操作 相应

另一方面,该工具在以下代码中识别出来 在循环之外没有对i的引用,所以这是可以接受的 (并且首选)保持循环变量声明完整。

int Foo::foo0() {
    for (int i = 0; i < 10; i++) {
        globalInt += i*i;
    }
    return 0;
}

答案 2 :(得分:1)

不完全确定这是否是您想要的,但您始终可以使用内置函数轻松解析类型。例如,回答你的问题(虽然可能不是你的潜在需求):

//ForStmt / Init::ExprStmt / Expr::BinaryExpr [ $type := Left.getTypeName() ] [ $type = 'int' | $type.contains('long') ]

这将找到非常方便地使用'int'或'long int'计数器类型的'for'循环,并且显然可以应用于基于表达式的语句的任何元素。

类型定义适用于此类操作,无论是程序员定义还是语言定义。但是,预处理器定义只会产生它们的本机语言类型(即宏本身不能通过KAST进行操作,只能扩展到它)。