我们如何检测源代码中的所有指针比较? C++

How can we detect all pointer comparisons in source code? c++

我们想要从 class 类型中找到所有指针比较。例如,我们有一个 class A 并从 A 派生出 classes,如 B、C 等。

A *pa;
A *pa2;
B *pb;

必须在我们的源代码中找到所有比较,例如 if (pa == pa2) 或 if (pa != pb)。

我知道我们可以使用 CLang 分析器来查找这些比较,但我们的源代码与 CLang 不兼容。我们正在使用 visual studio 2015.

请不要给出这样的解决方案;从源代码中删除 class A 然后尝试编译它,以便从 class A 中找到所有无法编译的用法。

有没有人找到解决方案?像 CppCheck(检查可能的错误)或 Visual Studio extension?

这样的工具

编辑:

有谁知道,我怎样才能在我的代码中使用 CppDepend/CQLinq 语法找到所有比较?它也可以帮助我。 CppDepend 使用 CLang 但如果它有解析错误它会继续解析。

我们的 DMS Software Reengineering Toolkit with its C++14 front end 可用于执行此操作。

DMS 是一种通用的程序分析和转换机制,可以对其进行定制,以在作为插件模块提供给它的编程语言上实现所需的效果。它的 C++14 前端可配置地处理纯 ANSI、GCC/Clang-style 语法或 Visual Studio 语法。它包括一个完整的预处理器。

为了实现 OP 的目的,可以将 DMS 配置为:

  1. 解析生成 AST 的编译单元。
  2. 对每个编译单元,执行名称和类型解析。这构建了包含类型信息的符号 tables,并提供了一个基础 用于计算任意表达式的类型。这种能力是 内置于 DMS 的 C++ 前端。
  3. 抓取 AST,寻找运算符 == 和 !=
  4. 要求 DMS 计算右侧和左侧子表达式的类型
  5. 验证类型是目标 class,还是从目标 class 继承的类型。 (大概目标 class 被识别为在某个源 file/line 位置定义;这可以通过搜索符号 table 找到。检查一个类型是否从另一个派生只是一个递归搜索为符号记录的可能多个父链接的问题 table 链接以检查父链接是否是所需的目标类型)。
  6. 报出算子的文件名、源行、列。

DMS 和 C++14 前端提供的 machinery/APIs 直接支持上述每个步骤。这可能需要将几页自定义代码添加到 DMS 才能达到效果。

我想我漏掉了问题。因此,您只想通过比较找到指向类型 A、B、C 等的任何指针的所有实例,其中指针在条件表达式中使用...

所以你知道所有的类型名称。这意味着存在有限数量的类型和有限数量的比较,例如== != <= >= < > 对吗?

因此,对于所有类型创建的指针的每个实例,构建一个 table。这为您提供了您要查找的每个指针的编码名称。

fred *myfred, *yourfred, *thefred;

账户 *primaryacct, *secondacct; ...等等...

您的 table 将是:
我的弗雷德
你弗雷德
特弗雷德
初级帐户
secondacct

现在对于每个实例的每个实例 - 从第一个开始 'myfred' 找到 myfred ,然后是 == 然后是 != 等等(吸收所有空格),当你找到第一个(左侧监狱例如

secondacct<=

然后获取右侧)并将其与您构建的 table 中的每个指针编码名称进行比较。当你有像 myfred!=primaryacct 这样的比赛时,你可以随心所欲地使用它。为了论证,我们只是说,您想对给定的比较或比较列表执行全局搜索和替换,您可以通过打开一个附加文件进行输出来随时执行此操作,并在您读入并找到每个如果发生这种情况,您可以将其输出到新的源代码文件中。

基本上只是找到每一个比较,查看它的每一面,看看双方是否都有一个在你的 table 中的编码名称。本质上,您只是在比较字符串的有限组合的任一侧使用相同的 table 标识符来解析代码——它们同样是:[ == != <= >= < > ]

我不知道有哪个软件工具可以执行此操作,但您可以非常快速地编写代码。当然,它只能服务于此一个目的,但它可以快速完成工作。

当然,我假设您的源代码是文本形式,并且您可以打开文件并读入它来执行此操作。如果是这样,那么您可以随心所欲地获得结果,例如每个文件的列表和每个表达的比较中找到出现的行。

读入时抓取整行代码-

在 C 中 - 只需使用 fgets

在 C++ 中 - 使用 getline

然后使用上述逻辑解析您读入的缓冲区。

------------- 已编辑 -------------- 关于以下评论

@YusufRamazanKaragöz - Oak - 我为过度概括表示歉意。您是否有机会提供包含其中一些问题的代码示例 - 例如,如果它涵盖多行怎么办?我的思考过程基于你写的 "All comparisons like if (pa == pa2) or if (pa != pb) must be found in our source code" 仅此而已,所以我没有扩展到函数 returns 等。至于构建 table - 你知道类型正确吗?因此,对于每一行声明了这些类型的变量的行,就是您构建它的方式。例如,如果我想要程序所有文件的每一行代码中每个 char 定义变量的 table - 我会在所有行中搜索单词 char。然后在该行之后我会寻找逗号分隔的字符串,直到没有逗号或分号(可以继续到下一行,所以使用 fgetc 代替 fgets)。其中一些声明是直接的,一些可能是 *char,一些是 char[] - 等等。然后我会得到一个 char 类型的每个变量的列表。我的意思是,如果当您搜索您正在谈论的类型名称时,您看不到它出现的那一行,以及它之后声明的所有内容吗?如果可以,那么你可以建立索引table。或者有什么原因我不明白为什么这不能完成?查找强制转换的值会创建另一组解析规则,并进一步使任务复杂化,就像模板与对象的比较一样。直到现在,我还没有真正理解您从最初的问题中遇到的困境。我真的很想提供帮助,但也许涵盖每种解析范例的代码块可以帮助我确定是否可以。事实上,如果你能告诉我你为什么要这样做,我就会有一个更好的思考过程。你想在全球范围内改变什么吗?如果你认为努力是徒劳的,我当然会听从你的决定并停止尝试。感谢您的耐心等待,我希望您能找到解决方案。

我的解决方案是:(如@M.M 所说)用模板包装器 class 包装指针,实现像 -> * 这样的运算符重载(因此编译问题更少)并删除比较== != 之类的运算符(因此找到与编译错误的比较)。可以使用正则表达式替换所有指针。 (A *A_Wrapper)

我还发现,在地图中使用指针就像指针比较。如果您在地图中使用指针,您还应该删除包装器 class.

中的 <-Operator

当然,我有编译错误,但这些错误并不难解决。看来这是确定的解决方案。

希望对大家有所帮助。