从c++文件中删除无用的行

Removing useless lines from c++ file

本文关键字:无用 删除 c++ 文件      更新时间:2023-10-16

很多时候,当我调试或重用某些代码时,文件开始获取什么都不做的行,尽管它们可能在某一点上做了一些事情。

向量和被填充,然后不使用,类/结构被定义但从未使用,函数被声明但从未使用。

我知道,在许多情况下,其中一些东西并不是多余的,因为它们可能从其他文件中可见,但在我的情况下,没有其他文件,只是我的文件中有无关的代码。

虽然我知道从技术上讲,调用push_back会有一些作用,因此向量本身并没有被使用,但在我的情况下,它的结果是未使用的。

那么:有没有一种方法可以做到这一点,要么使用编译器(clang、gcc、VS等),要么使用外部工具?

示例:

#include<vector>
using namespace std;
void test() {
vector<int> a;
a.push_back(1);
}
int main() {
test();
return 0;
}

应变为:int main(){return 0};

我们的DMS软件重组工具包及其C++11前端可以用于实现这一点;目前还没有现成的。DMS旨在为任意源语言提供自定义工具构造,并包含完整的解析器、名称解析器和各种流分析器来支持分析,以及基于分析结果对代码应用源到源转换的能力。

通常,您需要一个静态分析来确定是否使用了每个计算(结果,可能有几个,只考虑"x++")。对于每个未使用的计算,实际上您希望删除未使用的运算,然后重复分析。出于效率的原因,您希望进行一次分析,只确定结果的所有使用点;这本质上是一个数据流分析。当计算结果的使用集为空时,该计算结果可以被删除(请注意,删除"x++"值结果可能会留下"x++",因为仍然需要增量!),并且它所依赖的计算的使用集可以调整为从已删除的计算中删除引用,这可能会导致更多的删除。

要对任何语言进行此分析,您必须能够跟踪结果。对于C(和C++)来说,这可能非常丑陋;有一些"明显"的用途,其中计算结果被用于表达式,被分配给局部/全局变量(在其他地方使用),还有通过指针、对象字段更新、任意强制转换等间接分配。要知道这些影响,你的死代码分析工具必须能够读取整个软件系统,并计算其数据流。

为了安全起见,您希望分析是保守的,例如,如果工具没有证据表明没有使用结果,那么它必须假设使用了结果;您通常必须使用指针(或数组索引,它们只是伪装的指针)来执行此操作,因为通常您无法准确确定指针"指向"的位置。通过假设使用了所有结果,显然可以构建一个"安全"的工具:-}对于您没有源代码的库例程,您有时也会得到非常保守但必要的假设。在这种情况下,预先计算一组库副作用的摘要是很有帮助的(例如,"strcmp"没有,"sprintf"覆盖特定的操作数,"push_back"修改其对象…)。由于库可能很大,所以这个列表可能很大。

DMS通常可以解析和完整的源代码库,构建符号表(这样它就知道哪些标识符是本地/全局标识符及其精确类型),进行控制和本地数据流分析,为每个函数构建本地"副作用"摘要,构建调用图和全局副作用,并进行全局点分析,为这些"计算使用的"信息提供适当的保守性。

DMS已经被用于在2600万行代码的C代码系统上进行这种计算(是的,这是一个非常大的计算;需要100Gb的VM才能运行)。我们没有实现死代码消除部分(该项目还有另一个目的),但一旦有了这些数据,这就很简单了。DMS对大型Java代码进行了更为保守的分析(例如,"没有提及标识符",这意味着对标识符的分配是无效的),这在许多实际代码中导致了惊人数量的代码删除。

DMS的C++解析器目前构建符号表,并可以在C++11近在咫尺的情况下为C++98进行控制流分析。我们仍然需要本地数据流分析,这是一项努力,但全局分析已经预先存在于DMS中,并且可以用于此效果。(如果你不介意更保守的分析,"不使用标识符"很容易从符号表数据中获得)。

在实践中,你不希望这个工具只是默默地把东西撕出来;有些可能实际上是您无论如何都希望保留的计算。Java工具所做的是产生两个结果:一个是死计算列表,您可以检查它以确定您是否相信它,另一个是源代码的死代码删除版本。如果你相信死代码报告,你会保留死代码删除版本;如果您看到一个您认为不应该死的"死"计算,您可以修改代码使其不死,然后再次运行该工具。有了大的代码库,检查死代码报告本身可能是一种尝试;"你"如何知道一些明显的死代码是否不被你团队中的"其他人"重视?。(如果你犯了错,版本控制可以用来恢复!)

一个我们不知道(我也不知道)的非常棘手的问题是,在条件编译的情况下,"死代码"。(Java没有这个问题;C完全有,C++系统就更少了)。这可能真的很糟糕。想象一个条件,其中arm有某些副作用,而另一个arm有不同的副作用,或者另一种情况,其中一个由GCC的C++编译器解释,另一个由MS解释,编译器在结构的作用上存在分歧(是的,C++编译器在黑暗的角落里确实存在分歧)。在这里,我们充其量也可以非常保守。

CLANG具有一定的流量分析能力;以及一些进行源代码转换的能力,所以它可能会被迫这样做。我不知道它是否可以进行任何全局流/点分析。它似乎偏向于单一汇编单元,因为它的主要用途是汇编单一汇编单元。

要捕获未使用的变量,可以在gcc编译器上启用-Wunused标志。这将在编译时警告您未使用的参数、变量和计算值。我发现,使用-Wall-Wextra和-Werror标志可以确保编译器捕捉到您所描述的一些问题。更多信息可以在这里找到:http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

至于查找未使用的类,一种选择是使用IDE,比如Eclipse,并使用"查找引用"功能来搜索可能使用该类/对象的位置。

简短的回答是"否"。通过对客户端代码的静态分析,无法判断vector的push_back方法没有任何重要的副作用。据分析工具所知,它会写入某个地方的数据库,并驱动股票交易。

我建议使用版本控制软件-SVN、Git、Mercurial、Perforce…-以便在调试之后,您可以使用所述版本控制工具来查找和删除调试遗留问题。这使得保持代码更加精简变得非常容易。

此外,这类测试代码通常测试覆盖率很低,所以如果您确实进行了单元测试,它们应该显示为未覆盖的代码。

还有一些工具可以明确地寻找这类东西——Lint、Coverity等等。不过大多数都是商业性的。同样尝试在GCC上使用-O3,编译器可能会以这种方式识别更多实际未使用的变量,因为它会更积极地内联和消除代码。