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

Примечание. Эта статья была впервые опубликована мной в блоге Fluent C++: Почему статический анализ может улучшить сложную кодовую базу C++.

Во-первых, кода так много, что уже невозможно иметь хотя бы пару программистов на проект, знающих весь проект. Например, ранее ядро ​​Linux 1.0.0 содержало около 176 000 строк кода. Это много, но просмотреть весь код и понять общие принципы его работы можно было за пару недель, имея рядом кофеварку. Тем не менее, если взять ядро ​​Linux 5.0.0, размер кодовой базы составляет уже около 26 миллионов строк кода. Код ядра в 150 раз больше, чем раньше. Вы можете выбрать только несколько частей проекта и принять участие в их разработке. Нельзя успокоиться и разобраться, как именно это работает, каковы взаимосвязи между разными частями кода.

Во-вторых, язык C++ продолжает стремительно развиваться. С одной стороны, это хорошо, так как появляются новые конструкции, позволяющие писать более компактный и безопасный код. С другой стороны, из-за обратной совместимости старые большие проекты становятся разнородными. В них переплетаются старые и новые подходы к написанию кода. Здесь возникает аналогия с кольцами на спиле дерева. Из-за этого с каждым годом погружаться в проекты на C++ становится все сложнее. Разработчик должен знать, что к чему в коде, написанном как в стиле «Си с классами», так и в современных подходах (лямбдах, семантике перемещения и так далее). Чтобы полностью погрузиться в C++, требуется много времени.

Поскольку проекты еще нужно разрабатывать, люди начинают писать код на C++, не изучив до конца все его нюансы. Это приводит к дополнительным дефектам. Тем не менее, нерационально просто стоять и ждать, когда все разработчики будут безукоризненно знать C++.

Ситуация безвыходная? Нет. На помощь приходит новый класс инструментов: статические анализаторы кода. Тут многие видавшие виды программисты кривят губы, как будто я лимон только что подсунул :). Мол, знаем все ваши линтеры… Много предупреждений — хвастовство, маленькое жаркое… А что за новый класс инструментов?! Мы запускали линтеры даже 20 лет назад!

Все же рискну сказать, что это новый класс инструментов. То, что было 10–20 лет назад, — это не те инструменты, которые сейчас называют статическими анализаторами. Во-первых, я не говорю об инструментах, предназначенных для форматирования кода. Это тоже инструменты статического анализа, но мы говорим о выявлении ошибок в коде. Во-вторых, современные инструменты используют сложные технологии анализа, учитывающие отношения между различными функциями и виртуально выполняющие определенные части кода. Это не те линтеры 20-летней давности, построенные на регулярных выражениях. Кстати, обычный статический анализатор не умеет работать с регулярными выражениями. Для поиска ошибок используются такие технологии, как анализ потока данных, автоматическое аннотирование методов, символьное выполнение и другие.

Это не просто абстрактные слова, а реальность, которую я могу наблюдать, будучи одним из создателей инструмента PVS-Studio. Посмотрите эту статью, чтобы узнать, что помогает анализаторам находить самые интересные ошибки.

Что еще более важно, современные статические анализаторы обладают обширными знаниями о шаблонах ошибок. Анализаторы знают больше, чем даже профессиональные разработчики. Стало слишком сложно учитывать и помнить все нюансы при написании кода. Например, если вы специально об этом не читали, то ни за что не догадаетесь, что вызовы функции memset для очистки приватных данных иногда исчезают, так как с точки зрения компилятора вызов memset является избыточным. Между тем, это серьезный недостаток безопасности CWE-14, который выявляется буквально повсеместно. Или, например, если вы не слышали об этом руководстве, откуда вы знаете, что добавлять элемент в контейнер таким образом опасно?

std::vector<std::unique_ptr<MyType>> v;
v.emplace_back(new MyType(123));

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

Помимо обширных знаний о паттернах, статические анализаторы бесконечно внимательны и никогда не устают. Например, в отличие от людей, они не ленятся заглянуть в заголовочные файлы, чтобы убедиться, что isspace и sprintf — это настоящие функции, а не безумные макросы, которые все портят . Такие случаи демонстрируют сложность поиска багов в больших проектах: в одном месте что-то меняется, а в другом ломается.

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

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

Кто-то может сказать, что нет смысла в специальных инструментах, так как компиляторы учатся выполнять и такие статические проверки. Да, это правда. Тем не менее, статические анализаторы также развиваются и опережают компиляторы в качестве специализированных инструментов. Например, каждый раз, когда мы проверяем LLVM, мы находим там ошибки с помощью PVS-Studio.

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

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