Удаление ненужных строк из файла c++


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

Такие вещи, как векторы и заполнение, а затем неиспользуемые классы / структуры, которые определены, но никогда не используются, и функции, которые объявлены, но никогда не используются.

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

Итак: есть ли способ сделать это, используя компилятор (clang, gcc, VS и т. д.) или внешний инструмент?

Пример:

#include<vector>
using namespace std;
void test() {
    vector<int> a;
    a.push_back(1);
}
int main() {
    test();
    return 0;
}

Должно стать: int main(){return 0};

4 7

4 ответа:

Для этого может быть использован наш инструментарий реинжиниринга программного обеспечения DMS с его интерфейсом C++11; в настоящее время он не делает этого с полки. DMS предназначена для создания пользовательских инструментов для произвольных исходных языков и содержит полные синтаксические анализаторы, распознаватели имен и различные анализаторы потоков для поддержки анализа, а также возможность применения преобразований от источника к источнику в коде на основе результатов анализа.

В общем случае требуется статический анализ, который определяет, является ли каждое вычисление (результат может быть несколько, рассмотрим только "x++") используется или нет. Для каждого неиспользуемого вычисления фактически требуется удалить неиспользуемое вычисление и повторить анализ. По соображениям эффективности вы хотите сделать анализ, который определяет все (точки) использования результата(ов) только один раз; это по существу анализ потока данных. Когда набор использования результата вычисления становится пустым, этот результат вычисления может быть удален (обратите внимание, что удаление результата значения "x++" может оставьте позади "x++", потому что инкремент все еще необходим!) и наборы вычислений использования, от которых это зависит, могут быть скорректированы, чтобы удалить ссылки из удаленного, возможно, вызывая больше удалений.

Чтобы сделать этот анализ для любого языка, вы должны быть в состоянии отслеживать результаты. Для C (и C++) это может быть довольно некрасиво; есть "очевидные" применения, когда результат вычисления используется в выражении, и где он присваивается локальной / глобальной переменной (которая используется где-то еще), кроме того, существуют косвенные назначения через указатели, обновления полей объектов, произвольные приведения и т. д. Чтобы знать эти эффекты, ваш инструмент анализа мертвого кода должен быть способен считывать всю программную систему и вычислять потоки данных через нее.

Чтобы быть безопасным, вы хотите, чтобы этот анализ был консервативным, например, если инструмент не имеет доказательств того, что результат не используется, то он должен предполагать, что результат используется; Вы часто должны делать это с указателями (или индексами массива, которые просто указатели в маскировке), потому что в общем случае вы не можете точно определить, где указатель "указывает". Очевидно, можно построить "безопасный" инструмент, предположив, что используются все результаты: -} вы также получите иногда очень консервативные, но необходимые предположения для библиотечных подпрограмм, для которых у вас нет источника. В этом случае полезно иметь набор предварительно вычисленных сводок побочных эффектов библиотеки (например, "strcmp" не имеет ни одного, "sprintf" перезаписывает определенный операнд," push_back " изменяет его объект...). Поскольку библиотеки могут быть довольно большими, этот список может быть довольно большим.

DMS в целом может анализировать и всю базу исходного кода, строить таблицы символов (чтобы знать, какие идентификаторы являются локальными/глобальными и их точный тип), выполнять анализ управляющих и локальных потоков данных, строить локальную сводку "побочных эффектов" для каждой функции, строить граф вызовов и глобальные побочные эффекты, а также выполнять глобальный анализ точек, обеспечивая эту "вычислительную используемую" информацию с соответствующим консерватизмом.

DMS была использована для выполнения этих вычислений на системах C-кода из 26 миллионов строк кода (и да, это действительно большое вычисление; для запуска требуется 100 ГБ виртуальной машины). Мы не реализовали часть устранения мертвого кода (у проекта была другая цель), но это просто, как только у вас есть эти данные. DMS сделала исключение мертвого кода на больших кодах Java с более консервативным анализом (например, "нет упоминаний идентификатора", что означает, что назначения идентификатору мертвы) что вызывает удивительное количество удаления кода во многих реальных кодах.

Анализатор C++ DMS в настоящее время строит таблицы символов и может выполнять анализ потока управления для C++98, Когда C++11 находится под рукой. Мы все еще нуждаемся в локальном анализе потоков данных, что требует определенных усилий, но глобальный анализ уже существует в DMS и доступен для использования для этого эффекта. ("Нет использования идентификатора" легко доступно из данных таблицы символов, если вы не возражаете против более консервативного варианта анализ).

На практике вы не хотите, чтобы инструмент просто молча вырывал вещи; некоторые из них могут быть вычислениями, которые вы все равно хотите сохранить. Инструмент Java дает два результата: список мертвых вычислений, который вы можете проверить, чтобы решить, верите ли вы в него, и версию исходного кода, удаленную из мертвого кода. Если вы верите отчету о мертвом коде, вы сохраняете версию с удаленным мертвым кодом; если вы видите" мертвое " вычисление, которое, по вашему мнению, не должно быть мертвым, вы изменяете код чтобы сделать его не мертвым и запустить инструмент снова. С большой базой кода, проверка отчета о мертвом коде сама по себе может быть попыткой; как "вы" знаете, если какой-то кажущийся мертвым код не ценится "кем-то другим" в вашей команде?. (Контроль версий можно использовать для восстановления, если вы дурак!)

Действительно сложная проблема, с которой мы не справляемся (и ни один инструмент, о котором я знаю), - это "мертвый код" при наличии условной компиляции. (Java не имеет этой проблемы; C имеет ее в пиковых количествах, системы C++ гораздо меньше). Это может быть действительно противно. Представьте себе условного в котором рукоятка имеет определенные побочные эффекты и другая рука имеет различные побочные эффекты, или другой случай, в котором интерпретируется с помощью GCC это компилятор C++, а другую руку интерпретируется МС, и составители согласен с тем, что конструкции сделать (да, компиляторы C++ не соглашаются друг с другом в темных углах). В лучшем случае мы можем быть очень консервативны.

У CLANG есть некоторая способность делать анализ потока; и некоторая способность делать преобразования источника, так что это может быть его заставили сделать это. Я не знаю, Может ли он сделать какой-либо глобальный анализ потока/точек. Он, по-видимому, имеет уклон в сторону отдельных единиц компиляции, поскольку его основное использование-компиляция одной единицы компиляции.

Чтобы перехватить неиспользуемые переменные, можно включить флаг-Wunused в компиляторе gcc. Это предупредит вас о неиспользуемых параметрах, переменных и вычисленных значениях во время компиляции. Я обнаружил, что использование флагов-Wall-Wextra и-Werror гарантирует, что компилятор поймает некоторые из описанных вами проблем. Более подробную информацию можно найти здесь: http://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html

Что касается поиска неиспользуемых классов, один из вариантов-использовать IDE, например Eclipse, и использовать функция "найти ссылки" предназначена для поиска мест, где этот класс/объект может быть использован.

Короткий ответ: "нет."С помощью статического анализа клиентского кода невозможно сказать, что метод вектора push_back не имеет каких-либо важных побочных эффектов. Насколько известно аналитическому инструменту, он записывает данные в базу данных и управляет биржевой торговлей.

Я бы рекомендовал использовать программное обеспечение для управления версиями-SVN, Git, Mercurial, Perforce,... - так что после отладки вы можете использовать указанный инструмент управления версиями для поиска и удаления остатков отладки. Это делает его очень легким, чтобы сохранить ваш код более бережливым.

Кроме того, этот тип тестового кода обычно имеет небольшое покрытие тестов, поэтому если у вас есть модульное тестирование, они должны отображаться как не покрытый код.

Тогда есть инструменты, которые явно ищут такого рода вещи-Lint, Coverity и так далее. Большинство из них хотя и коммерческий. Также попробуйте использовать -O3 на GCC, компилятор может распознать больше фактически неиспользуемых переменных таким образом, поскольку он будет более агрессивно встроен и устранит код.