Обложка: Защищаем свой код от багов: обзор пяти статических анализаторов для Java-кода

Защищаем свой код от багов: обзор пяти статических анализаторов для Java-кода

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

К таким помощникам можно отнести статические анализаторы кода. Они могут найти и исправить проблемный код (баги, опечатки, уязвимости) на самых ранних этапах разработки. В данном обзоре мы кратко рассмотрим популярные статические анализаторы для Java-кода.

Чуть подробней о статическом анализе

Старый добрый Code Review — незаменимый помощник, который пользуется большим успехом по сей день. Но от того количества кода, который ревьюверам приходится просматривать и обдумывать, становится страшно. На это уходит очень много времени и сил. Поэтому в большинстве случаев должное внимание уделяют только критичному с точки зрения работы приложения коду.

Необходимо снять с ревьюверов нагрузку и при этом не потерять в качестве кодовой базы. На помощь приходят инструменты статического анализа кода. Они, в паре с Code Review (а также другими методологиями), будут подстраховывать друг друга. И держать качество продукта на достойном уровне. По сути, статический анализ можно рассматривать как автоматизированный Code Review.

Вся суть статических анализаторов в том, что на вход подают исходный код/байт-код. А на выходе получают отчёт с предупреждениями. Чтобы выявить потенциальные проблемы, исходный код преобразуется во внутреннее представление анализатора — как правило, AST с семантической информацией — с дальнейшим применением различных методологий для извлечения всей необходимой информации.

Встроенный анализатор кода в IntelliJ IDEA

Статический анализатор Java-кода, встроенный в IntelliJ IDEA, ничуть не уступает специализированным инструментам статического анализа. Поиском подозрительного, неоптимального или ошибочного кода занимаются инспекции. Они используют различные современные методологии статического анализа: анализ потока данных, сопоставление с шаблоном.

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

Немного изучив ‘Inspections > Java’, я пришёл к выводу, что инспекции из категорий Probably Bugs, Numeric Issues, Serialization Issues с большей вероятностью обнаруживают реальные баги в коде. Во всяком случае, стоит самим пробежаться по всем инспекциям. И определить те, которые будут полезны именно вам и специфичны для вашего проекта.

При использовании IntelliJ IDEA множество ошибок можно исправлять за секунды после того, как их допустили. Так как статический анализ проводится постоянно в режиме редактирования кода. Код с обнаруженными дефектами подсвечивается в редакторе.

Это очень удобно и круто! А комбинация ‘Alt + Enter’ на подсвеченном фрагменте кода позволяет вызвать контекстное меню. Через него можно мгновенно исправить код, выбрав один из предложенных вариантов (если лень исправлять вручную):

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

Также анализ можно запустить вручную ‘Analyze > Inspect Code’, либо запустить отдельную инспекцию с помощью ‘Analyze > Run Inspection by Name’, указав перед этим область действия анализа (например, на проект, модуль или только на файл). При этом запуске становятся доступны некоторые инспекции, которые из-за трудоёмкости не работают в режиме редактирования.

После анализа результаты будут сгруппированы по категориям\директориям в отдельном окне. Из него можно перейти к конкретному срабатыванию инспекции:

IntelliJ позволяет сохранить результат анализа только в форматах XML и HTML. Но удобно работать с обнаруженными проблемами можно только в самой IDE (по моему мнению).

Большинство возможностей статического анализатора доступны в IntelliJ IDEA Community Edition (бесплатной версии).

SonarJava

SonarJava — статический анализатор кода для Java от компании SonarSource, которая позиционирует его лучшим инструментом среди конкурентов.

Для построения AST модели, наполненной семантической информацией, SonarJava на вход принимает исходный код файла. Если код использует внешние зависимости (а они, как правило, всегда есть), то для полного построения модели также необходимо передавать байт-код зависимостей. Но за это не стоит переживать, если вы интегрировали анализатор в сборочную систему (например, Gradle, Maven). Она сделает всё за вас.

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

Особенности:

  • 150+ правил обнаружения ошибок;
  • 350+ правил обнаружения запахов кода;
  • 40+ правил обнаружения потенциальных уязвимостей;
  • интеграция с Maven, Gradle, Ant, Eclipse, IntelliJ IDEA, VS Сode;
  • возможность расширения пользовательскими диагностическими правилами;
  • SAST-специализированный инструмент: большинство диагностических правил сопоставлены CWE, CERT, OWASP.

Анализ можно запустить как в рамках различных IDE (через плагин SonarLint), так и в рамках SonarQube.

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

Разумеется, просматривать предупреждения можно и в отдельном окошке:

Например, в VS Code результаты работы плагина выглядят следующим образом:


Возможность запускать SonarJava разными способами делает его очень привлекательным. Это развязывает руки разработчикам в выборе инструментария при написании кода.

Большую часть срабатываний анализатора занимают предупреждения категории ‘Code Smells’, которые лишь сигнализируют о признаках проблемного кода. Большинство этих предупреждений вы проигнорируете (так нехорошо делать, но, думаю, так и будет). Поэтому, чтобы не утонуть в этих предупреждениях, перед анализом советую пройтись по списку и оставить только интересующие вас.

В рамках этой статьи платформа SonarQube не будет рассматриваться, но если вы серьёзно зададитесь вопросом качества кода, то обязательно обратите внимание на эту платформу. Она позволяет аккумулировать отчёты сторонних анализаторов в одном месте, предоставляя удобный web-интерфейс для непрерывного контроля качества кода с различными дополнительными возможностями. Что немаловажно, есть бесплатная версия SonarQube Community.

FindBugs/SpotBugs

FindBugs, к большому сожалению, давно ушёл в закат, так как последний стабильный релиз выходил в далёком 2015 году. Но его не стоит забывать по той причине, что это, пожалуй, наиболее известный бесплатный статический анализатор Java. Если спросить у Java-разработчика про статический анализ, то ему, скорее всего, сразу же на ум придёт FindBugs. Его отличительная черта от большинства рассмотренных ранее анализаторов в том, что он анализирует не исходный код, а байт-код.

Вслед за FindBugs эстафету принял его преемник – open-source анализатор SpotBugs. Скорее всего, все преимущества и недостатки FindBugs так же плавно перетекли в преемника, но хорошо этоили плохо — покажет время. А пока анализатор активно развивается своим сообществом. Как доказательство:

  • 400+ правил обнаружения ошибок;
  • интеграция в Ant, Maven, Gradle, Eclipse, IntelliJ IDEA;
  • возможность расширения пользовательскими диагностическими правилами.

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

В IntelliJ IDEA окно с предупреждениями выглядит следующим образом:

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

Анализ запускается вручную, но после анализа все найденные проблемные участки кода подсвечиваются наравне с другими предупреждениями от IntelliJ IDEA и SonarLint. Есть один нюанс. Чтобы все предупреждения (подсветка проблемного кода) актуализировались с учётом всех ваших изменений в файле, нужно перезапустить анализ. Также много рекомендательных срабатываний, поэтому перед активным использованием анализатор необходимо сконфигурировать.

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

PVS-Studio

В основе PVS-Studio лежит open-source библиотека Spoon. Она принимает на вход исходный код и генерирует хорошо спроектированную AST-модель с семантической информацией. На основе полученной модели применяются передовые методологии статического анализа:

  • анализ потока данных (Data-Flow Analysis);
  • символьное выполнение (Symbolic Execution);
  • аннотирование методов (Method Annotations);
  • сопоставление с шаблоном (Pattern-based analysis).

На данный момент анализатор оперирует более чем 105 диагностическими правилами, которые позволяют обнаруживать различные недостатки/ошибки в коде: опечатки, разыменование нулевой ссылки, недостижимый код, выход за границу массива, нарушение контракта использования метода и так далее. О всех возможностях диагностических правил можно посмотреть здесь.

Особенности:

  • ориентирован на поиск реальных ошибок;
  • помимо CLI версии анализатора также есть интеграция в IntelliJ IDEA, Maven, Gradle, Jenkins, SonarQube;
  • возможность запуска анализа в инкрементальном режиме;
  • позволяет выявлять потенциальные проблемы совместимости Java SE API при миграции проекта с Java 8 на более свежие версии;
  • PVS-Studio позволяет конвертировать отчёт в различные удобные для использования форматы: json, xml, html, txt;
  • SAST-специализированный инструмент: большинство диагностических правил сопоставлены CWECERTOWASP.

Как обычно, рассмотрю использование анализатора в IntelliJ IDEA.

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

Все предупреждения анализатора сводятся в специальную таблицу, в которой можно удобно с ними работать (сортировать, фильтровать, помечать как False Alarm):

PVS-Studio проприетарное ПО, но его также можно использовать бесплатно. Конкурентное преимущество анализатора — быстрая и качественная поддержка, оказываемая непосредственно разработчиками анализатора.

PMD

PMD – open-source статический анализатор, позволяющий обнаруживать общие ошибки программирования: неиспользуемые переменные, пустые блоки, создание ненужных объектов и так далее.

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

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

Особенности:

  • умеет интегрироваться во многие IDE (IntelliJ IDEA, Eclipse, NetBeans и так далее) и сборочные системы (Maven, Gradle, Ant);
  • большое разнообразие доступных форматов отчёта анализатора: sarif, csv, ideaj, json, text(default), xml, html, textcolor и так далее;
  • 300+ паттернов диагностических правил. Категории: стиль кодирования, лучшие практики, ошибки, многопоточность, производительность и так далее;
  • вместе с PMD поставляется CPD (copy-paste detector), который позволяет находить дубликаты в коде.

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

Для IntelliJ IDEA также есть плагин, через который можно запускать анализ с различными наборами правил, но выбирать отдельные файлы для анализа нельзя. Окно с предупреждениями выглядит следующим образом:

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

Подводим итоги

Конечно же, помимо рассмотренных анализаторов есть и другие: как платные (Coverity, Klockwork, JArchitect и так далее), так и бесплатные (Error Prone, Infer, Checkstyle и так далее). Все они ориентированы на одно: предотвратить попадание ошибочного или потенциально ошибочного кода в production.

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

Немаловажную роль в выборе статического анализатора играют:

  • интеграция в различные IDE;
  • интеграция в сборочные системы;
  • удобство запуска анализатора на сервере;
  • возможность обнаружения ошибок в режиме редактирования кода;
  • возможность удобной работы с предупреждениями;
  • SAST-ориентированность;
  • процент ложных срабатываний;
  • сложность конфигурирования;

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

Приводил примеры интеграции с IntelliJ IDEA, так как часто ею пользуюсь. Поэтому для меня интеграция с этой IDE была приоритетна при обзоре.

Заключение

Мы с вами рассмотрели инструментарий, который поможет быстро обнаружить и исправить проблемный код до того, как он попадёт в ‘production’. На сегодняшний день таких инструментов достаточно много и каждый имеет свои слабые и сильные стороны.

С учётом этого останавливаться на каком-то одном анализаторе — не совсем верное решение. Чтобы создать достойный барьер от багов и уязвимостей, советую вам всё-таки использовать комплексную защиту из нескольких анализаторов, так как они будут прекрасно взаимодополнять друг друга. Так что выбор за вами!