Автоматически помечает неиспользуемые элементы структуры в C

#c #clang #abstract-syntax-tree #static-analysis

#c #clang #абстрактное синтаксическое дерево #статический анализ

Вопрос:

Мне нужен инструмент для автоматического пометки неиспользуемых элементов структуры в кодовой базе C. Мое определение «неиспользуемого» простое — если определение элемента структуры удалено из кода, и код успешно компилируется, то элемент структуры объявляется неиспользуемым. Вопрос в том, как это можно сделать автоматическим способом? (скорость не вызывает особого беспокойства, поскольку кодовая база невелика).

Существующие статьи stack overflow на эту тему, похоже, намекают на то, что сегодня не существует инструмента статического анализа, который мог бы это сделать. С другой стороны, учитывая модульность Clang, я чувствую, что это должно быть выполнимо с помощью манипуляций AST. Возьмем, к примеру, один файл. Что я хотел бы сделать, так это следующее (позже это можно обобщить на набор исходных файлов в кодовой базе):

  1. Генерируйте AST из кода C.
  2. Рекурсивно просматривайте все определения полей структуры и удаляйте их по одному. Мы можем сохранить «просмотренный» словарь, чтобы убедиться, что мы не удаляем уже просмотренные узлы определения поля.
    • Отфильтровывайте определения полей только для тех, которые присутствуют в анализируемой кодовой базе (например, чтобы избежать определений в стандартных библиотеках).
  3. Скомпилируйте код.
  4. Если код компилируется успешно, то соответствующее объявление поля не используется и помечается.
  5. Перейдите к # 1.

Ключевое слово выше — удалить. Как я могу удалить определение поля? Кажется, есть два способа использования Clang.

  1. На уровне исходного кода мы можем удалить объявление поля с помощью Clang Rewriter (есть опция «RemoveText (SourceRange)»). Но я не знаю, будет ли это работать все время (например: поскольку структуры автоматически генерируются с использованием расширения макросов).
  2. Удалите узел объявления поля из AST, а затем «перекомпилируйте» AST (что бы это ни значило).

Среди двух приведенных выше вариантов # 1 кажется хакерским — вам нужно будет создать копию исходного файла, переписать его после удаления определения поля, а затем повторно скомпилировать измененный исходный код. И я не уверен, насколько хорошо это будет работать, когда для генерации определений полей структуры задействованы сложные МАКРОСЫ.

# 2 кажется чистым, но из поиска в Google, похоже, нет такой вещи, как «удаление узла AST» (он неизменяем). Пожалуйста, поправьте меня, если я ошибаюсь. Даже если я преуспею в этом, как я продолжу с этого момента, чтобы переоценить AST для отсутствующих ссылок на поля структуры? (этап «компиляции»).

Любые предложения приветствуются (заранее спасибо!). У меня уже есть некоторый первоначальный успех с подходом № 1, описанным выше, но я чувствую, что это неправильное направление.

Комментарии:

1. Может быть неверно для вашего кода, но в целом элементы структуры без ссылок не так уж редки для целей выравнивания. Например. возможно, вы считываете данные из файла в структуру с элементами, которые есть в файле, но о которых ваше приложение не заботится.

2. Прокомментируйте их и попробуйте перекомпилировать?

3. Вы могли бы написать свой плагин GCC для этого. Весной 2021 года Bismon может быть полезен

4. @500-InternalServerError Да, я ожидаю , что будут ложные срабатывания, и кому-то нужно будет разобраться в них и обработать те, которые кажутся подлинными — просто нужен способ автоматизировать этот процесс их пометки.

5. @wildplasser — я бы хотел автоматизировать это. Выполнение этого вручную потребует слишком много усилий и времени.

Ответ №1:

cppcheck может это сделать. Например:

 // test.cpp
struct Struct
{
    int used;
    int unused;
};

int main()
{
    Struct s;
    s.used = 0;
    return s.used;
}
 
 $ cppcheck test.cpp --enable=all
Checking test.cpp ...
test.cpp:5:9: style: struct member 'Struct::unused' is never used. [unusedStructMember]
    int unused;
        ^
 

Хотя я использовал код C в примере, он ведет себя так же для C.

Комментарии:

1. Спасибо за это предложение! Я попробую это сделать.