工具找到包含的未使用的头?

时间:2021-08-18 15:05:48

I know PC-Lint can tell you about headers which are included but not used. Are there any other tools that can do this, preferably on linux?

我知道PC-Lint可以告诉你包含但不使用的标题。有没有其他工具可以做到这一点,最好是在linux上?

We have a large codebase that through the last 15 years has seen plenty of functionality move around, but rarely do the leftover #include directives get removed when functionality moves from one implementation file to another, leaving us with a pretty good mess by this point. I can obviously do the painstaking thing of removing all the #include directives and letting the compiler tell me which ones to reinclude, but I'd rather solve the problem in reverse - find the unused ones - rather than rebuilding a list of used ones.

我们有一个很大的代码基,在过去的15年里,我们已经看到了大量的功能在移动,但是当功能从一个实现文件移动到另一个实现文件时,很少会删除剩下的#include指令,这给我们带来了相当大的麻烦。显然,我可以做一件艰苦的事情,删除所有#include指令,让编译器告诉我应该重新包含哪些指令,但我宁愿反过来解决问题——找到未使用的指令——而不是重新构建一个已使用的指令列表。

9 个解决方案

#1


28  

DISCLAIMER: My day job is working for a company that develops static analysis tools.

免责声明:我的日常工作是为一家开发静态分析工具的公司工作。

I would be surprised if most (if not all) static analysis tools did not have some form of header usage check. You could use this wikipedia page to get a list of available tools and then email the companies to ask them.

如果大多数静态分析工具(如果不是全部的话)没有某种形式的头使用检查,我会感到惊讶。你可以使用这个*页面获取可用工具的列表,然后给公司发邮件询问它们。

Some points you might consider when you're evaluating a tool:

当你评估一个工具时,你可能会考虑以下几点:

For function overloads, you want all headers containing overloads to be visible, not just the header that contains the function that was selected by overload resolution:

对于函数重载,您希望所有包含重载的header都是可见的,而不仅仅是包含由重载解析所选择的函数的头。

// f1.h
void foo (char);

// f2.h
void foo (int);


// bar.cc
#include "f1.h"
#include "f2.h"

int main ()
{
  foo (0);  // Calls 'foo(int)' but all functions were in overload set
}

If you take the brute force approach, first remove all headers and then re-add them until it compiles, if 'f1.h' is added first then the code will compile but the semantics of the program have been changed.

如果采用蛮力方法,首先删除所有头文件,然后重新添加它们,直到它编译,If 'f1。首先添加h',然后编译代码,但是程序的语义已经改变。

A similar rule applies when you have partial and specializations. It doesn't matter if the specialization is selected or not, you need to make sure that all specializations are visible:

当您具有部分和专门化时,也适用类似的规则。是否选择专门化并不重要,您需要确保所有专门化都是可见的:

// f1.h
template <typename T>
void foo (T);

// f2.h
template <>
void foo (int);

// bar.cc
#include "f1.h"
#include "f2.h"


int main ()
{
  foo (0);  // Calls specialization 'foo<int>(int)'
}

As for the overload example, the brute force approach may result in a program which still compiles but has different behaviour.

对于重载示例,蛮力方法可能会导致一个仍在编译但有不同行为的程序。

Another related type of analysis that you can look out for is checking if types can be forward declared. Consider the following:

另一种相关的分析类型是检查类型是否可以被转发。考虑以下:

// A.h
class A { };

// foo.h
#include "A.h"
void foo (A const &);

// bar.cc
#include "foo.h"

void bar (A const & a)
{
  foo (a);
}

In the above example, the definition of 'A' is not required, and so the header file 'foo.h' can be changed so that it has a forward declaration only for 'A':

在上面的示例中,不需要定义“A”,因此头文件“foo”。h'可以更改为只对a有正向声明:

// foo.h
class A;
void foo (A const &);

This kind of check also reduces header dependencies.

这种检查也减少了头的依赖性。

#2


22  

Here's a script that does it:

这里有一个剧本:

#!/bin/bash
# prune include files one at a time, recompile, and put them back if it doesn't compile
# arguments are list of files to check
removeinclude() {
    file=$1
    header=$2
    perl -i -p -e 's+([ \t]*#include[ \t][ \t]*[\"\<]'$2'[\"\>])+//REMOVEINCLUDE $1+' $1
}
replaceinclude() {
   file=$1
   perl -i -p -e 's+//REMOVEINCLUDE ++' $1
}

for file in $*
do
    includes=`grep "^[ \t]*#include" $file | awk '{print $2;}' | sed 's/[\"\<\>]//g'`
    echo $includes
    for i in $includes
    do
        touch $file # just to be sure it recompiles
        removeinclude $file $i
        if make -j10 >/dev/null  2>&1;
        then
            grep -v REMOVEINCLUDE $file > tmp && mv tmp $file
            echo removed $i from $file
        else
            replaceinclude $file
            echo $i was needed in $file
        fi
    done
done

#3


5  

Have a look at Dehydra.

看一下脱水症。

From the website:

从网站:

Dehydra is a lightweight, scriptable, general purpose static analysis tool capable of application-specific analyses of C++ code. In the simplest sense, Dehydra can be thought of as a semantic grep tool.

脱水是一种轻量级、可脚本化、通用的静态分析工具,能够对c++代码进行特定于应用程序的分析。在最简单的意义上,脱水可以被认为是一种语义grep工具。

It should be possible to come up with a script that checks for unused #include files.

应该可以提出一个脚本,检查未使用的#include文件。

#4


4  

Google's cppclean seems to do a decent job of finding unused header files. I just started using it. It produces a few false positives. It will often find unnecessary includes in header files, but what it will not tell you is that you need a forward declaration of the associated class, and the include needs to be moved to the associated source file.

谷歌的cppclean似乎能够找到未使用的头文件。我刚开始用它。它会产生一些误报。它通常会在头文件中发现不必要的内容,但是它不会告诉您的是您需要一个相关类的forward声明,并且include需要被转移到相关的源文件中。

#5


3  

If you are using Eclipse CDT you can try Includator which is free for beta testers (at the time of this writing) and automatically removes superfluous #includes or adds missing ones.

如果您正在使用Eclipse CDT,您可以尝试Includator,它对beta测试人员是免费的(在撰写本文时),并自动删除多余的#include或添加缺失的。

Disclaimer: I work for the company that develops Includator and have been using it for the past few months. It works quite well for me, so give it a try :-)

免责声明:在过去的几个月里,我一直在为公司工作,并一直在使用它。它对我很管用,所以试一试。

#6


1  

As far as I know, there isn't one (that isn't PC-Lint), which is a shame, and surprising. I've seen the suggestion to do this bit of pseudocode (which is basically automating your "painstaking process":

据我所知,没有一个(不是PC-Lint)是令人羞愧和惊讶的。我看到了做这一小段伪代码的建议(它基本上是自动化您的“艰苦的过程”):

for every cpp file
for every header include
comment out the include
compile the cpp file
if( compile_errors )
un-comment out the header
else
remove header include from cpp

对于每个页眉的每个cpp文件,包括注释,包括编译cpp文件,如果(compile_errors)未注释掉标题,则从cpp中删除header。

Put that in a nightly cron, and it should do the job, keeping the projcet in question free of unused headers (you can always run it manually, obviously, but it'll take a long time to execute). Only problem is when not including a header doesn't generate an error, but still produces code.

把它放在一个夜间的cron中,它应该做这个工作,将projcet保存在没有使用的头的问题中(显然,您可以手动运行它,但它需要很长时间才能执行)。唯一的问题是,当不包括header时,不会产生错误,但是仍然会产生代码。

#7


1  

I've done this manually and its worth it in the short (Oh, is it the long term? - It takes a long time) term due to reduced compile time:

我已经手工做过了,短期内是值得的(哦,是长期的吗?)-由于编译时间缩短,需要很长的时间:

  1. Less headers to parse for each cpp file.
  2. 为每个cpp文件解析的头更少。
  3. Less dependencies - the whole world doesn't need re-compiling after a change to one header.
  4. 更少的依赖关系—整个世界不需要在更改为一个头之后重新编译。

Its also a recursive process - each header file that stays in needs examining to see if any header files it includes can be removed. Plus sometimes you can substitute forward declarations for header includes.

它也是一个递归过程——每个头文件都需要检查,看看它包含的头文件是否可以删除。此外,有时还可以用前面的声明替换头文件。

Then the whole process needs repeating every few months/year to keep on top of leftover headers.

然后整个过程需要每隔几个月或一年重复一次,以保持在剩余的页眉之上。

Actually, I'm a bit annoyed with C++ compilers, they should be able to tell you what's not needed - the Microsoft compiler can tell you when a change to a header file can be safely ignored during compilation.

实际上,我对c++编译器有点不满,它们应该能够告诉您什么是不需要的——Microsoft编译器可以告诉您何时可以在编译期间安全地忽略对头文件的更改。

#8


0  

If someone is interested, I just putted on sourceforge a small Java comand-line tool for doing exactly that. As it is written in Java, it is obviously runable on linux.

如果有人感兴趣,我只是在sourceforge上创建了一个小的Java comand-line工具来实现这一点。因为它是用Java编写的,所以显然可以在linux上运行。

The link for the project is https://sourceforge.net/projects/chksem/files/chksem-1.0/

项目的链接是https://sourceforge.net/projects/chksem/files/chksam -1.0/

#9


-1  

Most approaches for removing unused includes work better if you first make sure that each your header files compiles on its own. I did this relatively quickly as follows (apologies for typos -- I am typing this at home:

如果您首先确保每个头文件都独立地编译,那么大多数删除未使用的方法都可以更好地工作。我做得比较快,如下所示(为拼写错误道歉——我在家里输入这个:

find . -name '*.h' -exec makeIncluder.sh {} \;

where makeIncluder.sh contains:

makeIncluder的地方。sh包含:

#!/bin/sh
echo "#include \"$1\"" > $1.cpp

For each file ./subdir/classname.h, this approach creates a file called ./subdir/classname.h.cpp containing the line

对于每个文件,/子目录名称。h,这种方法创建一个名为./subdir/classname.h的文件。cpp包含线

#include "./subdir/classname.h"

If your makefile in the . directory compiles all cpp files and contains -I. , then just recompiling will test that every include file can compile on its own. Compile in your favorite IDE with goto-error, and fix the errors.

如果你的makefile在。目录编译所有cpp文件并包含-I。,然后重新编译将测试每个include文件都可以自己编译。使用goto-error在您喜欢的IDE中编译,并修复错误。

When you're done, find . -name '*.h.cpp' -exec rm {} \;

完成后,找到。- name ' * . h。cpp' -exec rm {} \;

#1


28  

DISCLAIMER: My day job is working for a company that develops static analysis tools.

免责声明:我的日常工作是为一家开发静态分析工具的公司工作。

I would be surprised if most (if not all) static analysis tools did not have some form of header usage check. You could use this wikipedia page to get a list of available tools and then email the companies to ask them.

如果大多数静态分析工具(如果不是全部的话)没有某种形式的头使用检查,我会感到惊讶。你可以使用这个*页面获取可用工具的列表,然后给公司发邮件询问它们。

Some points you might consider when you're evaluating a tool:

当你评估一个工具时,你可能会考虑以下几点:

For function overloads, you want all headers containing overloads to be visible, not just the header that contains the function that was selected by overload resolution:

对于函数重载,您希望所有包含重载的header都是可见的,而不仅仅是包含由重载解析所选择的函数的头。

// f1.h
void foo (char);

// f2.h
void foo (int);


// bar.cc
#include "f1.h"
#include "f2.h"

int main ()
{
  foo (0);  // Calls 'foo(int)' but all functions were in overload set
}

If you take the brute force approach, first remove all headers and then re-add them until it compiles, if 'f1.h' is added first then the code will compile but the semantics of the program have been changed.

如果采用蛮力方法,首先删除所有头文件,然后重新添加它们,直到它编译,If 'f1。首先添加h',然后编译代码,但是程序的语义已经改变。

A similar rule applies when you have partial and specializations. It doesn't matter if the specialization is selected or not, you need to make sure that all specializations are visible:

当您具有部分和专门化时,也适用类似的规则。是否选择专门化并不重要,您需要确保所有专门化都是可见的:

// f1.h
template <typename T>
void foo (T);

// f2.h
template <>
void foo (int);

// bar.cc
#include "f1.h"
#include "f2.h"


int main ()
{
  foo (0);  // Calls specialization 'foo<int>(int)'
}

As for the overload example, the brute force approach may result in a program which still compiles but has different behaviour.

对于重载示例,蛮力方法可能会导致一个仍在编译但有不同行为的程序。

Another related type of analysis that you can look out for is checking if types can be forward declared. Consider the following:

另一种相关的分析类型是检查类型是否可以被转发。考虑以下:

// A.h
class A { };

// foo.h
#include "A.h"
void foo (A const &);

// bar.cc
#include "foo.h"

void bar (A const & a)
{
  foo (a);
}

In the above example, the definition of 'A' is not required, and so the header file 'foo.h' can be changed so that it has a forward declaration only for 'A':

在上面的示例中,不需要定义“A”,因此头文件“foo”。h'可以更改为只对a有正向声明:

// foo.h
class A;
void foo (A const &);

This kind of check also reduces header dependencies.

这种检查也减少了头的依赖性。

#2


22  

Here's a script that does it:

这里有一个剧本:

#!/bin/bash
# prune include files one at a time, recompile, and put them back if it doesn't compile
# arguments are list of files to check
removeinclude() {
    file=$1
    header=$2
    perl -i -p -e 's+([ \t]*#include[ \t][ \t]*[\"\<]'$2'[\"\>])+//REMOVEINCLUDE $1+' $1
}
replaceinclude() {
   file=$1
   perl -i -p -e 's+//REMOVEINCLUDE ++' $1
}

for file in $*
do
    includes=`grep "^[ \t]*#include" $file | awk '{print $2;}' | sed 's/[\"\<\>]//g'`
    echo $includes
    for i in $includes
    do
        touch $file # just to be sure it recompiles
        removeinclude $file $i
        if make -j10 >/dev/null  2>&1;
        then
            grep -v REMOVEINCLUDE $file > tmp && mv tmp $file
            echo removed $i from $file
        else
            replaceinclude $file
            echo $i was needed in $file
        fi
    done
done

#3


5  

Have a look at Dehydra.

看一下脱水症。

From the website:

从网站:

Dehydra is a lightweight, scriptable, general purpose static analysis tool capable of application-specific analyses of C++ code. In the simplest sense, Dehydra can be thought of as a semantic grep tool.

脱水是一种轻量级、可脚本化、通用的静态分析工具,能够对c++代码进行特定于应用程序的分析。在最简单的意义上,脱水可以被认为是一种语义grep工具。

It should be possible to come up with a script that checks for unused #include files.

应该可以提出一个脚本,检查未使用的#include文件。

#4


4  

Google's cppclean seems to do a decent job of finding unused header files. I just started using it. It produces a few false positives. It will often find unnecessary includes in header files, but what it will not tell you is that you need a forward declaration of the associated class, and the include needs to be moved to the associated source file.

谷歌的cppclean似乎能够找到未使用的头文件。我刚开始用它。它会产生一些误报。它通常会在头文件中发现不必要的内容,但是它不会告诉您的是您需要一个相关类的forward声明,并且include需要被转移到相关的源文件中。

#5


3  

If you are using Eclipse CDT you can try Includator which is free for beta testers (at the time of this writing) and automatically removes superfluous #includes or adds missing ones.

如果您正在使用Eclipse CDT,您可以尝试Includator,它对beta测试人员是免费的(在撰写本文时),并自动删除多余的#include或添加缺失的。

Disclaimer: I work for the company that develops Includator and have been using it for the past few months. It works quite well for me, so give it a try :-)

免责声明:在过去的几个月里,我一直在为公司工作,并一直在使用它。它对我很管用,所以试一试。

#6


1  

As far as I know, there isn't one (that isn't PC-Lint), which is a shame, and surprising. I've seen the suggestion to do this bit of pseudocode (which is basically automating your "painstaking process":

据我所知,没有一个(不是PC-Lint)是令人羞愧和惊讶的。我看到了做这一小段伪代码的建议(它基本上是自动化您的“艰苦的过程”):

for every cpp file
for every header include
comment out the include
compile the cpp file
if( compile_errors )
un-comment out the header
else
remove header include from cpp

对于每个页眉的每个cpp文件,包括注释,包括编译cpp文件,如果(compile_errors)未注释掉标题,则从cpp中删除header。

Put that in a nightly cron, and it should do the job, keeping the projcet in question free of unused headers (you can always run it manually, obviously, but it'll take a long time to execute). Only problem is when not including a header doesn't generate an error, but still produces code.

把它放在一个夜间的cron中,它应该做这个工作,将projcet保存在没有使用的头的问题中(显然,您可以手动运行它,但它需要很长时间才能执行)。唯一的问题是,当不包括header时,不会产生错误,但是仍然会产生代码。

#7


1  

I've done this manually and its worth it in the short (Oh, is it the long term? - It takes a long time) term due to reduced compile time:

我已经手工做过了,短期内是值得的(哦,是长期的吗?)-由于编译时间缩短,需要很长的时间:

  1. Less headers to parse for each cpp file.
  2. 为每个cpp文件解析的头更少。
  3. Less dependencies - the whole world doesn't need re-compiling after a change to one header.
  4. 更少的依赖关系—整个世界不需要在更改为一个头之后重新编译。

Its also a recursive process - each header file that stays in needs examining to see if any header files it includes can be removed. Plus sometimes you can substitute forward declarations for header includes.

它也是一个递归过程——每个头文件都需要检查,看看它包含的头文件是否可以删除。此外,有时还可以用前面的声明替换头文件。

Then the whole process needs repeating every few months/year to keep on top of leftover headers.

然后整个过程需要每隔几个月或一年重复一次,以保持在剩余的页眉之上。

Actually, I'm a bit annoyed with C++ compilers, they should be able to tell you what's not needed - the Microsoft compiler can tell you when a change to a header file can be safely ignored during compilation.

实际上,我对c++编译器有点不满,它们应该能够告诉您什么是不需要的——Microsoft编译器可以告诉您何时可以在编译期间安全地忽略对头文件的更改。

#8


0  

If someone is interested, I just putted on sourceforge a small Java comand-line tool for doing exactly that. As it is written in Java, it is obviously runable on linux.

如果有人感兴趣,我只是在sourceforge上创建了一个小的Java comand-line工具来实现这一点。因为它是用Java编写的,所以显然可以在linux上运行。

The link for the project is https://sourceforge.net/projects/chksem/files/chksem-1.0/

项目的链接是https://sourceforge.net/projects/chksem/files/chksam -1.0/

#9


-1  

Most approaches for removing unused includes work better if you first make sure that each your header files compiles on its own. I did this relatively quickly as follows (apologies for typos -- I am typing this at home:

如果您首先确保每个头文件都独立地编译,那么大多数删除未使用的方法都可以更好地工作。我做得比较快,如下所示(为拼写错误道歉——我在家里输入这个:

find . -name '*.h' -exec makeIncluder.sh {} \;

where makeIncluder.sh contains:

makeIncluder的地方。sh包含:

#!/bin/sh
echo "#include \"$1\"" > $1.cpp

For each file ./subdir/classname.h, this approach creates a file called ./subdir/classname.h.cpp containing the line

对于每个文件,/子目录名称。h,这种方法创建一个名为./subdir/classname.h的文件。cpp包含线

#include "./subdir/classname.h"

If your makefile in the . directory compiles all cpp files and contains -I. , then just recompiling will test that every include file can compile on its own. Compile in your favorite IDE with goto-error, and fix the errors.

如果你的makefile在。目录编译所有cpp文件并包含-I。,然后重新编译将测试每个include文件都可以自己编译。使用goto-error在您喜欢的IDE中编译,并修复错误。

When you're done, find . -name '*.h.cpp' -exec rm {} \;

完成后,找到。- name ' * . h。cpp' -exec rm {} \;