Для получения триального ключа
заполните форму ниже
Team license
Enterprise license
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Запросите информацию о ценах
Новая лицензия
Продление лицензии
--Выберите валюту--
USD
EUR
RUB
* Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Бесплатная лицензия PVS-Studio для специалистов Microsoft MVP
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

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

Мне интересно попробовать плагин на:
** Нажимая на кнопку, вы даете согласие на обработку
своих персональных данных. См. Политику конфиденциальности

Ваше сообщение отправлено.

Мы ответим вам на


Если вы так и не получили ответ, пожалуйста, проверьте папку
Spam/Junk и нажмите на письме кнопку "Не спам".
Так Вы не пропустите ответы от нашей команды.

>
>
>
Предупреждения помогают писать лаконичн…

Предупреждения помогают писать лаконичный код

19 Июл 2022

Некоторые предупреждения анализатора или компилятора сложно однозначно классифицировать как ложное срабатывание или указание на настоящую ошибку. Бывает, что формально анализатор/компилятор прав, но и код работает правильно. Что делать? Возможно, это повод упростить код.

0968_simplify_ru/image1.png

Известным недостатком всех статических анализаторов кода является выдача ими ложных срабатываний. С предупреждениями компилятора приблизительно такая же история.

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

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

Бывает, что анализатор/компилятор совершенно прав, выдавая предупреждение, но при этом код работает ровно так, как и задумывалось. Ошибки в нём нет. Обычно это свидетельствует о том, что код избыточен, переусложнён или "с запахом". Чтобы было понятнее, давайте сразу перейдём к практическому примеру и рассмотрим фрагмент кода из проекта Blender:

static bool lineart_do_closest_segment(....)
{
  int side = 0;
  ....
  /* No need to cut in the middle,
     because one segment completely overlaps the other. */
  if (side) {
    if (side > 0) {
      *is_side_2r = true;
      *use_new_ref = true;
    }
    else if (side < 0) {          // <=
      *is_side_2r = false;
      *use_new_ref = false;
    }
    return false;
  }
  ....
}

Анализатор PVS-Studio выдаёт здесь предупреждение "V547: Expression 'side < 0' is always true" на строчку, выделенную комментарием.

Уберём всё лишнее и рассмотрим код подробнее.

if (side) {
  if (side > 0) {
    *is_side_2r = true;
    *use_new_ref = true;
  }
  else if (side < 0) {
    *is_side_2r = false;
    *use_new_ref = false;
  }
  return false;
}

Первое условие отсекает случаи, когда переменная side равна 0. Далее в зависимости от того, меньше или больше нуля эта переменная, в переменные записываются разные значения и функция завершает свою работу.

В момент вычисления условия side < 0 анализатор уверен, что переменная всегда меньше 0, поэтому и выдаёт предупреждение.

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

Однако здесь видно, что никакой ошибки нет. Это просто немного избыточный код. Лишнее условие написано для красоты или для перестраховки. Ещё один вариант – избыточность возникла в процессе рефакторинга. Такое тоже бывает, и в некоторых статьях я рассматривал такие случаи.

Тем не менее, вернёмся к срабатыванию анализатора. Программист прав. Анализатор прав. Что делать? Самый простой вариант – точечно подавить предупреждение с помощью специального комментария:

if (side) {
  if (side > 0) {
    *is_side_2r = true;
    *use_new_ref = true;
  }
  else if (side < 0) {    //-V547
    *is_side_2r = false;
    *use_new_ref = false;
  }
  return false;
}

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

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

Следующий простой способ избавиться от предупреждения анализатора – это удалить лишнюю проверку:

if (side) {
  if (side > 0) {
    *is_side_2r = true;
    *use_new_ref = true;
  }
  else {
    *is_side_2r = false;
    *use_new_ref = false;
  }
  return false;
}

Собственно, ничего не изменилось. Просто исчезло одно условие, и вместе с ним исчезнет предупреждение. Однако лично мне такой вариант не очень нравится, так как читать код стало чуть сложнее. В голове нужно помнить, где какое значение имеет переменная side.

Возможно, если бы я писал код, то он был бы таким:

if (side > 0) {
  *is_side_2r = true;
  *use_new_ref = true;
  return false;
}
else if (side < 0) {
  *is_side_2r = false;
  *use_new_ref = false;
  return false;
}

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

Тем не менее, если вы ценитель короткого кода, то можно продолжить. Как вам такой вариант?

if (side) {
  const bool sideGreaterThanZero = side > 0;
  *is_side_2r = sideGreaterThanZero;
  *use_new_ref = sideGreaterThanZero;
  return false;
}

В целом, короткий и понятный код. Хотя, на мой взгляд, его сложнее читать, чем предыдущий фрагмент. Возможно, это дело вкуса.

Можно ещё короче? Можно:

if (side) {
  *use_new_ref = *is_side_2r = side > 0;
  return false;
}

Впрочем, я не в восторге от такого кода. Это уже из области "смотрите, как я могу". Не буду рекомендовать такой вариант. Тем не менее, получилось интересно. Обратив внимание на избыточное условие и проведя рефакторинг, можно сократить количество строк кода с 11 до 4.

Какой именно вариант изменения кода выбрать – решать вам. Моей целью было показать, что, когда анализатор/компилятор выдаёт срабатывание на корректный код, не стоит спешить подавлять предупреждение. Возможно, это повод для небольшого рефакторинга и упрощения кода.

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

Последние статьи:

Опрос:

Популярные статьи по теме
Holy C++

Дата: 23 Ноя 2022

Автор: Гость

В этой статье постараюсь затронуть все вещи, которые можно без зазрения совести выкинуть из С++, не потеряв ничего (кроме боли), уменьшить стандарт, нагрузку на создателей компиляторов, студентов, из…
Продление жизни временных значений в С++: рецепты и подводные камни

Дата: 01 Ноя 2022

Автор: Гость

Прочитав эту статью, вы узнаете следующее: способы, которыми можно продлить время жизни временного объекта в С++; рекомендации и подводные камни этого механизма, с которыми может столкнуться С++ прог…
Как мы баг в PVS-Studio искали или 278 Гигабайтов логов

Дата: 28 Окт 2022

Автор: Григорий Семенчев, Сергей Ларин, Филипп Хандельянц

Предлагаем вашему вниманию интересную историю о поиске бага внутри анализатора PVS-Studio. Да, мы тоже допускаем ошибки, но мы готовы засучить рукава и залезть в самую глубину "кроличьей норы".
0, 1, 2, Фредди забрал Blender

Дата: 26 Окт 2022

Автор: Андрей Карпов

Эта статья могла бы получить название "Как PVS-Studio защищает от поспешных правок кода, пример N7". Однако так именовать статьи становится скучновато. Поэтому сейчас вы узнаете, причём здесь Фредди …
Примеры ошибок, которые может обнаружить PVS-Studio в коде LLVM 15.0

Дата: 25 Окт 2022

Автор: Андрей Карпов

Компиляторы развиваются и выдают всё больше предупреждений. Остаются ли преимущества от использования статических анализаторов кода, таких как PVS-Studio? Да, так как анализаторы тоже развиваются. Пе…

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

Следующие комментарии
Unicorn with delicious cookie
Мы используем куки, чтобы пользоваться сайтом было удобно.
Хорошо