删除不使用的或冗余的代码

Removal of unused or redundant code

本文关键字:代码 冗余 删除      更新时间:2023-10-16

如何检测从未被调用的函数定义并从文件中删除它们,然后保存?

假设我现在只有一个CPP文件,它有一个main()函数和许多其他函数定义(函数定义也可以在main() 内)。如果我要写一个程序来解析这个CPP文件,并检查一个函数是否被调用,如果它没有被调用,那么删除它的方法是什么?

我想到了一些方法:

  1. 我会找到main()开始和结束的行号。我可以通过维护一堆开括号和闭括号{}来实现。
  2. main之后的都是函数定义。然后我可以解析函数定义。要做到这一点,我可以用以下方式解析它:

    < string >< open paren >< comma separated string(s) for arguments >< closing paren >
    
  3. 一旦我有了(2)中描述的所有函数的名称,我就可以用它的名称作为键和值作为bool来制作一个映射,指示函数是否被调用一次

  4. 最后再次解析文件,以检查是否有任何调用函数的名称与此映射中相同。函数调用可以来自main内部,也可以来自其他函数。键的值(即函数名)可以根据函数是否被调用来标记。

我觉得我把我的逻辑复杂化了,可以用更聪明的方法来完成。根据上面的逻辑,很难找到所有的极端情况(可能有很多)。此外,函数指针可能会使解析逻辑变得困难。如果这还不够,还可以对函数指针进行typedef编辑。

我如何开始设计我的程序?map(用于维护文件名)和stack(用于维护大括号)是正确的数据结构吗?还是有其他更适合处理它的方法?

注意:我不寻找任何工具来做到这一点。我也不想使用任何库(如果它的存在是为了让事情变得简单)。

我认为你不应该尝试从头开始构建一个c++解析器,因为其他人在评论中说这真的很难。恕我直言,您最好从CLang库开始,而不是直接使用抽象语法树进行低级解析。

您甚至可以使用range作为如何使用它们生成交叉引用表的示例。

或者,您可以直接使用GNU global,因为它的gtags命令直接生成您必须分析的定义和参考数据库。

恕我直言,这两种方法比从头开始创建c++解析器要简单得多。

我能想到的最简单的方法是:

    写一个最小的解析器,可以识别函数。它只需要检测函数的开始行和结束行。
  • 以编程方式注释掉第一个函数,保存到临时文件。
  • 尝试通过调用编译器来编译文件。
  • 检查是否有编译错误,如果有,则调用函数,如果没有,则未使用。
  • 继续下一个功能
  • 这是一个评论,而不是一个答案,但我把它贴在这里,因为它太长了,不适合评论空间
有很多问题你应该考虑。首先,您不应该假设main()是源文件中的第一个函数。

即使是这样,也应该在main()之前有一些函数头声明,以便编译器可以识别它们在main中的调用。

接下来,函数的开始和结束大括号不需要在单独的行中,它们也不需要是该行中唯一的字符。一般来说,几乎整个c++代码都可以放在一行中!

此外,函数可以在具有相同名称的情况下与参数类型不同(重载),因此如果您不将整个代码解析为参数类型,则无法识别调用的是哪个函数。甚至更多:您将不得不执行与标准转换/强制类型转换匹配的类型列表,可能会考虑内联构造函数调用。当然,您不应该忘记默认参数。谷歌解析重载函数调用的例子,请看这里的概要

另外,可能存在未使用的函数链。例如,如果a()调用b(), b()调用c()d(),但a()本身没有被调用,那么整个四个都是未使用的,即使存在对b(), c()d()的"调用"。

也有可能通过指针调用函数,在这种情况下,您可能无法找到调用。例子:

int (*testfun)(int) = whattotest ? TestFun1 : TestFun2;  // no call
int testResult = testfun(paramToTest);  // unknown function called

最后,代码可以用#define -s来混淆。

结论:你可能需要编写自己的c++编译器(除了机器码生成器)来实现你的目标。

这是一个非常粗略的想法,我怀疑它是否非常有效,但也许它可以帮助你开始。首先遍历文件一次,找出所有函数名(我不完全确定您将如何做到这一点)。但一旦你有了这些名字,再次遍历文件,在文件的任何地方寻找函数名,在main和其他函数中。如果您发现超过1个实例,则意味着该函数正在被调用,应该保留