Blender, PVS-Studio, Example 3
Продолжаем серию маленьких заметок про то, как анализатор PVS-Studio может быстро находить новые ошибки в коде. При условии, конечно, что он регулярно используется :). Итак, перед нами очередной баг в проекте Blender.


Ради развлечения я мониторю проект Blender. Каждый день мне приходит отчёт PVS-Studio с предупреждениями, относящимися к новому коду. Иногда какая-то ошибка мне нравится, и я пишу про неё. Именно этим я занимаюсь и сейчас :).


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


В этот раз моё внимание привлекло два предупреждения PVS-Studio, указывающих на одну строчку кода:


  • [CWE-480] V616: The 'OB_MODE_OBJECT' named constant with the value of 0 is used in the bitwise operation. transform_snap_object.c 480
  • [CWE-571] V560: A part of conditional expression is always true: !(base->object->mode & OB_MODE_OBJECT). transform_snap_object.c 480

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


if (is_object_active && !(base->object->mode & OB_MODE_OBJECT)) {

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


Чтобы понять, что код неверен, нужно посмотреть, как объявлена именованная константа в перечислении eObjectMode:


typedef enum eObjectMode {
  OB_MODE_OBJECT = 0,
  OB_MODE_EDIT = 1 << 0,
  OB_MODE_SCULPT = 1 << 1,
  OB_MODE_VERTEX_PAINT = 1 << 2,
  OB_MODE_WEIGHT_PAINT = 1 << 3,
  OB_MODE_TEXTURE_PAINT = 1 << 4,
  OB_MODE_PARTICLE_EDIT = 1 << 5,
  OB_MODE_POSE = 1 << 6,
  OB_MODE_EDIT_GPENCIL = 1 << 7,
  OB_MODE_PAINT_GPENCIL = 1 << 8,
  OB_MODE_SCULPT_GPENCIL = 1 << 9,
  OB_MODE_WEIGHT_GPENCIL = 1 << 10,
  OB_MODE_VERTEX_GPENCIL = 1 << 11,
} eObjectMode;

Итак, константа OB_MODE_OBJECT равна нулю! Ещё раз взглянем на условие:


if (is_object_active && !(base->object->mode & OB_MODE_OBJECT)) {

Получается, что результат операции "побитовый И" (&) всегда равен 0. Про это первое сообщение анализатора.


Применим к 0 оператор "!", и получается, что перед нами выражение:


if (is_object_active && true) {

Про то, что часть выражения всегда истинна, нам говорит уже второе предупреждение PVS-Studio.


Скорее всего, правильным вариантом будет:


if (is_object_active && base->object->mode != OB_MODE_OBJECT) {

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


Спасибо за внимание и подписывайтесь на мой Twitter: @Code_Analysis.


Дополнительные ссылки:


  1. Бесплатные варианты лицензирования PVS-Studio.
  2. Как внедрить статический анализатор кода в legacy проект и не демотивировать команду.
  3. Развитие инструментария С++ программистов: статические анализаторы кода.

Если хотите поделиться этой статьей с англоязычной аудиторией, то прошу использовать ссылку на перевод: Andrey Karpov. How PVS-Studio prevents rash code changes, example N3.

Комментарии (0)