Инструменты для поиска включенных заголовков, которые не используются?

Я знаю PC-Lint может рассказать вам о заголовках, которые включены, но не используются. Есть ли другие инструменты, которые могут это сделать, желательно на linux?

У нас есть большая база кода, которая за последние 15 лет обнаружила, что много функциональности перемещается, но редко делают оставшиеся директивы #include удаляются, когда функциональность перемещается из одного файла реализации в другой, оставляя нас с довольно хорошим беспорядком эта точка. Я, очевидно, могу сделать кропотливую попытку удалить все директивы #include и позволить компилятору рассказать мне, какие из них нужно повторно включить, но я бы скорее решил проблему в обратном порядке - найти неиспользуемые, а не перестроить список используемых.

Ответ 1

ОТКАЗ ОТ ОТВЕТСТВЕННОСТИ: Моя дневная работа работает в компании, которая разрабатывает инструменты статического анализа.

Я был бы удивлен, если бы большинство (если не всех) инструментов статического анализа не имели какой-либо формы проверки использования заголовка. Вы можете использовать эту страницу wikipedia, чтобы получить список доступных инструментов, а затем отправить по электронной почте компании, чтобы спросить их.

Некоторые моменты, которые вы можете рассмотреть при оценке инструмента:

Для перегрузок функций вы хотите, чтобы все заголовки, содержащие перегрузки, были видимыми, а не только заголовок, содержащий функцию, которая была выбрана с помощью разрешения перегрузки:

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

Здесь script, который делает это:

#!/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

Посмотрите Dehydra.

С веб-сайта:

Dehydra - это простой, статический аналитический инструмент общего назначения, предназначенный для анализа кода С++, специфичного для приложения. В самом простом смысле Дегидра можно рассматривать как семантический инструмент grep.

Должно быть возможно создать script, который проверяет неиспользуемые файлы #include.

Ответ 4

Google cppclean, похоже, делает достойную работу по поиску неиспользуемых файлов заголовков. Я только начал использовать его. Он создает несколько ложных срабатываний. Он часто находит ненужные включения в заголовочные файлы, но то, что он вам не скажет, - это то, что вам нужно объявление вперед связанного класса, а включение необходимо перенести в связанный исходный файл.

Ответ 5

Если вы используете Eclipse CDT, вы можете попробовать Includator, который является бесплатным для бета-тестеров (на момент написания этой статьи) и автоматически удаляет лишние # включает или добавляет недостающие.

Отказ от ответственности: я работаю в компании, которая разрабатывает Includator и использует ее в течение последних нескольких месяцев. Это хорошо работает для меня, поэтому попробуйте: -)

Ответ 6

Насколько я знаю, нет ни одного (это не PC-Lint), что является позором и удивительным. Я видел предложение сделать этот бит псевдокода (который в основном автоматизирует ваш "кропотливый процесс":

для каждого файла cpp
    для каждого заголовка:         закомментируйте включение         скомпилировать файл cpp
        if (compile_errors)
            не комментировать заголовок
        остальное
            удалить заголовок из cpp

Поместите это в ночной cron, и он должен выполнить эту работу, не допуская, чтобы рассматриваемый projcet был свободен от неиспользуемых заголовков (вы всегда можете запустить его вручную, очевидно, но это займет много времени, чтобы выполнить). Проблема только в том, что не включая заголовок не генерирует ошибку, но все равно производит код.

Ответ 7

Я сделал это вручную, и его стоит в коротком (о, это долгий срок? - Это занимает много времени) из-за сокращения времени компиляции:

  • Меньше заголовков для разбора для каждого файла cpp.
  • Меньше зависимостей - весь мир не нуждается в повторной компиляции после изменить на один заголовок.

Его также рекурсивный процесс - каждый заголовочный файл, который остается в поиске, чтобы посмотреть, могут ли быть удалены любые файлы заголовков . Плюс иногда вы можете подставить форвардные объявления для заголовка.

Затем весь процесс нужно повторять каждые несколько месяцев/год, чтобы сохранить верхние части оставшихся заголовков.

На самом деле, меня немного раздражает компилятор С++, они должны быть в состоянии сказать вам, что не нужно - компилятор Microsoft может сказать вам, когда во время компиляции можно безопасно игнорировать изменение файла заголовка.

Ответ 8

Если кому-то интересно, я просто положил на sourceforge небольшой инструмент Java comand-line для выполнения именно этого. Поскольку он написан на Java, он явно работает на linux.

Ссылка для проекта https://sourceforge.net/projects/chksem/files/chksem-1.0/

Ответ 9

Большинство подходов к удалению неиспользуемых функций работают лучше, если вы сначала убедитесь, что каждый файл заголовка компилируется сам по себе. Я сделал это относительно быстро следующим образом (извинения за опечатки - я печатаю это дома:

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 {} \;