Pull to refresh

Статический анализ кода C++

Reading time 5 min
Views 33K
Для меня красота C++ заключается, в первую очередь, во вседозволенности и ужасающей мощи языка. Мы можем работать с памятью так же плотно, как и в C, и в то же время имеем такие средства абстракции, как шаблоны и STL, где параметризовать можно что угодно и чем угодно.
Плата за это соответствующая — не всегда вразумительные ошибки компилятора (попробуйте забыть поставить точку с запятой после определения класса), очень большой срок подготовки и обучения программистов, но самое главное — некоторые баги становятся заметными только во время исполнения программы.
Мы хотим больше полезных ворнингов до запуска наших программ. Одно из средств получения желаемого — статический анализ кода. Статический — значит, не запуская программу. Интересны не только вероятные ошибки, случаи undefined behaviour, утечки памяти, но и вещи вроде недоступности/неиспользуемости кода, рекомендации по повышению интуитивности стиля программирования.

Средства получения метрик ПО, добываемых методами статического анализа в этой статье не рассматриваются. Замечания, связанные со стандартами программирования (фигурная скобочка должна находиться на отдельной строчке, ууууу!!) тоже не интересны.
Критерии оценки простые — количество и полезность находимых багов, простота использования (в частности, отсутствие требований модификации кода), бесплатность/разумная цена/хороший кряк.
Проводим первичный обзор и выдаем на-гора пачку ссылок:


Найденные штуки


Педантичные ключи gcc


В первую очередь, надо использовать все возможные штатные средства. gcc предоставляет следующие интересные ключи, связанные с повышением бдительности компилятора и препроцессора.
  • -Wall включает все ворнинги, среди которых совместимость с новым стандартом, границы массива (по-моему, не пашет, хотя говорят, что будет работать с -O2), volatile/register переменные (сообщит, что ему пофигу на твои умные слова и что //register//, а что нет, он будет решать сам), точки следования (i++ + ++i)
  • -Wextra — еще пачка ворнингов типа пустых тел в if'ах, сравнение signed и unsigned
  • -pedantic — следование ISO C++ стандарту. Например, запрет типа long long.
  • -Weffc++ must have опция. Не включается с помощью -Wextra или -Wall и содержит проверку рекомендаций Скотта Мейерса:
    • Item 11: Define a copy constructor and an assignment operator for classes with dynamically allocated memory.
    • Item 12: Prefer initialization to assignment in constructors.
    • Item 14: Make destructors virtual in base classes.
    • Item 15: Have «operator=» return a reference to *this.
    • Item 23: Don't try to return a reference when you must return an object.
    • Item 6: Distinguish between prefix and postfix forms of increment and decrement operators.
    • Item 7: Never overload "&&", "||", or ",".
  • -Woverloaded-virtual — перегрузка виртуальных фунций выглядит плохо.
  • -Wctor-dtor-privacy — неиспользуемые классы — с приватным конструкторами и деструктором
  • -Wnon-virtual-dtor — невиртуальный деструктор
  • -Wold-style-cast — приведение в стиле C — это плохо
  • -Werr='тип ворнинга' — воспринимать ворнинг как еррор. Для настроящих самураев -Werr без параметров
  • -Wconversion -Wsign-conversion — ворнинг о преобразовании типа. при котором значение может измениться. Как ни странно, не входит в -Wall
  • -Winit-self — int i = i;
  • -Wunreachable-code — код, который никогда не будет выполнен

Разумеется, не сам такой умный, а прочитал всё вот в этой ман-статье про ключи, связанные с предупреждениями

Cppcheck


Пожалуй, самая достойная из найденных программ.
Официальный сайт программы и её плагин для эклипсоидов. Распознает довольно много, находит следующие ошибки:
  • некоторые memory leaks, например, отсутствие delete и delete[], отсутствие delete в деструкторе
  • выход за границу массива
  • exception'ы, бросаемые в деструкторе
  • разыменование нулевого указателя
  • разыменование после очистки памяти
  • виртуальность деструктора базового класса
  • использование одного и того же итератора для разных контейнеров
  • разные более мелкие штуки

Есть возможность помечать классы как умные указатели (чтобы не рапортовало о ложных мемликах), GUI на Qt4.

Vera++


Vera++, в отличие от cppcheck, ориентированна на проверку стиля. Имеет пополняемую базу правил. По умолчанию в базе много реально идиотстких штук типа «перед двоеточием должен быть пробел». Единственная полезная возможность — запрет на использование using namespace в заголовочных файлах. Правила, однако можно писать и самому на языке Tcl. :)

RATS


RATS рассказывает довольно-таки убедительные страшилки про безопасность и buffer overflow-атаки. Не стал пристально разглядывать потому что не очень-то много знаю про защищенный код.

Проверялки для C без плюсов


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

Будут ли они полезны плюсовым разработчикам? Если у вас есть код без классов и вы помните, какие программы на C++ не будут собираться компилятором С, то почему бы и нет?

Splint


Вот такая штука для чистого C. Собирается практически без усилий, но работает чистенько и много чего ищет — смотри мануал.

Simian


Simian — similarity analyzer. Ищет дублирование кода, значит. Честно говоря, я не придумал ей применения

CIL


CIL — С Intermediate Language. Компилирует C в упрощенный C! Упрощенный C уже можно скармливать другим анализаторам, что теоретически должно повышать качество их работы.

Непонятное, офтопное


  • Oink. Будьте осторожны, если захотите это юзать. При компиляции мне показалось, что я собираю как минимум ядро linux для какой-нибудь экзотической архитектуры. требует Flex и Bison, долго догадывался… (: А еще надо фиксить кучу ошибок при компиляции — такое впечатление, что разработчики свое детище не то, что никогда не запускают — не собирали ни разу. В конце концов зверя одолел, а вот | список фич оказался отнюдь не внушительным. Работает с файлами препроцессора, но выдает по ним какую-то чушь: натравив на простую ошибку с границей массива — получаю тишину. Если же скормить oink'у что-нибудь на C++ с STL — он разразится тирадой на много-много строк. И это я получил ценой 3 часов компиляния и правки исходников?
  • Mozilla Dehydra — нечто, базируещееся на этом самом Oink'е. Буду рад, если кто-нибудь расскажет об этой чекалке, ибо слово Mozilla в этот раз отнюдь не означало «все просто и понятно». Список находимых багов не увидел, компилять не хотелось после Oink'a.
  • Еще есть компиляторы, которые предоставляют больше ворнингов на основе статического анализа. Например Rose
  • Rational Purify — прославленная компания IBM Rational имеет свой набор инструментов для статического и динамического анализа. Буду рад, если кто-нибудь расскажем об этой программе, триальник для линукса у них оказался только для x64-архитектуры.


Триальное/крякнутое ПО


А мало его такого. Много анализаторов, стоящих много долларов и не имеют кряков.
Вот пример такого анализатора, можно зайти на их сайт и попросить триальник Cleanscape. Возможности не сильно отличаются от cppcheck.

Что бы еще хотелось


Не все ошибки в коде, поддающиеся статическому анализу, были представлены в существующих программах. А хотелось бы:
  • Политику спецификации исключений как в Java.
  • использование auto_ptr в контейнерах STL, разыменование auto_ptr после присвоения другому auto_ptr.
  • using namespace в h-файлах
  • переопределенный delete без new или new без delete
  • вызов delete[] для немассивов
  • использование vector с bool-значениями внутри
Tags:
Hubs:
+36
Comments 43
Comments Comments 43

Articles