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

StackOverflow https://stackoverflow.com/questions/1301850

  •  18-09-2019
  •  | 
  •  

Вопрос

Я знаю ПК-Линт могу рассказать вам о заголовках, которые включены, но не используются.Есть ли какие-нибудь другие инструменты, которые могут это сделать, желательно в Linux?

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

Это было полезно?

Решение

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

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

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

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

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

Этот вид проверки также уменьшает зависимости заголовков.

Другие советы

Вот скрипт, который это делает:

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

Посмотри на Дегидра.

С веб-сайта:

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

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

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

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

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

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

для каждого файла cpp
для каждого заголовка включить
закомментируйте включение
скомпилировать файл cpp
если (компиляция_ошибки)
раскомментируйте заголовок
еще
удалить включение заголовка из cpp

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

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

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

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

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

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

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

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

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

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-error и исправьте ошибки.

Когда вы закончите, find . -name '*.h.cpp' -exec rm {} \;

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top