Автоматическое удаление неиспользуемых локальных переменных из исходного кода C - PullRequest
8 голосов
/ 18 февраля 2009

Я хочу удалить неиспользуемые локальные переменные из файла C. Пример:

int fun(int a , int b)
{
  int c,sum=0;
  sum=a + b;
    return sum;
}

Здесь неиспользуемой переменной является 'c'.

У меня будет внешний список всех неиспользуемых локальных переменных. Теперь используя неиспользуемые локальные переменные, которые у меня есть, мы должны найти локальные переменные из исходного кода и удалить.
В вышеприведенном примере «c» является неиспользуемой переменной. Я буду знать это (у меня есть код для этого). Здесь я должен найти c и удалить его.

EDIT

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

Ответы [ 10 ]

21 голосов
/ 18 февраля 2009

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

Помещение исходного фрагмента в "f.c":

% gcc -c -Wall f.c
f.c: In function 'fun':
f.c:1: warning: unused variable 'c'
10 голосов
/ 18 февраля 2009

Tricky - для этого вам придется проанализировать код C. Насколько близок должен быть результат? Пример того, что я имею в виду:

int a, /* foo */
    b, /* << the unused one */
    c; /* bar */

Теперь для людей очевидно, что второй комментарий должен идти.

Небольшая вариация:

void test(/* in */ int a, /* unused */ int b, /* out */ int* c);

Опять же, второй комментарий должен идти, на этот раз перед b.

Как правило, вы хотите проанализировать ввод, отфильтровать его и выдать все, что не является объявлением неиспользуемой переменной. Ваш парсер должен был бы сохранить комментарии и операторы #include, но если вы не #include заголовки, может быть невозможно распознать объявления (даже более того, если макросы используются, чтобы скрыть объявление). В конце концов, вам нужны заголовки, чтобы решить, если A * B (); является объявлением функции (когда A является типом) или умножением (когда A является переменной)


[править] Далее:

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

int foo(int a, int b, int c) { return a + b; }

Очевидно, что c не используется. Вы можете изменить это на?

int foo(int a, int b) { return a + b; }

Возможно, но не если & foo хранится в int(*)(int,int,int). И это может случиться где-то еще. Если (и только если) это произойдет, вы должны изменить его на

int foo(int a, int b, int /*unused*/ ) { return a + b; }
5 голосов
/ 18 февраля 2009

Мой ответ - скорее подробный комментарий к очень тщательному ответу MSalters. Я бы вышел за пределы «хитрости» и сказал бы, что такой инструмент и невозможен, и нецелесообразен.

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

int foo(double a, double b)
{
   b = 10.0;
   return (int) b;
}

int bar(double a, double b)
{
   a = 5.00;
   return (int) a;
}

У любого простого парсера могут возникнуть проблемы с тем, что и 'a', и 'b' являются неиспользуемыми переменными.

Во-вторых, если вы считаете комментарии такими же, как у MSalter, вы обнаружите, что люди не комментируют последовательно;

double a;
/*a is designed as a dummy variable*/
double b;

/*a is designed as a dummy variable*/
double a;
double b;

double a; /*a is designed as a dummy variable*/
double b;

и т.д..

Таким образом, простое удаление неиспользованных переменных приведет к появлению осиротевших комментариев, которые, возможно, более опасны, чем вообще не комментирует .

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

Наконец, вы должны подумать, почему переменные были в коде в первую очередь, и если они устарели, почему они не были удалены, когда все их ссылки были.

5 голосов
/ 18 февраля 2009

Почему вы хотите это сделать? Предполагая, что у вас есть приличный оптимизирующий компилятор (GCC, Visual Studio и др.), Двоичный вывод не будет отличаться, независимо от того, удаляете ли вы int c в исходном примере или нет.

Если речь идет только об очистке кода, любая последняя IDE будет давать вам быстрые ссылки на исходный код для каждого предупреждения, просто нажмите и удалите:)

1 голос
/ 18 февраля 2009

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

1 голос
/ 18 февраля 2009

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

0 голосов
/ 18 сентября 2009

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

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

Например, если строка источника: "int a, unused, b;" и компилятор сообщил, что «не используется» как неиспользованная переменная в этой строке, чем будет соответствовать шаблон «/, не используется, /», и вы можете заменить эту подстроку одним «,».

0 голосов
/ 14 июня 2009

Один из постеров выше говорит «невозможно и нецелесообразно». Другой говорит «хитро», и это правильный ответ. Вам нужен 1) полный синтаксический анализатор C (или любой интересующий вас язык), 2) процедуры вывода, которые понимают язык ссылки на идентификаторы и потоки данных, чтобы определить, что переменная действительно "мертв", и 3) способность реально изменить исходный код.

Что сложно во всем этом, так это огромная энергия, чтобы построить 1) 2) 3). Вы не можете оправдать какую-либо отдельную задачу по очистке. Что можно сделать, это построить такую ​​инфраструктуру специально с целью амортизации его по многим другим задачи анализа и преобразования программ.

Моя компания предлагает такой инструмент: реинжиниринг программного обеспечения DMS Инструментарий. Увидеть http://www.semdesigns.com/Products/DMS/DMSToolkit.html DMS имеет качественные интерфейсы для многих языков, включая C, C ++, Java и COBOL.

На самом деле мы создали автоматизированный «поиск бесполезных объявлений» инструмент для Java, который делает две вещи: а) перечисляет их все (таким образом производя список!) б) делает копию кода с бесполезными объявлениями удален. Вы выбираете, какой ответ вы хотите сохранить: -)

Сделать то же самое для C не составит труда. Мы уже есть инструмент, который идентифицирует такие мертвые переменные / функции.

Один случай, который мы не упомянули, это «бесполезный параметр» случай, потому что удалить бесполезный параметр, у вас есть найти все звонки из других модулей, убедитесь, что настройка аргумента не имеет стороны эффект, и вырвать бесполезный аргумент. У нас на самом деле есть полные графики всего программного обеспечения система интересов, и поэтому это также будет возможно.

Итак, это просто сложно, и даже не очень сложно если у вас есть правильная инфраструктура.

0 голосов
/ 24 февраля 2009

Вам понадобится хороший синтаксический анализатор, который сохраняет исходную позицию символов токенов (даже в присутствии препроцессора!). Есть несколько инструментов для автоматического рефакторинга C / C ++, но они далеки от мейнстрима.

Рекомендую проверить Блог Тараса . Парень проводит большой автоматизированный рефакторинг кодовой базы Mozilla, например, заменяя выходные параметры возвращаемыми значениями. Его основным инструментом для переписывания кода является Pork :

Pork - это синтаксический анализ и переписывание кода на C ++ цепочка инструментов. Ядро свинины - это C ++ синтаксический анализатор, который обеспечивает точный символ позиции для начала и конца каждый узел AST, а также набор расширения макросов, которые содержат любые место нахождения. Эта информация позволяет C ++ быть автоматически переписан в точный способ.

Из блога:

До сих пор свинина использовалась для «несовершеннолетних» такие вещи, как переименование классы и функции, вращающиеся outparameters и корректирующий prbool ошибок. Кроме того, свинина зарекомендовала себя в эксперименте, который включал переписывая почти каждую функцию (т.е. создание патча 3 + МБ) в Mozilla для использовать сборку мусора вместо подсчет ссылок.

Это для C ++, но оно может удовлетворить ваши потребности.

0 голосов
/ 18 февраля 2009

Также: шина .

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...