代码之家  ›  专栏  ›  技术社区  ›  Nick Bastin

查找未使用的包含邮件头的工具?[关闭]

  •  70
  • Nick Bastin  · 技术社区  · 15 年前

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

    我们有一个庞大的代码库,在过去的15年中,我们看到了大量的功能性转移,但很少有剩余的include指令在功能性从一个实现文件转移到另一个实现文件时被删除,这使得我们在这一点上陷入了相当混乱的境地。很明显,我可以通过删除所有include指令并让编译器告诉我要重新包含哪些指令来完成这项艰巨的任务,但我宁愿反过来解决这个问题—找到未使用的指令—而不是重新构建已使用指令的列表。

    9 回复  |  直到 10 年前
        1
  •  29
  •   Richard Corden    15 年前

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

    如果大多数(如果不是全部)静态分析工具没有进行某种形式的头使用检查,我会感到惊讶。你可以使用 this 维基百科页面获取可用工具的列表,然后通过电子邮件向公司询问。

    在评估工具时,您可能会考虑以下几点:

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

    // 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
    }
    

    如果采用蛮力方法,首先删除所有头文件,然后重新添加,直到编译;如果先添加“f1.h”,则代码将编译,但程序的语义已更改。

    类似的规则也适用于部分专业化的情况。无论是否选择了专门化,您都需要确保所有专门化都是可见的:

    // 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)'
    }
    

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

    另一种相关的分析类型是检查是否可以向前声明类型。考虑以下事项:

    // 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);
    }
    

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

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

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

        2
  •  22
  •   Andy C    13 年前

    下面是一个脚本:

    #!/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
  •   Ton van den Heuvel    13 年前

    看一看 Dehydra .

    从网站:

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

    应该可以编写一个脚本来检查未使用的包含文件。

        4
  •  4
  •   Chance    10 年前

    谷歌 cppclean 似乎找到未使用的头文件做得不错。我刚开始用它。它会产生一些误报。它通常会在头文件中发现不必要的include,但它不会告诉您需要关联类的正向声明,并且include需要移动到关联的源文件。

        5
  •  3
  •   Mirko Stocker    13 年前

    如果您使用EclipseCDT,您可以尝试 Includator 对于beta测试人员(在撰写本文时),它是免费的,并自动删除多余的内容,包括或添加缺少的内容。

    免责声明:我为开发includer的公司工作,在过去的几个月里一直在使用它。它对我来说非常有效,所以请试一试:—)

        6
  •  1
  •   Cinder6    15 年前

    据我所知,没有一个(那不是pc lint),这是一个羞耻和令人惊讶的。我已经看到了执行这一点伪代码的建议(它基本上使您的“艰苦过程”自动化了):

    对于每个cpp文件
    对于每个标题,包括
    注释出包含内容
    编译cpp文件
    if(编译错误)
    取消对标题的注释
    其他的
    从CPP中删除标题包含

    把它放在一个夜间cron中,它应该完成这项工作,使projset没有未使用的头文件(显然,您可以手动运行它,但执行起来需要很长时间)。唯一的问题是,不包含头不会生成错误,但仍会生成代码。

        7
  •  1
  •   quamrana    15 年前

    我已经手动完成了这项工作,它在短期内是值得的(哦,这是长期的吗?-由于编译时间缩短,这需要很长的时间:

    1. 为每个cpp文件解析的头更少。
    2. 减少依赖性-整个世界不需要在 更改为一个标题。

    它也是一个递归的过程-保留在其中的每个头文件都需要检查是否有头文件 包括可以删除。此外,有时还可以将前向声明替换为头包含。

    然后,整个过程需要每隔几个月/年重复一次,以保持在剩余邮件头之上。

    事实上,我对C++编译器有点恼火,他们应该能够告诉你什么是不需要的——微软编译器可以告诉你在编译过程中,头文件的更改何时可以被安全地忽略。

        8
  •  0
  •   Arctica    11 年前

    如果有人感兴趣,我只是在SooCyFrand上安装了一个小Java COMAND LIN工具来完成这项工作。 正如它是用Java编写的,它在Linux上显然是可运行的。

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

        9
  •  -1
  •   John Hainsworth    11 年前

    如果首先确保每个头文件都是独立编译的,那么删除未使用的头文件的大多数方法都会有更好的效果。我很快就做到了(为打字错误道歉——我在家里打字:

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

    哪里 makeIncluder.sh 包含:

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

    对于每个文件 ./subdir/classname.h ,此方法创建一个名为 ./subdir/classname.h.cpp 包含行

    #include "./subdir/classname.h"
    

    如果你 makefile 在…目录编译所有cpp文件并包含 -I. 然后,只需重新编译就可以测试每个包含文件是否可以自己编译。在您最喜欢的IDE中编译goto错误,并修复错误。

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