Klocwork(或其他工具)能知道类型、typedefs和#define指令吗

Can Klocwork (or other tools) be aware of types, typedefs and #define directives?

本文关键字:typedefs 类型 #define 指令 其他 工具 Klocwork      更新时间:2023-10-16

我一直在寻找工具来帮助检测阻止程序作为64位代码正常运行的错误。最近,我一直在尝试Klocwork及其自定义检查器功能,它使我能够使用XPath将源代码作为树进行导航。作为正则表达式的"更聪明"的替代方案,这很有用,但我还没能让它了解类型。

例如,假设我想找到使用intlong进行计数的for循环的每个实例。下面的代码很容易找到。

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();
    }
}

ahmeddirie提供的解决方案找到了第一个循环,因为i的类型被明确定义为int。但是,第二个循环找不到,因为typedef模糊了底层类型。哪些工具可以跟踪类型,从而将第二个循环变量j识别为int

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

不完全确定这是否是您想要的,但您总是可以使用内置函数轻松地解析类型。例如,回答你的问题(尽管可能不是你的根本需求):

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

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

无论是程序员定义的还是语言定义的,类型定义都可以接受这种操作。然而,预处理器定义只会产生它们的母语类型(即宏本身不可通过KAST进行操作,只能扩展到它扩展到的类型)。

我工作的公司Semantic Designs股份有限公司提供了包含用于分析和转换程序的通用基础设施用于各种编程语言的特定分析组件。这些统称为DMS。在C++的情况下,DMS包括集成lexer、预处理器、解析器以及名称和类型GCC3、GCC4、ISO14882c1998(ANSI)、,Visual C++6.0和非托管Visual Studio 2005 C++。对于各种方言的C,也存在控制流分析,一个副作用分析器和符号依赖性分析器,使用这些工具指针检查器、非活动代码移除器、函数探查器和程序切片器已经实现。

名称和类型解析组件提供完整的符号表信息和查找功能,以便引用标识符可以很容易地与其类型和其他声明性信息。信息与捕获的信息类似由编译器使用,但与抽象语法树一起保留以可由任何包含组成部分

Semantic Designs最近构建了一个自定义工具具体与循环中的索引变量类型相关声明,例如在您的示例中。在这种情况下,问题是为了升级使用-fno作为作用域编译器开关的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表示对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:foo3中的1值。不过,经过转换后,该值会受到最初设计的循环迭代的影响GCC2。该工具必须认识到,对globcnt的最终引用是int类型的局部变量,而不是的全局变量键入float,它可以通过符号表查找来完成,然后执行照着

另一方面,该工具在以下代码中识别出循环之外没有引用i,因此可以接受(并且首选)以保持循环变量声明不变。

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