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

时间:2013-04-05 03:02:00

标签: c++ static-analysis unused-variables

有很多次,当我在调试或重用某些代码时,文件开始获取没有做任何事情的行,尽管他们可能已经在某一点上做了一些事情。

像向量和填充之类的东西,然后是未使用的,已定义但从未使用过的类/结构体,以及声明但从未使用过的函数。

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

虽然我理解从技术上讲,调用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};

4 个答案:

答案 0 :(得分:3)

我们的DMS软件再造工具包及其C ++ 11前端可用于执行此操作;它现在不是现成的。 DMS旨在为任意源语言提供自定义工具构造,并包含完整的解析器,名称解析器和各种流分析器以支持分析,以及基于分析结果对代码应用源到源转换的能力。

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

要对任何语言进行此分析,您必须能够跟踪结果。对于C(和C ++),这可能非常难看;有明显的&#34;使用计算结果在表达式中使用的位置,以及将其分配给局部/全局变量(在其他地方使用)的位置,并通过指针,对象字段更新,通过任意强制转换等进行间接赋值。这些影响,您的死代码分析工具必须能够读取整个软件系统,并计算它的数据流。

为了安全起见,您希望该分析保守,例如,如果该工具没有未使用结果的证据,则必须假设结果已被使用;你经常不得不用指针(或者只是伪装指针的数组索引)来做这件事,因为一般来说你无法精确地确定指针的位置&#34;指向&#34;。人们显然可以建立一个安全的&#34;通过假设使用所有结果的工具: - }对于您没有源的库例程,您最终会得到有时非常保守但必要的假设。在这种情况下,有一组预先计算的库副作用摘要是有帮助的(例如,&#34; strcmp&#34;没有,&#34; sprintf&#34;覆盖特定的操作数,&#34; push_back&#34;修改其对象...)。由于库可能非常大,因此这个列表可能非常大。

DMS通常可以解析整个源代码库,构建符号表(因此它知道哪些标识符是本地/全局及其精确类型),进行控制和本地数据流分析,构建本地&#34;副作用&#34 ;每个函数汇总,构建一个调用图和全局副作用,并进行全局点分析,提供这个&#34;使用的计算&#34;有适当保守主义的信息。

DMS已被用于在2600万行代码的C代码系统上进行此计算(是的,这是一个非常大的计算;它需要运行100Gb VM)。我们没有实现死代码消除部分(项目有另一个目的),但是一旦你有了这些数据,那就很简单了。 DMS已经使用更保守的分析对大型Java代码执行了死代码消除(例如,&#34;没有使用标识符&#34;这意味着标识符的分配已经死亡),这导致了大量的代码删除许多真实的代码。

DMS的C ++解析器目前构建了符号表,可以为C ++ 98控制流分析,C ++ 11就在眼前。我们仍然需要本地数据流分析,这是一些努力,但全局分析已经预先存在于DMS中,可用于此效果。 (如果你不介意更保守的分析,那么很容易从符号表数据中获得&#34;标识符和#34的使用;

在实践中,你不希望这个工具只是默默地撕掉东西;有些可能实际上是你希望保留的计算。 Java工具的作用是产生两个结果:一个死计算列表,您可以检查它以确定您是否相信它,以及一个死代码删除的源代码版本。如果您相信死代码报告,则保留死代码删除版本;如果你看到一个&#34;死了&#34;你认为不应该死的计算,你修改代码使其不死并再次运行该工具。有了很大的代码库,检查死代码报告本身就可以尝试;怎么做&#34;你&#34;知道某些显然死的代码是否被&#34;其他人&#34;在你的团队? (如果你蠢事,版本控制可以用来恢复!)

我们没有(也就是我所知道的工具)处理的一个非常棘手的问题是&#34;死代码&#34;在条件编译的情况下。 (Java没有这个问题; C有它在黑桃,C ++系统更少)。这真的很讨厌。想象一下,一个条件,其中手臂有一定的副作用,另一个手臂有不同的副作用,或另一种情况,其中一个由GCC的C ++编译器解释,另一个手臂由MS解释,编译器不同意构造的作用是什么(是的,C ++编译器在黑暗的角落里不同意)。充其量我们在这里可以非常保守。

CLANG有能力进行流量分析;以及进行源转换的一些能力,因此可能会强制执行此操作。我不知道它是否可以进行任何全局流量/点分析。它似乎偏向于单个编译单元,因为它的主要用途是编译单个编译单元。

答案 1 :(得分:1)

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

对于查找未使用的类,一个选项是使用IDE,比如Eclipse,并使用“查找引用”功能搜索可能使用该类/对象的位置。

答案 2 :(得分:1)

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

答案 3 :(得分:0)

我建议使用版本控制软件 - SVN,Git,Mercurial,Perforce,... - 以便在调试后您可以使用所述版本控制工具查找和删除调试剩余部分。这使得保持代码更加精简变得非常容易。

此外,这种测试代码通常几乎没有测试覆盖率,所以如果你有单元测试,它们应该显示为不包含代码。

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